<?php /* ** Copyright (C) 2001-2025 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ require_once dirname(__FILE__).'/../include/CIntegrationTest.php'; class CAutoregClient extends CZabbixClient { public function sendRequest($host, $ip) { parent::request([ 'request' => 'active checks', 'host' => $host, 'ip' => $ip ]); } } /** * Test suite for action notifications * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider * @onAfter clearData * @hosts test_hostconn */ class testHostConnMacroValidation extends CIntegrationTest { private static $hostid; private static $triggerid; private static $triggerid_action; private static $triggerid_neg; private static $triggerid_action_neg; private static $trigger_actionid; private static $trigger_actionid_neg; private static $scriptid_action; private static $scriptid; private static $drule_actionid; private static $druleid; private static $hostmacroid; private static $interfaceid; private static $trap2_itemid; private static $ext_itemid; const ITEM_TRAP = 'trap1'; const ITEM_TRAP2 = 'trap2_allowedhosts'; const ITEM_EXT = 'item_external'; const HOST_NAME = 'test_hostconn'; /** * Prematurely enable global scripts so prepareData wouldn't fail. * */ private function updateServerStatus() { $server_status = [ "version" => ZABBIX_VERSION, "configuration" => [ "enable_global_scripts" => true, "allow_software_update_check" => true ] ]; DBexecute("update config set server_status='".json_encode($server_status)."'"); } /** * @inheritdoc */ public function prepareData() { $response = $this->call('host.create', [ 'host' => self::HOST_NAME, 'interfaces' => [ [ 'type' => 1, 'main' => 1, 'useip' => 1, 'ip' => '127.0.0.1', 'dns' => '', 'port' => $this->getConfigurationValue(self::COMPONENT_AGENT, 'ListenPort') ] ], 'groups' => [ [ 'groupid' => 4 ] ] ]); $this->assertArrayHasKey('hostids', $response['result']); $this->assertArrayHasKey(0, $response['result']['hostids']); self::$hostid = $response['result']['hostids'][0]; $response = $this->call('host.get', [ 'output' => ['host'], 'hostids' => [self::$hostid], 'selectInterfaces' => ['interfaceid'] ]); $this->assertArrayHasKey(0, $response['result']); $this->assertArrayHasKey('interfaces', $response['result'][0]); $this->assertArrayHasKey(0, $response['result'][0]['interfaces']); self::$interfaceid = $response['result'][0]['interfaces'][0]['interfaceid']; $response = $this->call('item.create', [ 'hostid' => self::$hostid, 'name' => self::ITEM_TRAP, 'key_' => self::ITEM_TRAP, 'type' => ITEM_TYPE_TRAPPER, 'value_type' => ITEM_VALUE_TYPE_UINT64 ]); $this->assertArrayHasKey('itemids', $response['result']); $this->assertEquals(1, count($response['result']['itemids'])); $response = $this->call('trigger.create', [ 'description' => 'Trigger for HOST.CONN test', 'expression' => 'last(/'.self::HOST_NAME.'/'.self::ITEM_TRAP.')>0' ]); $this->assertArrayHasKey('triggerids', $response['result']); $this->assertCount(1, $response['result']['triggerids']); self::$triggerid = $response['result']['triggerids'][0]; $response = $this->call('trigger.create', [ 'description' => 'Trigger for HOST.CONN test via action', 'expression' => 'last(/'.self::HOST_NAME.'/'.self::ITEM_TRAP.')>100' ]); $this->assertArrayHasKey('triggerids', $response['result']); $this->assertCount(1, $response['result']['triggerids']); self::$triggerid_action = $response['result']['triggerids'][0]; $response = $this->call('trigger.create', [ 'description' => 'Trigger for negative HOST.CONN test', 'expression' => 'last(/'.self::HOST_NAME.'/'.self::ITEM_TRAP.')>0' ]); $this->assertArrayHasKey('triggerids', $response['result']); $this->assertCount(1, $response['result']['triggerids']); self::$triggerid_neg = $response['result']['triggerids'][0]; $response = $this->call('trigger.create', [ 'description' => 'Trigger for negative HOST.CONN test via action', 'expression' => 'last(/'.self::HOST_NAME.'/'.self::ITEM_TRAP.')>2000' ]); $this->assertArrayHasKey('triggerids', $response['result']); $this->assertCount(1, $response['result']['triggerids']); self::$triggerid_action_neg = $response['result']['triggerids'][0]; $response = $this->call('usermacro.create', [ 'hostid' => self::$hostid, 'macro' => '{$INJADDR}', 'value' => '127.0.0.1' ]); $this->assertArrayHasKey('result', $response); $this->assertArrayHasKey('hostmacroids', $response['result']); self::$hostmacroid = $response['result']['hostmacroids'][0]; $response = $this->call('hostinterface.update', [ 'interfaceid' => self::$interfaceid, 'useip' => 0, 'dns' => '{$INJADDR}', 'ip' => '' ]); $this->assertArrayHasKey('interfaceids', $response['result']); $this->assertArrayHasKey(0, $response['result']['interfaceids']); self::$interfaceid = $response['result']['interfaceids'][0]; $this->updateServerStatus(); $response = $this->call('script.create', [ 'name' => 'inj test', 'command' => 'echo -n hello {HOST.CONN}', 'execute_on' => ZBX_SCRIPT_EXECUTE_ON_SERVER, 'scope' => ZBX_SCRIPT_SCOPE_HOST, 'type' => ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT ]); $this->assertArrayHasKey('scriptids', $response['result']); self::$scriptid = $response['result']['scriptids'][0]; $response = $this->call('script.create', [ 'name' => 'inj test action', 'command' => 'echo -n hello {HOST.CONN}', 'execute_on' => ZBX_SCRIPT_EXECUTE_ON_SERVER, 'scope' => ZBX_SCRIPT_SCOPE_ACTION, 'type' => ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT ]); $this->assertArrayHasKey('scriptids', $response['result']); self::$scriptid_action = $response['result']['scriptids'][0]; DBexecute("update config set server_status=''"); $response = $this->call('action.create', [ 'esc_period' => '1m', 'eventsource' => EVENT_SOURCE_TRIGGERS, 'status' => 0, 'filter' => [ 'conditions' => [ [ 'conditiontype' => ZBX_CONDITION_TYPE_TRIGGER, 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => self::$triggerid_action ] ], 'evaltype' => CONDITION_EVAL_TYPE_AND_OR ], 'name' => 'action_trigger_trap', 'operations' => [ [ 'operationtype' => OPERATION_TYPE_COMMAND, 'esc_period' => '0s', 'esc_step_from' => 1, 'esc_step_to' => 2, 'evaltype' => CONDITION_EVAL_TYPE_AND_OR, 'opcommand_grp' => [ [ 'groupid' => 4 ] ], 'opcommand' => [ 'scriptid' => self::$scriptid_action ] ] ], 'pause_suppressed' => 0 ]); $this->assertArrayHasKey('actionids', $response['result']); $this->assertEquals(1, count($response['result']['actionids'])); self::$trigger_actionid = $response['result']['actionids'][0]; $response = $this->call('action.create', [ 'esc_period' => '1m', 'eventsource' => EVENT_SOURCE_TRIGGERS, 'status' => 0, 'filter' => [ 'conditions' => [ [ 'conditiontype' => ZBX_CONDITION_TYPE_TRIGGER, 'operator' => CONDITION_OPERATOR_EQUAL, 'value' => self::$triggerid_action_neg ] ], 'evaltype' => CONDITION_EVAL_TYPE_AND_OR ], 'name' => 'Action on trigger (negative case)', 'operations' => [ [ 'operationtype' => OPERATION_TYPE_COMMAND, 'esc_period' => '0s', 'esc_step_from' => 1, 'esc_step_to' => 2, 'evaltype' => CONDITION_EVAL_TYPE_AND_OR, 'opcommand_grp' => [ [ 'groupid' => 4 ] ], 'opcommand' => [ 'scriptid' => self::$scriptid_action ] ] ], 'pause_suppressed' => 0 ]); $this->assertArrayHasKey('actionids', $response['result']); $this->assertEquals(1, count($response['result']['actionids'])); self::$trigger_actionid_neg = $response['result']['actionids'][0]; $response = $this->call('item.create', [ 'hostid' => self::$hostid, 'name' => self::ITEM_TRAP2, 'key_' => self::ITEM_TRAP2, 'type' => ITEM_TYPE_TRAPPER, 'value_type' => ITEM_VALUE_TYPE_UINT64, 'trapper_hosts' => '{HOST.CONN}' ]); $this->assertArrayHasKey('itemids', $response['result']); $this->assertEquals(1, count($response['result']['itemids'])); self::$trap2_itemid = $response['result']['itemids'][0]; $response = $this->call('item.create', [ 'hostid' => self::$hostid, 'name' => self::ITEM_EXT, 'key_' => 'script[{HOST.CONN}]', 'type' => ITEM_TYPE_EXTERNAL, 'delay' => '30s', 'value_type' => ITEM_VALUE_TYPE_UINT64 ]); $this->assertArrayHasKey('itemids', $response['result']); $this->assertEquals(1, count($response['result']['itemids'])); self::$ext_itemid = $response['result']['itemids'][0]; $response = $this->call('drule.create', [ 'name' => 'test discovery rule HOST.CONN', 'iprange' => '127.0.0.1-10', 'dchecks' => [ [ 'type' => SVC_AGENT, 'key_' => 'agent.variant', 'ports' => PHPUNIT_PORT_PREFIX.self::AGENT_PORT_SUFFIX, 'uniq' => 1, 'host_source' => 3, 'name_source' => 3 ] ], 'delay' => '10s' ] ); $this->assertArrayHasKey('druleids', $response['result']); $this->assertEquals(1, count($response['result']['druleids'])); self::$druleid = $response['result']['druleids'][0]; $response = $this->call('action.create', [ 'name' => 'create_host_d', 'eventsource' => EVENT_SOURCE_DISCOVERY, 'status' => 0, 'operations' => [ [ 'operationtype' => OPERATION_TYPE_COMMAND, 'opcommand_grp' => [ [ 'groupid' => 4 ] ], 'opcommand' => [ 'scriptid' => self::$scriptid_action ] ] ] ]); $this->assertArrayHasKey('actionids', $response['result']); $this->assertEquals(1, count($response['result']['actionids'])); self::$drule_actionid = $response['result']['actionids'][0]; return true; } /** * Component configuration provider for agent related tests. * * @return array */ public function traceConfigurationProvider() { return [ self::COMPONENT_SERVER => [ 'DebugLevel' => 5, 'LogFileSize' => 0 ] ]; } /** * Component configuration provider for agent related tests. * * @return array */ public function defaultConfigurationProvider() { return [ self::COMPONENT_SERVER => [ 'DebugLevel' => 4, 'LogFileSize' => 0, 'EnableGlobalScripts' => 1 ], self::COMPONENT_AGENT => [ 'Hostname' => self::HOST_NAME, 'AllowKey' => 'system.run[*]' ] ]; } /** * Test regression of validation. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testValidMacroManualHostScript() { $response = $this->callUntilDataIsPresent('script.execute', [ 'scriptid' => self::$scriptid, 'hostid' => self::$hostid ], 30, 2); $this->assertArrayHasKey('response', $response['result']); $this->assertEquals('success', $response['result']['response']); $this->assertArrayHasKey('value', $response['result']); $this->assertEquals('hello 127.0.0.1', $response['result']['value']); } /** * Test regression of validation. * * @required-components server, agent * @depends testHostConnMacroValidation_testValidMacroManualHostScript * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testValidMacroManualEventScript() { $response = $this->call('script.update', [ 'scriptid' => self::$scriptid, 'scope' => ZBX_SCRIPT_SCOPE_EVENT ]); $this->assertArrayHasKey('scriptids', $response['result']); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 1); $response = $this->callUntilDataIsPresent('event.get', [ 'objectids' => self::$triggerid, 'sortfield' => 'clock', 'sortorder' => 'DESC', 'limit' => 1 ], 30, 2); $this->assertArrayHasKey(0, $response['result']); $eventid = $response['result'][0]['eventid']; $response = $this->callUntilDataIsPresent('script.execute', [ 'scriptid' => self::$scriptid, 'eventid' => $eventid ], 30, 2); $this->assertArrayHasKey('response', $response['result']); $this->assertEquals('success', $response['result']['response']); $this->assertArrayHasKey('value', $response['result']); $this->assertEquals('hello 127.0.0.1', $response['result']['value']); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 0); } /** * Test regression of validation. * * @required-components server, agent * @depends testHostConnMacroValidation_testValidMacroManualEventScript * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testValidMacroAction() { $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 101); $response = $this->callUntilDataIsPresent('alert.get', [ 'actionids' => [self::$trigger_actionid], 'sortfield' => 'alertid', 'sortorder' => 'DESC', 'limit' => 1 ], 5, 2); $this->assertArrayHasKey(0, $response['result']); $this->assertEquals('test_hostconn:echo -n hello 127.0.0.1', $response['result'][0]['message']); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 0); } /** * Test regression of validation. * * @required-components server, agent * @depends testHostConnMacroValidation_testValidMacroAction * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testValidMacroAllowedHosts() { $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP2, 1); $response = $this->callUntilDataIsPresent('history.get', [ 'itemids' => [self::$trap2_itemid] ], 5, 2); $this->assertEquals(1, count($response['result'])); } /** * Test regression of validation. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testValidMacroItemKey() { $this->clearLog(self::COMPONENT_SERVER); $this->call('task.create', [ 'type' => ZBX_TM_TASK_CHECK_NOW, 'request' => [ 'itemid' => self::$ext_itemid ] ]); $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, "In get_value_external() key:'script[127.0.0.1]'", true, 60, 1); } /** * Test regression of validation. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testValidMacroDruleAction() { $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, "In discovery_register_host() ip:'127.0.0.1' status:0 value:'1'", false, 60, 1); } /** * Test injection via invalid macro provided to manual host script. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testInvalidMacroManualHostScript() { $response = $this->call('script.update', [ 'scriptid' => self::$scriptid, 'scope' => ZBX_SCRIPT_SCOPE_HOST ]); $this->assertArrayHasKey('scriptids', $response['result']); $response = $this->call('usermacro.update', [ 'hostmacroid' => self::$hostmacroid, 'value' => '127.0.0.1;uname' ]); $this->assertArrayHasKey('result', $response); $this->assertArrayHasKey('hostmacroids', $response['result']); $this->reloadConfigurationCache(); $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, "End of zbx_dc_sync_configuration()", true, 30, 1); $response = CAPIHelper::call('script.execute', [ 'scriptid' => self::$scriptid, 'hostid' => self::$hostid ]); $this->assertArrayHasKey('error', $response); $this->assertArrayHasKey('data', $response['error']); $this->assertEquals("Invalid macro '{HOST.CONN}' value", $response['error']['data']); } /** * Test injection via manual event script. * * @required-components server, agent * @depends testHostConnMacroValidation_testInvalidMacroManualHostScript * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testInvalidMacroManualEventScript() { $response = $this->call('script.update', [ 'scriptid' => self::$scriptid, 'scope' => ZBX_SCRIPT_SCOPE_EVENT ]); $this->assertArrayHasKey('scriptids', $response['result']); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 222); $response = $this->callUntilDataIsPresent('event.get', [ 'objectids' => self::$triggerid, 'sortfield' => 'clock', 'sortorder' => 'DESC', 'limit' => 1 ], 30, 2); $this->assertArrayHasKey(0, $response['result']); $eventid = $response['result'][0]['eventid']; $response = CAPIHelper::call('script.execute', [ 'scriptid' => self::$scriptid, 'eventid' => $eventid ]); $this->assertArrayHasKey('error', $response); $this->assertArrayHasKey('data', $response['error']); $this->assertEquals("Invalid macro '{HOST.CONN}' value", $response['error']['data']); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 0); } /** * Test injection via invalid trigger action operation. * * @required-components server, agent * @depends testHostConnMacroValidation_testInvalidMacroManualEventScript * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testInvalidMacroAction() { $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 9999); $response = $this->callUntilDataIsPresent('alert.get', [ 'actionids' => [self::$trigger_actionid_neg] ], 30, 2); $this->assertArrayHasKey(0, $response['result']); $this->assertEquals("Invalid macro '{HOST.CONN}' value", $response['result'][0]['error']); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP, 0); } /** * Test injection via malicious autoregistration request. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testInvalidMacroAutoregistration() { $this->clearLog(self::COMPONENT_SERVER); $c = new CAutoregClient('localhost', self::getConfigurationValue(self::COMPONENT_SERVER, 'ListenPort', 10051), 3, 3, ZBX_SOCKET_BYTES_LIMIT ); $c->sendRequest(self::HOST_NAME, '127.250.250.250;uname'); $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, 'cannot send list of active checks to "127.0.0.1": "127.250.250.250;uname" is not a valid IP address', true, 30, 2, true ); } /** * Test injection via potentially malicious item key contents. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testInvalidMacroItemKey() { $this->clearLog(self::COMPONENT_SERVER); $this->call('task.create', [ 'type' => ZBX_TM_TASK_CHECK_NOW, 'request' => [ 'itemid' => self::$ext_itemid ] ]); $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, "cannot resolve macro '{HOST.CONN}'", true, 60, 1); $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, "In get_value_external() key:'script[*UNKNOWN*]'", true, 60, 1); } /** * Test injection via potentially malicious item key contents. * * @required-components server, agent * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testInvalidMacroAllowedHosts() { $this->clearLog(self::COMPONENT_SERVER); $this->expectException(\PHPUnit\Framework\ExpectationFailedException::class); $this->sendSenderValue(self::HOST_NAME, self::ITEM_TRAP2, 1); $this->waitForLogLineToBePresent(self::COMPONENT_SERVER, 'cannot process item "'.self::ITEM_TRAP2.'"', true, 60, 1, true); } /** * Test circular {HOST.CONN} configuration * * @required-components server * @configurationDataProvider defaultConfigurationProvider */ public function testHostConnMacroValidation_testHostConnRecursion() { $this->stopComponent(self::COMPONENT_SERVER); $response = $this->call('hostinterface.update', [ 'interfaceid' => self::$interfaceid, 'dns' => '{HOST.CONN}' ]); $this->assertArrayHasKey('interfaceids', $response['result']); $this->assertArrayHasKey(0, $response['result']['interfaceids']); $this->startComponent(self::COMPONENT_SERVER); } /** * Test injection via running an action operation for discovery. * * @required-components server * @configurationDataProvider traceConfigurationProvider */ public function testHostConnMacroValidation_testHostConnExpansionInSecondaryIf() { $this->stopComponent(self::COMPONENT_SERVER); $this->clearLog(self::COMPONENT_SERVER); $response = $this->call('hostinterface.update', [ 'interfaceid' => self::$interfaceid, 'dns' => 'zabbix.com', 'main' => 1 ]); $this->assertArrayHasKey('interfaceids', $response['result']); $this->assertArrayHasKey(0, $response['result']['interfaceids']); $response = $this->call('hostinterface.create', [ [ 'hostid' => self::$hostid, 'useip' => INTERFACE_USE_DNS, 'ip' => '', 'dns' => 'zabbix.com', 'main' => 1, 'port' => '10163', 'type' => INTERFACE_TYPE_SNMP, 'details' => [ 'version' => 3, 'bulk' => 1, 'max_repetitions' => 10, 'securityname' => 'zabbix', 'securitylevel' => 0, 'authprotocol' => 0, 'privprotocol' => 0, 'contextname' => 'zabbix' ] ], [ 'hostid' => self::$hostid, 'useip' => INTERFACE_USE_DNS, 'dns' => '{HOST.CONN}', 'main' => 0, 'ip' => '', 'port' => '10164', 'type' => INTERFACE_TYPE_SNMP, 'details' => [ 'version' => 3, 'bulk' => 1, 'max_repetitions' => 10, 'securityname' => 'zabbix', 'securitylevel' => 0, 'authprotocol' => 0, 'privprotocol' => 0, 'contextname' => 'zabbix' ] ], [ 'hostid' => self::$hostid, 'type' => INTERFACE_TYPE_IPMI, 'useip' => INTERFACE_USE_DNS, 'ip' => '', 'dns' => 'zabbix.com', 'port' => '1023', 'main' => 1 ], [ 'hostid' => self::$hostid, 'type' => INTERFACE_TYPE_IPMI, 'ip' => '', 'useip' => INTERFACE_USE_DNS, 'dns' => '{HOST.CONN}', 'port' => '10231', 'main' => 0 ], [ 'hostid' => self::$hostid, 'type' => INTERFACE_TYPE_JMX, 'useip' => INTERFACE_USE_DNS, 'dns' => 'zabbix.com', 'port' => '1234', 'ip' => '', 'main' => 1 ], [ 'hostid' => self::$hostid, 'type' => INTERFACE_TYPE_JMX, 'ip' => '', 'useip' => INTERFACE_USE_DNS, 'dns' => '{HOST.CONN}', 'port' => '12345', 'main' => 0 ], [ 'hostid' => self::$hostid, 'ip' => '', 'dns' => '{HOST.CONN}', 'main' => 0, 'port' => '20000', 'type' => INTERFACE_TYPE_AGENT, 'useip' => INTERFACE_USE_DNS ] ]); $this->assertArrayHasKey('interfaceids', $response['result']); $this->assertCount(7, $response['result']['interfaceids']); $this->startComponent(self::COMPONENT_SERVER); $log = file_get_contents(self::getLogPath(self::COMPONENT_SERVER)); $data = explode("\n", $log); $synced_ifs = preg_grep("/interfaceid:[0-9]+ hostid:[0-9]+ ip:'' dns:'zabbix.com' /", $data); $this->assertCount(8, $synced_ifs); } /** * Delete all created data after test. */ public static function clearData(): void { CDataHelper::call('action.delete', [self::$trigger_actionid, self::$trigger_actionid_neg, self::$drule_actionid]); CDataHelper::call('drule.delete', [self::$druleid]); CDataHelper::call('host.delete', [self::$hostid]); CDataHelper::call('script.delete', [self::$scriptid_action, self::$scriptid]); } }