feat: change browser modules api and using css styles (classes) in the vue files

This commit is contained in:
Dmitry Ng
2022-12-09 19:03:48 +03:00
parent 19b6424398
commit a9ed215b2c
17 changed files with 1338 additions and 1703 deletions
+27 -429
View File
@@ -37,8 +37,8 @@
"reason",
"version"
],
"last_module_update": "2022-10-26 00:00:00",
"last_update": "2022-10-26 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -66,8 +66,8 @@
"fields": [
"reason"
],
"last_module_update": "2022-10-26 00:00:00",
"last_update": "2022-10-26 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -96,8 +96,8 @@
"actions": [],
"events": [],
"fields": [],
"last_module_update": "2022-04-29 00:00:00",
"last_update": "2022-04-29 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -131,8 +131,8 @@
"syslog_module_stopped"
],
"fields": [],
"last_module_update": "2022-04-29 00:00:00",
"last_update": "2022-04-29 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -181,8 +181,8 @@
"subject.fullpath",
"subject.process.fullpath"
],
"last_module_update": "2022-04-29 00:00:00",
"last_update": "2022-04-29 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -249,8 +249,8 @@
"subject.process.id",
"subject.process.name"
],
"last_module_update": "2022-08-26 00:00:00",
"last_update": "2022-06-26 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -314,8 +314,8 @@
"subject.process.fullpath",
"subject.process.id"
],
"last_module_update": "2022-11-07 09:11:39",
"last_update": "2022-11-07 09:11:39"
"last_module_update": "2022-12-08 09:11:39",
"last_update": "2022-12-08 09:11:39"
},
{
"group_id": "",
@@ -364,8 +364,8 @@
"subject.fullpath",
"subject.process.fullpath"
],
"last_module_update": "2022-09-30 00:00:00",
"last_update": "2022-09-30 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -385,113 +385,42 @@
},
"actions": [],
"events": [
"Auxiliary_SuspiciousWeightsTrojan_Linux_subject_a",
"Local_Recon_by_Web_User",
"Malware_Exploit_Elf_CVE_2019_14287_a",
"Malware_Exploit_Elf_CVE_2021_4034_a",
"Malware_Trojan_Linux_Generic_a",
"Malware_Trojan_Spy_Linux_Generic_a",
"Raw_Socket",
"Reverse_Tunneling_via_SSH",
"Suspicious_Create_File_Attribute_Hidden",
"Suspicious_Create_File_Boot_Modification",
"Suspicious_Create_File_Boot_RCScripts",
"Suspicious_Create_File_Ldd_PreloadDynamicLibrary",
"Suspicious_Create_File_Scheduler_At",
"Suspicious_Create_File_Scheduler_Cron",
"Suspicious_Create_File_Scheduler_SystemdTimer",
"Suspicious_Create_File_Shell_Config",
"Suspicious_Create_File_Ssh_AuthorizedKeys",
"Suspicious_Create_File_Systemd_Service",
"Suspicious_Create_File_Xdg_Autostart",
"Suspicious_Create_Process_Chkconfig_DisableRCScripts",
"Suspicious_Create_Process_Iptables_ModifyFirewall",
"Suspicious_Create_Process_Service_Disable",
"Suspicious_Create_Process_Setenforce_ModifySELinux",
"Suspicious_Create_Process_Sudo_Bruteforce",
"Suspicious_Create_Process_WipeData_Destruction",
"Suspicious_Read_File_Email_Local",
"Suspicious_Read_File_Fstab_Configuration",
"Suspicious_Read_File_Passwd_CredentialsEnumeration",
"Suspicious_Read_File_Polkit_Policy",
"Suspicious_Read_File_Shadow_CredentialsDumping",
"Suspicious_Read_System_BIOS_Reconnaissance",
"Suspicious_Read_System_DMI_CheckVM",
"Suspicious_Read_System_Kernel_ASLR",
"Suspicious_Read_System_Kernel_PtraceScope",
"Suspicious_Read_System_KeyState_Keylogger",
"Suspicious_Read_System_PCI_GPU",
"Suspicious_Read_System_PCI_USB",
"Suspicious_Read_System_Proc_ARPSessions",
"Suspicious_Read_System_Proc_BootCmdline",
"Suspicious_Read_System_Proc_Modules",
"Suspicious_Read_System_Proc_Route",
"Suspicious_Read_System_Proc_SCSI",
"Suspicious_Read_System_Proc_TCPSessions",
"Suspicious_Write_Disk_Data_DirectAccess",
"Suspicious_Write_File_PAM_Persistence",
"Suspicious_Write_Process_Inject_Ptrace",
"Tunneling_via_SSH",
"Tunneling_via_SSHuttle_Client",
"Tunneling_via_SSHuttle_Server"
"Suspicious_Write_Process_Inject_Ptrace"
],
"fields": [
"action",
"alert.key",
"category.generic",
"category.high",
"category.low",
"correlation_name",
"correlation_type",
"event_src.asset",
"event_src.host",
"event_src.hostname",
"event_src.id",
"event_src.ip",
"event_src.subsys",
"event_src.title",
"event_src.vendor",
"importance",
"incident.aggregation.key",
"incident.aggregation.timeout",
"incident.category",
"incident.severity",
"labels",
"numfield1",
"numfield2",
"object",
"object.account.group",
"object.account.id",
"object.account.name",
"object.fullpath",
"object.name",
"object.path",
"object.process.cmdline",
"object.process.cwd",
"object.process.fullpath",
"object.process.id",
"object.process.meta",
"object.process.name",
"object.process.parent.id",
"object.process.path",
"object.state",
"object.value",
"status",
"subject",
"subject.account.id",
"subject.account.name",
"subject.account.privileges",
"subject.account.session_id",
"subject.process.fullpath",
"subject.process.id",
"subject.process.meta",
"subject.process.name",
"subject.process.parent.id",
"subject.process.path",
"subject.type"
"subject.process.path"
],
"last_module_update": "2022-11-07 12:52:12",
"last_update": "2022-11-07 12:52:12"
"last_module_update": "2022-12-08 12:52:12",
"last_update": "2022-12-08 12:52:12"
},
{
"group_id": "",
@@ -511,379 +440,48 @@
},
"actions": [],
"events": [
"Abnormal_Directory_for_Process",
"Abusing_CredSSP",
"Access_System_Credential_files_via_cmdline",
"Access_into_Sensitive_Files_via_Network_Share",
"Accessibility_Feature_Tool_Abuse",
"Account_Created_on_Local_System",
"Account_Discovery",
"Account_or_Group_discovery_via_SAM_R",
"Add_new_user_in_commandline",
"Alternate_Data_Stream",
"Audit_XP_params_change",
"Auxiliary_SuspiciousWeightsTrojan_Windows_subject_a",
"Auxiliary_SuspiciousWeightsTrojan_Windows_subject_b",
"BitsJob_Download_and_Run",
"COM_object_persistence",
"CVE_2021_41379_Subrule_Elevation_service_chain",
"CVE_2021_41379_Subrule_Start_elevation",
"CVE_2021_41379_exploitation",
"CVE_2022_26500_26501_exploitation",
"CVE_2022_26503_Subrule_1",
"CVE_2022_26503_Subrule_2",
"CVE_2022_26503_exploitation",
"Client_Side_Execution_via_DCOM",
"Clipboard_Access",
"Clipboard_Access_Powershell",
"Cmstp_AWL_Bypass",
"Cobalt_Strike_Assembly",
"Cobalt_Strike_Payload_Delivery_Check",
"Cobalt_Strike_Pipe_from_AdminShare",
"Cobalt_Strike_Process_Injection",
"Cobalt_Strike_Psexec_Jump",
"Cobalt_Strike_SMB_Beacon",
"Cobalt_Strike_Service_Move",
"Code_Execution_Via_JDK_Tools",
"Command_Processor_Autorun_Modify",
"Computer_object_ldap_request",
"ControlPanel_AWL_Bypass",
"Copy_Mimikatz_To_Share",
"Credential_Access_to_Passwords_Storage",
"Credential_Dump_in_Local_Registry",
"Csc_AWL_Bypass",
"DRSUAPI_User_Enumeration",
"DSInternals_Usage",
"Disable_Credential_Guard",
"Disable_LSA_Protection",
"Disable_Restricted_Admin_Mode",
"Dnscmd_AWL_Bypass",
"Domain_Controllers_Discovery",
"Domain_Trust_Discovery",
"Download_File_Through_Curl",
"Download_File_Through_Windows_Defender",
"Downloading_Remote_File_Via_Lolbas",
"Empire_Stager",
"Esentutil_Copy_File",
"Execute_Encoded_Powershell",
"Execute_Malicious_Command",
"Execute_Malicious_Powershell_Cmdlet",
"Execute_PSEXEC",
"Execute_Suspicious_Command_via_cmd",
"Failed_LSASS_Injection",
"Fast_Create_and_Delete_Account",
"Finger_AWL_Bypass",
"Groups_And_Users_Enumeration",
"Hidden_Scheduled_Task",
"Hidden_Service_Create",
"Hide_Account_from_Logon_Screen",
"IEExec_AWL_Bypass",
"Impacket_SMBEXEC",
"Impacket_Secretsdump",
"InstallUtil_AWL_Bypass",
"Intercept_Creds_from_MSTSC",
"Internal_Monologue_Attack",
"KeePass_Keys_Extraction",
"KeePass_Persistence",
"Koadic_MSHTA_Stager",
"Koadic_REGSVR32_Stager",
"Koadic_Rundll32_Stager",
"Koadic_WMIC_Stager",
"LAPS_Enumeration",
"LOLBin_Copying",
"LSA_SSP_Change",
"Lazagne_Usage",
"Local_Pass_the_Hash",
"Lsass_Dump_via_SilentProcessExit",
"Lsass_SilentProcessExit_Keys",
"MSBuild_AWL_Bypass",
"MSHTA_AWL_Bypass",
"MSXSL_AWL_Bypass",
"Malicious_CHM_File",
"Malicious_Office_Document",
"Malware_Backdoor_Win32_Evilnum_a",
"Malware_Backdoor_Win32_Havex_a",
"Malware_Backdoor_Win32_NetWire_b",
"Malware_Backdoor_Win32_Pteredo_a",
"Malware_Backdoor_Win64_Throwback_b",
"Malware_Exploit_Win32_CVE_2022_30190_a",
"Malware_Exploit_Win32_PrintSpooler_a",
"Malware_Hacktool_Win32_CrackMapExec_a",
"Malware_Trojan_Dropper_MSOffice_Launcher_a",
"Malware_Trojan_Dropper_Script_Generic_a",
"Malware_Trojan_Dropper_Win32_Generic_k",
"Malware_Trojan_Ransom_Win32_Generic_a",
"Malware_Trojan_Ransom_Win32_Generic_b",
"Malware_Trojan_Win32_Generic_a",
"Malware_Trojan_Win32_Generic_o",
"Malware_Trojan_Win32_Generic_s",
"Malware_Trojan_Win32_Generic_t",
"Mavinject_AWL_Bypass",
"Metasploit_Payload",
"Microsoft_Teams_AWL_Bypass",
"Mimikatz_Command",
"Msiexec_AWL_Bypass",
"NetCat_Usage",
"Network_Share_Discovery",
"Odbcconf_AWL_Bypass",
"Office_File_with_Macros",
"Office_Normal_dotm_modification",
"Office_XLL_modification",
"Password_Policy_Discovery",
"Pcalua_AWL_Bypass",
"Permission_Groups_Discovery",
"Persistence_Netsh_DLL",
"Port_Forwarding_or_Tunneling",
"Potential_Users_Or_Groups_Enumeration_Process",
"Potential_domain_groups_and_users_enumeration_handle",
"Potential_localgroups_and_administrators_enumeration_handle",
"Powershell_Remoting",
"Process_Discovery",
"Proxy_Tools_Usage",
"RDP_Session_Hijacking",
"Reading_Registry_Objects",
"RegAsm_or_RegSvcs_AWL_Bypass",
"Registry_Winlogon_Helper",
"Regsvr32_AWL_Bypass",
"Remote_Admin_Share_Access",
"Remote_Code_Execution_Via_AtSvc",
"Remote_Connection_through_SMBEXEC_WinXP",
"Remote_Copy_Credential_Dump_Artifact",
"Remote_Copying_Malicious_File",
"Remote_File_Download_Via_Certutil",
"Remote_Password_Dump",
"Remoting_Impacket_PsExec",
"Remoting_SysInternals_PsExec",
"Remoting_WMI",
"Remoting_WinExec",
"Remoting_Windows_Shell",
"Remove_Access_To_Sensitive_Account",
"Remove_Account_From_Sensitive_Group",
"Rubeus_Usage",
"RunAs_Subrule_Login",
"RunAs_System_or_External_tools",
"Run_Malicious_Msbuild_Project",
"Run_whoami_as_System",
"Rundll32_AWL_Bypass",
"SPN_LDAP_requests",
"Scheduled_Task_Was_Created_Or_Updated_Via_Schtasks",
"Search_Stored_Credentials",
"Service_Created_or_Modified",
"Shadow_Copies_Deletion_with_Builtin_Tools",
"Shadow_Key_Creation",
"Shadow_Screen_save",
"Shadow_Screen_saves_PowerShell",
"SharpSploit_Usage",
"SharpWMI_Usage",
"Sharphound_Client_Side",
"Sharphound_Server_Side",
"SilentTrinity_Stager",
"Sliver_Shell_Usage",
"Software_Discovery",
"Spoolsv_Priv_Escalation",
"Stop_Important_Service",
"Stop_Important_Service_registry",
"Subrule_Sharphound_Client_Side",
"Subrule_Sharphound_Server_Side",
"Suspicious_Access_To_Users_Folder",
"Suspicious_Child_from_Messenger_Process",
"Suspicious_Create_File_FromBrowser_SuspiciousExtension",
"Suspicious_Create_File_FromMessenger_SuspiciousExtension",
"Suspicious_Create_File_PartitionMaster_DirectAccess",
"Suspicious_Create_File_RawDisk_DirectAccess",
"Suspicious_Create_Object_Account_Persistence",
"Suspicious_Create_Process_At_Persistence",
"Suspicious_Create_Process_BitsAdmin_RestrictionBypass",
"Suspicious_Create_Process_Debugger_Inject",
"Suspicious_Create_Process_DirectoryMock_UACBypass",
"Suspicious_Create_Process_NetSh_NetShell",
"Suspicious_Create_Process_Ping_SelfDelete",
"Suspicious_Create_Process_RunScript_WScript",
"Suspicious_Create_Process_Schtasks_Persistence",
"Suspicious_Create_Process_TaskKill_TerminateProcess",
"Suspicious_Create_Process_VSTOInstaller_OfficeAddIns",
"Suspicious_Create_Process_VirtualInstance_Evasion",
"Suspicious_Create_Process_WinDef_AddExclusion",
"Suspicious_Create_Process_WinDef_ChangeSettings",
"Suspicious_Create_Process_WithDebugger_Execution_Predetect_2_1",
"Suspicious_Create_Process_WusaExtract_UACBypass",
"Suspicious_Create_Registry_Key_SafeBoot",
"Suspicious_Delete_Registry_Key_ETWProvider",
"Suspicious_Delete_Registry_Key_RunAsPPL",
"Suspicious_Delete_Registry_Key_SafeBoot",
"Suspicious_Delete_Registry_Key_Service",
"Suspicious_Delete_Registry_Key_TaskSD",
"Suspicious_File_Created_by_Legal_Process",
"Suspicious_Windows_Kernel_creating",
"Suspicious_Wmic_Command",
"Suspicious_Write_File_EfiSystemPartition_Persistence",
"Suspicious_Write_File_USB_AirSpread",
"Suspicious_Write_Process_Inject_CreateRemoteThread",
"Suspicious_Write_Registry_Key_CPL",
"Suspicious_Write_Registry_Key_ChangeDefaultFileAssociation",
"Suspicious_Write_Registry_Key_ChangeFirewallPolicy",
"Suspicious_Write_Registry_Key_ChangeRdpPort",
"Suspicious_Write_Registry_Key_CredentialsDelegation",
"Suspicious_Write_Registry_Key_DisableAppLaunch",
"Suspicious_Write_Registry_Key_DisableTaskManager",
"Suspicious_Write_Registry_Key_DisableUAC",
"Suspicious_Write_Registry_Key_FlashConfigEnrollee",
"Suspicious_Write_Registry_Key_ImagePath",
"Suspicious_Write_Registry_Key_InjectAppCertDlls",
"Suspicious_Write_Registry_Key_InjectAppInitDLLs",
"Suspicious_Write_Registry_Key_InjectImageFileExecutionOptions",
"Suspicious_Write_Registry_Key_InjectLoadAppInitDLLs",
"Suspicious_Write_Registry_Key_InjectSilentProcessExit",
"Suspicious_Write_Registry_Key_KillAntivirus",
"Suspicious_Write_Process_Inject_ProcessTampering",
"Suspicious_Write_Registry_Key_LsaComponents",
"Suspicious_Write_Registry_Key_ModifyRdpSettings",
"Suspicious_Write_Registry_Key_OfficeTemplate",
"Suspicious_Write_Registry_Key_ProxyHijack",
"Suspicious_Write_Registry_Key_SafeBoot",
"Suspicious_Write_Registry_Key_ScreenSaver",
"Suspicious_Write_Registry_Key_SvchostContextService",
"Suspicious_Write_Registry_Key_TerminalContextService",
"Suspicious_Write_Registry_Key_TimeProviders",
"Suspicious_process_execution_sequence",
"Sysmon_Driver_Unload",
"System_Information_Discovery",
"System_Network_Configuration_Discovery",
"System_Network_Connections_Discovery",
"System_Service_Discovery",
"TikiTorch_Process_Injection",
"Universal_Windows_Platform_Apps_Modify",
"User_object_ldap_request",
"Userinitmprlogonscript_Modify",
"WDAC_Bypass_via_Dbgsrv",
"WDigest_Enable",
"WMI_Subscriptions",
"WinAPI_Access_from_Powershell",
"Windows_Accessibility_StickyKey_modification",
"Windows_Autorun_Modification",
"Windows_Defender_Disable",
"Windows_Eventlog_cleaning",
"Windows_Hacktool_Usage",
"Windows_Malicious_service_registration",
"Windows_Registry_sensitive_keys_modification",
"Windows_Service_Installed",
"Windows_Shadow_copy_removal",
"Windows_WMI_event_consumer_registration",
"Windows_WMI_event_subscription_removal",
"Windows_firewall_enable_local_RDP",
"XSL_Script_WMIC_Execution"
"Suspicious_Write_Registry_Key_ScreenSaver"
],
"fields": [
"action",
"alert.context",
"alert.key",
"category.generic",
"category.high",
"category.low",
"chain_id",
"correlation_name",
"correlation_type",
"dst.asset",
"dst.fqdn",
"dst.host",
"dst.hostname",
"dst.ip",
"dst.mac",
"dst.port",
"event_src.asset",
"event_src.category",
"event_src.fqdn",
"event_src.host",
"event_src.hostname",
"event_src.ip",
"event_src.rule",
"event_src.subsys",
"event_src.title",
"event_src.vendor",
"importance",
"incident.aggregation.key",
"incident.aggregation.time_window",
"incident.aggregation.timeout",
"incident.attacking_addresses",
"incident.category",
"incident.severity",
"labels",
"numfield1",
"object",
"object.account.domain",
"object.account.fullname",
"object.account.id",
"object.account.name",
"object.account.privileges",
"object.account.session_id",
"object.domain",
"object.fullpath",
"object.hash",
"object.id",
"object.name",
"object.new_value",
"object.path",
"object.process.cmdline",
"object.process.cwd",
"object.process.fullpath",
"object.process.guid",
"object.process.hash",
"object.process.id",
"object.process.meta",
"object.process.name",
"object.process.original_name",
"object.process.parent.cmdline",
"object.process.parent.fullpath",
"object.process.parent.guid",
"object.process.parent.id",
"object.process.parent.name",
"object.process.parent.path",
"object.process.path",
"object.process.version",
"object.property",
"object.query",
"object.state",
"object.storage.fullpath",
"object.storage.id",
"object.storage.name",
"object.storage.path",
"object.type",
"object.value",
"object.vendor",
"object.version",
"reason",
"src.asset",
"src.fqdn",
"src.host",
"src.hostname",
"src.ip",
"src.mac",
"src.port",
"status",
"subject",
"subject.account.domain",
"subject.account.fullname",
"subject.account.id",
"subject.account.name",
"subject.account.privileges",
"subject.account.session_id",
"subject.name",
"subject.process.cmdline",
"subject.process.cwd",
"subject.process.fullpath",
"subject.process.guid",
"subject.process.hash",
"subject.process.id",
"subject.process.meta",
"subject.process.name",
"subject.process.original_name",
"subject.process.parent.id",
"subject.process.path",
"subject.process.version",
"subject.type",
"subject.version"
"subject.process.path"
],
"last_module_update": "2022-10-26 00:00:00",
"last_update": "2022-10-26 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -913,7 +511,7 @@
"fields": [
"reason"
],
"last_module_update": "2022-11-02 00:00:00",
"last_update": "2022-11-02 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
}
]
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
+126 -142
View File
@@ -1,163 +1,147 @@
<template>
<div>
<el-tabs tab-position="left" v-model="leftTab">
<el-tab-pane
name="api"
:label="locale[$i18n.locale]['api']"
class="layout-fill overflow-hidden"
v-if="viewMode === 'agent'"
>
<div id="exec_actions" class="layout-margin-xl limit-length">
<el-select v-model="actionName" slot="prepend" :placeholder="locale[$i18n.locale]['actionSelectPl']">
<el-option v-for="(id, idx) in module.info.actions"
:label="module.locale.actions[id][$i18n.locale].title"
:value="id"
:key="idx"
></el-option>
</el-select>
<div v-if="actionName">
<div id="inp_actions"
v-for="(id, idx) in module.current_action_config[actionName].fields"
:key="idx">
<el-input
:placeholder="module.locale.fields[id][$i18n.locale].description"
v-model="actionDataModel[id]">
</el-input>
<div>
<div id="exec_actions" class="layout-margin-bottom-xl">
<el-select v-model="actionName" slot="prepend" :placeholder="locale[$i18n.locale]['actionSelectPl']">
<el-option v-for="(id, idx) in module.info.actions"
:label="module.locale.actions[id][$i18n.locale].title"
:value="id"
:key="idx"
></el-option>
</el-select>
<div v-if="actionName">
<div id="inp_actions"
v-for="(id, idx) in module.current_action_config[actionName].fields"
:key="idx">
<el-input
:placeholder="module.locale.fields[id][$i18n.locale].description"
v-model="actionDataModel[id]">
</el-input>
</div>
<el-button @click="submitReqToExecAction" slot="append"
>{{ locale[$i18n.locale]["buttonExecAction"] }}
</el-button>
</div>
<el-button @click="submitReqToExecAction" slot="append"
>{{ locale[$i18n.locale]["buttonExecAction"] }}
</el-button>
</div>
</div>
<div class="layout-fill layout-row layout-row-column layout-row-between scrollable">
<ul>
<li :key="line" v-for="line in lines">{{ line }}</li>
</ul>
<div class="layout-column layout-align-space-between scrollable">
<ul>
<li :key="line" v-for="line in lines">{{ line }}</li>
</ul>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
const name = "responder";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
data: () => ({
leftTab: undefined,
connection: {},
lines: [],
actionName: undefined,
actionDataModel: {},
locale: {
ru: {
api: "Удаление файлов",
buttonExecAction: "Выполнить действие",
connected: "— подключение к серверу установлено",
connError: "Не удалось подключиться к серверу",
recvError: "Не удалось выполнить операцию",
checkError: "Данные введены некорректно",
actionError: "Выберите действие из списка",
actionSelectPl: "Выбрать действие"
},
en: {
api: "File remover",
buttonExecAction: "Exec action",
connected: "— connection to the server established",
connError: "Failed to connect to the server",
recvError: "Unable to perform the operation",
checkError: "Data entered incorrectly",
actionError: "Please choose action from list",
actionSelectPl: "Select action"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['connError']);
console.log(error);
},
);
}
},
mounted() {
this.leftTab = this.viewMode === 'agent' ? 'api' : undefined;
},
methods: {
recvData(msg) {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
this.lines.push(
`${date_ms} RECV DATA: ${new TextDecoder(
"utf-8"
).decode(msg.content.data)}`
);
},
submitReqToExecAction() {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
if (!this.actionName) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["actionError"]);
return;
}
const defActCfg = this.module.default_action_config[this.actionName]
if (typeof(defActCfg) !== "object" || !Array.isArray(defActCfg.fields) || defActCfg.fields.length === 0) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let actionData = {};
try {
for (let fieldID in this.module.default_action_config[this.actionName].fields) {
const fieldName = this.module.default_action_config[this.actionName].fields[fieldID];
switch (this.module.fields_schema.properties[fieldName]["type"]) {
case "number":
actionData[fieldName] = parseInt(this.actionDataModel[fieldName], 10);
break;
case "string":
actionData[fieldName] = this.actionDataModel[fieldName].toString();
break;
case "array":
case "object":
actionData[fieldName] = JSON.parse(this.actionDataModel[fieldName].toString());
break;
}
if (!actionData[fieldName]) {
throw "empty field value";
}
name,
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
connection: {},
lines: [],
actionName: undefined,
actionDataModel: {},
locale: {
ru: {
buttonExecAction: "Выполнить действие",
connected: "— подключение к серверу установлено",
connError: "Не удалось подключиться к серверу",
recvError: "Не удалось выполнить операцию",
checkError: "Данные введены некорректно",
actionError: "Выберите действие из списка",
actionSelectPl: "Выбрать действие"
},
en: {
buttonExecAction: "Exec action",
connected: "— connection to the server established",
connError: "Failed to connect to the server",
recvError: "Unable to perform the operation",
checkError: "Data entered incorrectly",
actionError: "Please choose action from list",
actionSelectPl: "Select action"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['connError']);
console.log(error);
},
);
}
},
methods: {
recvData(msg) {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
this.lines.push(
`${date_ms} RECV DATA: ${new TextDecoder(
"utf-8"
).decode(msg.content.data)}`
);
},
submitReqToExecAction() {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
if (!this.actionName) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["actionError"]);
return;
}
const defActCfg = this.module.default_action_config[this.actionName]
if (typeof (defActCfg) !== "object" || !Array.isArray(defActCfg.fields) || defActCfg.fields.length === 0) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let actionData = {};
try {
for (let fieldID in this.module.default_action_config[this.actionName].fields) {
const fieldName = this.module.default_action_config[this.actionName].fields[fieldID];
switch (this.module.fields_schema.properties[fieldName]["type"]) {
case "number":
actionData[fieldName] = parseInt(this.actionDataModel[fieldName], 10);
break;
case "string":
actionData[fieldName] = this.actionDataModel[fieldName].toString();
break;
case "array":
case "object":
actionData[fieldName] = JSON.parse(this.actionDataModel[fieldName].toString());
break;
}
if (!actionData[fieldName]) {
throw "empty field value";
}
}
} catch (e) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let data = JSON.stringify({
data: actionData,
actions: [`${this.module.info.name}.${this.actionName}`]
});
this.lines.push(
`${date_ms} SEND ACTION: ${data}`
);
this.connection.sendAction(data, this.actionName);
}
}
catch (e) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let data = JSON.stringify({
data: actionData,
actions: [`${this.module.info.name}.${this.actionName}`]
});
this.lines.push(
`${date_ms} SEND ACTION: ${data}`
);
this.connection.sendAction(data, this.actionName);
}
}
};
</script>
<style scoped>
#exec_actions .el-select, #inp_actions .el-input {
#exec_actions .el-select, #inp_actions .el-input {
max-width: 800px;
min-width: 400px;
width: 100%;
margin-bottom: 12px;
}
}
</style>
+303 -310
View File
@@ -1,342 +1,335 @@
<template>
<div>
<el-tabs tab-position="left" v-model="leftTab">
<el-tab-pane
name="api"
:label="locale[$i18n.locale]['api']"
class="layout-fill layout-column overflow-hidden"
v-if="viewMode === 'agent'"
>
<div class="layout-margin-xl limit-length">
<el-input :placeholder="locale[$i18n.locale]['filePl']" v-model="filepath">
<el-button
slot="append"
icon="el-icon-s-promotion"
class="flex-none"
@click="submitReqToExecAction"
>{{ locale[$i18n.locale]['buttonExecAction'] }}
</el-button>
</el-input>
<div id="error" v-if="lastExecError" class="invalid-feedback">
{{ lastExecError }}
</div>
<div>
<div class="layout-margin-bottom-xl">
<el-input :placeholder="locale[$i18n.locale]['filePl']" v-model="filepath">
<el-button
slot="append"
icon="el-icon-s-promotion"
class="flex-none"
@click="submitReqToExecAction"
>{{ locale[$i18n.locale]['buttonExecAction'] }}
</el-button>
</el-input>
<div id="error" v-if="lastExecError" class="invalid-feedback">
{{ lastExecError }}
</div>
</div>
<div id="query">
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 8}"
:placeholder="locale[$i18n.locale]['queryPl']"
v-model="sqlQuery"
@keyup.ctrl.enter.native="execSQL">
</el-input>
<div id="error" v-if="lastSqlError" class="invalid-feedback">
{{ lastSqlError }}
</div>
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 8}"
:placeholder="locale[$i18n.locale]['queryPl']"
v-model="sqlQuery"
@keyup.ctrl.enter.native="execSQL">
</el-input>
<div id="error" v-if="lastSqlError" class="invalid-feedback">
{{ lastSqlError }}
</div>
</div>
<br>
<p class="layout-margin-xl buttons">
<el-button type="primary" @click="execSQL" :disabled="!connection"
>{{ locale[$i18n.locale]['buttonExec'] }}</el-button>
<el-button @click="saveQuery"
>{{ locale[$i18n.locale]['buttonSave'] }}</el-button>
<el-button @click="loadQuery"
>{{ locale[$i18n.locale]['buttonLoad'] }}</el-button>
<el-button @click="resetFilters"
>{{ locale[$i18n.locale]['buttonReset'] }}</el-button>
<el-button type="primary" @click="execSQL" :disabled="!connection"
>{{ locale[$i18n.locale]['buttonExec'] }}
</el-button>
<el-button @click="saveQuery"
>{{ locale[$i18n.locale]['buttonSave'] }}
</el-button>
<el-button @click="loadQuery"
>{{ locale[$i18n.locale]['buttonLoad'] }}
</el-button>
<el-button @click="resetFilters"
>{{ locale[$i18n.locale]['buttonReset'] }}
</el-button>
</p>
<div id="search">
<el-input
:placeholder="locale[$i18n.locale]['searchPl']"
v-model="queryFilterText"
class="input-with-select"
>
<el-select
v-model="queryFilterField"
slot="prepend"
<el-input
:placeholder="locale[$i18n.locale]['searchPl']"
v-model="queryFilterText"
class="input-with-select"
>
<el-option
:label="locale[$i18n.locale]['allFields']"
value="all"
></el-option>
<el-option
v-for="(col, i) in queryColumns"
:key="i"
:label="col.prop"
:value="col.prop"
></el-option>
</el-select>
</el-input>
<el-select
v-model="queryFilterField"
slot="prepend"
>
<el-option
:label="locale[$i18n.locale]['allFields']"
value="all"
></el-option>
<el-option
v-for="(col, i) in queryColumns"
:key="i"
:label="col.prop"
:value="col.prop"
></el-option>
</el-select>
</el-input>
</div>
<br>
<el-checkbox-group v-model="options" class="layout-margin-bottom-s">
<el-checkbox
:label="locale[$i18n.locale]['chgFixedFirst']"
></el-checkbox>
<el-checkbox
:label="locale[$i18n.locale]['chgUseRegexp']"
></el-checkbox>
<el-checkbox-group v-model="options" class="layout-margin-bottom-m">
<el-checkbox
:label="locale[$i18n.locale]['chgFixedFirst']"
></el-checkbox>
<el-checkbox
:label="locale[$i18n.locale]['chgUseRegexp']"
></el-checkbox>
</el-checkbox-group>
<div ref="boxTable" style="flex-grow: 1">
<el-table
ref="resultTable"
border
style="position: absolute"
:height="height"
:data="queryDataFilter"
>
<el-table-column
v-for="(col, i) in queryColumns"
:key="i"
:prop="col.prop"
:label="col.prop"
:width="col.width"
:fixed="i === 0 && options.indexOf(locale[$i18n.locale]['chgFixedFirst']) !== -1"
:filters="col.filters"
:filter-method="filterHandler"
sortable
><template slot-scope="scope">{{scope.row[col.prop]}}</template></el-table-column>
</el-table>
<el-table
ref="resultTable"
border
style="position: absolute"
:height="height"
:data="queryDataFilter"
>
<el-table-column
v-for="(col, i) in queryColumns"
:key="i"
:prop="col.prop"
:label="col.prop"
:width="col.width"
:fixed="i === 0 && options.indexOf(locale[$i18n.locale]['chgFixedFirst']) !== -1"
:filters="col.filters"
:filter-method="filterHandler"
sortable
>
<template slot-scope="scope">{{ scope.row[col.prop] }}</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
const name = "file_uploader";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
data: () => ({
leftTab: undefined,
height: 100,
timerId: undefined,
sqlQuery: `SELECT * FROM files ORDER BY id DESC;`,
filepath: "",
connection: undefined,
queryColumns: [],
queryData: [],
options: [],
lastSqlError: "",
lastExecError: "",
queryFilterText: "",
queryFilterField: "all",
nStdCol: 1.4,
nPxChar: 9,
nCharsPad: 7,
locale: {
ru: {
api: "Отправка файлов",
buttonExec: "Выполнить запрос",
buttonSave: "Сохранить запрос",
buttonLoad: "Загрузить изменения",
buttonReset: "Сбросить фильтр",
buttonExecAction: "Отправить файл",
connected: "— подключение к серверу установлено",
connAgentError: "Не удалось подключиться к агенту",
connServerError: "Не удалось подключиться к серверу",
fileCheckError: "Внутренняя ошибка сервера",
fileNotFoundError: "Файл не найден или недоступен",
fileSizeError: "Превышен максимальный размер файла",
filePathError: "Путь к файлу задан некорректно",
checkSuccess: "Файл отправлен во внешнюю систему",
recvError: "Не удалось выполнить SQL-запрос",
allFields: "Все",
chgFixedFirst: "Закрепить первый столбец",
chgUseRegexp: "Использовать регулярные выражения",
rgAgentSide: "Выполнить на агенте",
rgServerSide: "Выполнить на сервере",
searchPl: "Поиск по файлам",
queryPl: "SQL-запрос для выборки",
filePl: "Путь к файлу"
},
en: {
api: "File sender",
buttonExec: "Execute query",
buttonSave: "Save query",
buttonLoad: "Load query",
buttonReset: "Reset filter",
buttonExecAction: "Send file",
connected: "— connection to the server established",
connAgentError: "Failed to connect to the agent",
connServerError: "Failed to connect to the server",
fileCheckError: "Server internal error",
fileNotFoundError: "File not found or not available",
fileSizeError: "File size exceeded",
filePathError: "Invalid file path",
checkSuccess: "File is sent to external system",
recvError: "Failed to execute SQL query",
allFields: "All",
chgFixedFirst: "Fix first column",
chgUseRegexp: "Use regexp",
rgAgentSide: "Execute on agent side",
rgServerSide: "Execute on server side",
searchPl: "Search by file",
queryPl: "SQL query for selection",
filePl: "File path"
}
}
}),
created() {
if (this.viewMode === 'agent') {
window.addEventListener("resize", this.resizeTable);
this.timerId = window.setInterval(this.resizeTable, 500);
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.lastSqlError = this.locale[this.$i18n.locale]['connServerError'];
this.$root.NotificationsService.error(this.lastSqlError);
console.log(error);
},
);
}
},
destroyed() {
if (this.viewMode === 'agent') {
window.removeEventListener("resize", this.resizeTable);
window.clearInterval(this.timerId);
}
},
mounted() {
this.leftTab = this.viewMode === 'agent' ? 'api' : undefined;
},
computed: {
queryDataFilter() {
let re = null;
try {
re = new RegExp(this.queryFilterText);
} catch {}
const isRegex = re && this.options.indexOf(this.locale[this.$i18n.locale]['chgUseRegexp']) !== -1;
const text = isRegex ? re : this.queryFilterText.toLowerCase();
const search = (row, field) => {
const fields = Object.keys(row).filter(key => key == field || field == "all");
const match = (val) => isRegex ? val.match(text) : val.toLowerCase().includes(text);
return fields.some(f => match((row[f] || "null").toString()));
}
return this.queryData.filter(row => !text || search(row, this.queryFilterField));
}
},
methods: {
recvData(msg) {
let data = new TextDecoder("utf-8").decode(msg.content.data);
let result = JSON.parse(data);
if (result.status === "error") {
if (result.error == "connection_error") {
this.lastSqlError = this.locale[this.$i18n.locale]['connAgentError'];
this.$root.NotificationsService.error(this.lastSqlError);
} else if (result.error == "internal_error") {
this.lastExecError = this.locale[this.$i18n.locale]['fileCheckError'];
this.$root.NotificationsService.error(this.lastExecError);
} else if (result.error == "file_not_found") {
this.lastExecError = this.locale[this.$i18n.locale]['fileNotFoundError'];
this.$root.NotificationsService.error(this.lastExecError);
} else if (result.error == "file_size_exceeded") {
this.lastExecError = this.locale[this.$i18n.locale]['fileSizeError'];
this.$root.NotificationsService.error(this.lastExecError);
} else {
this.lastSqlError = this.locale[this.$i18n.locale]['recvError']
this.$root.NotificationsService.error(this.lastSqlError);
this.lastSqlError += ": " + result.error;
name,
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
height: 100,
timerId: undefined,
sqlQuery: `SELECT * FROM files ORDER BY id DESC;`,
filepath: "",
connection: undefined,
queryColumns: [],
queryData: [],
options: [],
lastSqlError: "",
lastExecError: "",
queryFilterText: "",
queryFilterField: "all",
nStdCol: 1.4,
nPxChar: 9,
nCharsPad: 7,
locale: {
ru: {
buttonExec: "Выполнить запрос",
buttonSave: "Сохранить запрос",
buttonLoad: "Загрузить изменения",
buttonReset: "Сбросить фильтр",
buttonExecAction: "Отправить файл",
connected: "— подключение к серверу установлено",
connAgentError: "Не удалось подключиться к агенту",
connServerError: "Не удалось подключиться к серверу",
fileCheckError: "Внутренняя ошибка сервера",
fileNotFoundError: "Файл не найден или недоступен",
fileSizeError: "Превышен максимальный размер файла",
filePathError: "Путь к файлу задан некорректно",
checkSuccess: "Файл отправлен во внешнюю систему",
recvError: "Не удалось выполнить SQL-запрос",
allFields: "Все",
chgFixedFirst: "Закрепить первый столбец",
chgUseRegexp: "Использовать регулярные выражения",
rgAgentSide: "Выполнить на агенте",
rgServerSide: "Выполнить на сервере",
searchPl: "Поиск по файлам",
queryPl: "SQL-запрос для выборки",
filePl: "Путь к файлу"
},
en: {
buttonExec: "Execute query",
buttonSave: "Save query",
buttonLoad: "Load query",
buttonReset: "Reset filter",
buttonExecAction: "Send file",
connected: "— connection to the server established",
connAgentError: "Failed to connect to the agent",
connServerError: "Failed to connect to the server",
fileCheckError: "Server internal error",
fileNotFoundError: "File not found or not available",
fileSizeError: "File size exceeded",
filePathError: "Invalid file path",
checkSuccess: "File is sent to external system",
recvError: "Failed to execute SQL query",
allFields: "All",
chgFixedFirst: "Fix first column",
chgUseRegexp: "Use regexp",
rgAgentSide: "Execute on agent side",
rgServerSide: "Execute on server side",
searchPl: "Search by file",
queryPl: "SQL query for selection",
filePl: "File path"
}
}
}),
created() {
if (this.viewMode === 'agent') {
window.addEventListener("resize", this.resizeTable);
this.timerId = window.setInterval(this.resizeTable, 500);
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.lastSqlError = this.locale[this.$i18n.locale]['connServerError'];
this.$root.NotificationsService.error(this.lastSqlError);
console.log(error);
},
);
}
} else if (result.type == "exec_sql_resp") {
this.queryColumns = result.cols
.map((c, i) => ({
prop: c,
width: this.getColWidth(result.rows
.map(r => (r[i] || "null").toString().length), c.length),
filters: result.rows
.map(r => r[i])
.sort()
.filter((v, i, a) => a.indexOf(v) === i)
.map(v => ({
text: (v || "null").toString().substring(0, 40),
value: v,
})),
}));
this.queryData = result.rows.map((r) => r.reduce((a,x,i) => ({...a, [result.cols[i]]: x}), {}));
this.resizeTable();
} else if (result.type == "exec_upload_resp") {
this.$root.NotificationsService.success(`${this.locale[this.$i18n.locale]['checkSuccess']}`);
} else {
console.log("received unknown message type from server", result)
}
},
submitReqToExecAction() {
this.lastExecError = "";
let filepath = this.filepath.trim()
if (filepath === "" || filepath.length > 256) {
this.lastExecError = this.locale[this.$i18n.locale]['filePathError'];
this.$root.NotificationsService.error(this.lastExecError);
} else {
let actionName = "fu_upload_object_file";
let data = JSON.stringify({
data: { "object.fullpath": filepath },
actions: [`${this.module.info.name}.${actionName}`]
});
this.connection.sendAction(data, actionName);
}
destroyed() {
if (this.viewMode === 'agent') {
window.removeEventListener("resize", this.resizeTable);
window.clearInterval(this.timerId);
}
},
execSQL() {
if (!this.connection) return;
this.lastSqlError = "";
this.queryFilterText = "";
this.queryFilterField = "all";
this.queryColumns = [];
this.queryData = [];
let data = JSON.stringify({
type: "exec_sql_req",
sql: this.sqlQuery,
});
this.connection.sendData(data);
computed: {
queryDataFilter() {
let re = null;
try {
re = new RegExp(this.queryFilterText);
} catch {
}
const isRegex = re && this.options.indexOf(this.locale[this.$i18n.locale]['chgUseRegexp']) !== -1;
const text = isRegex ? re : this.queryFilterText.toLowerCase();
const search = (row, field) => {
const fields = Object.keys(row).filter(key => key == field || field == "all");
const match = (val) => isRegex ? val.match(text) : val.toLowerCase().includes(text);
return fields.some(f => match((row[f] || "null").toString()));
}
return this.queryData.filter(row => !text || search(row, this.queryFilterField));
}
},
saveQuery() {
localStorage.setItem("FileUploaderSqlQuery", this.sqlQuery);
},
loadQuery() {
if (localStorage.getItem("FileUploaderSqlQuery")) {
this.sqlQuery = localStorage.getItem("FileUploaderSqlQuery");
this.queryData = [];
this.execSQL();
}
},
resizeTable() {
this.height = this.$refs.boxTable.clientHeight;
},
getColWidth(array, min) {
const n = array.length;
if (n === 0) {
return Math.floor(this.nPxChar * (this.nCharsPad + min));
}
const mean = array.reduce((a, b) => a + b) / n;
return Math.floor(this.nPxChar * Math.max(
this.nCharsPad + min, mean + this.nStdCol * Math.sqrt(array
.map(x => Math.pow(x - mean, 2))
.reduce((a, b) => a + b) / n))).toString();
},
filterHandler(value, row, column) {
const property = column['property'];
return row[property] === value;
},
resetFilters() {
this.queryFilterText = "";
this.queryFilterField = "all";
this.$refs.resultTable.clearFilter();
methods: {
recvData(msg) {
let data = new TextDecoder("utf-8").decode(msg.content.data);
let result = JSON.parse(data);
if (result.status === "error") {
if (result.error == "connection_error") {
this.lastSqlError = this.locale[this.$i18n.locale]['connAgentError'];
this.$root.NotificationsService.error(this.lastSqlError);
} else if (result.error == "internal_error") {
this.lastExecError = this.locale[this.$i18n.locale]['fileCheckError'];
this.$root.NotificationsService.error(this.lastExecError);
} else if (result.error == "file_not_found") {
this.lastExecError = this.locale[this.$i18n.locale]['fileNotFoundError'];
this.$root.NotificationsService.error(this.lastExecError);
} else if (result.error == "file_size_exceeded") {
this.lastExecError = this.locale[this.$i18n.locale]['fileSizeError'];
this.$root.NotificationsService.error(this.lastExecError);
} else {
this.lastSqlError = this.locale[this.$i18n.locale]['recvError']
this.$root.NotificationsService.error(this.lastSqlError);
this.lastSqlError += ": " + result.error;
}
} else if (result.type == "exec_sql_resp") {
this.queryColumns = result.cols
.map((c, i) => ({
prop: c,
width: this.getColWidth(result.rows
.map(r => (r[i] || "null").toString().length), c.length),
filters: result.rows
.map(r => r[i])
.sort()
.filter((v, i, a) => a.indexOf(v) === i)
.map(v => ({
text: (v || "null").toString().substring(0, 40),
value: v,
})),
}));
this.queryData = result.rows.map((r) => r.reduce((a, x, i) => ({...a, [result.cols[i]]: x}), {}));
this.resizeTable();
} else if (result.type == "exec_upload_resp") {
this.$root.NotificationsService.success(`${this.locale[this.$i18n.locale]['checkSuccess']}`);
} else {
console.log("received unknown message type from server", result)
}
},
submitReqToExecAction() {
this.lastExecError = "";
let filepath = this.filepath.trim()
if (filepath === "" || filepath.length > 256) {
this.lastExecError = this.locale[this.$i18n.locale]['filePathError'];
this.$root.NotificationsService.error(this.lastExecError);
} else {
let actionName = "fu_upload_object_file";
let data = JSON.stringify({
data: {"object.fullpath": filepath},
actions: [`${this.module.info.name}.${actionName}`]
});
this.connection.sendAction(data, actionName);
}
},
execSQL() {
if (!this.connection) return;
this.lastSqlError = "";
this.queryFilterText = "";
this.queryFilterField = "all";
this.queryColumns = [];
this.queryData = [];
let data = JSON.stringify({
type: "exec_sql_req",
sql: this.sqlQuery,
});
this.connection.sendData(data);
},
saveQuery() {
localStorage.setItem("FileUploaderSqlQuery", this.sqlQuery);
},
loadQuery() {
if (localStorage.getItem("FileUploaderSqlQuery")) {
this.sqlQuery = localStorage.getItem("FileUploaderSqlQuery");
this.queryData = [];
this.execSQL();
}
},
resizeTable() {
this.height = this.$refs.boxTable.clientHeight;
},
getColWidth(array, min) {
const n = array.length;
if (n === 0) {
return Math.floor(this.nPxChar * (this.nCharsPad + min));
}
const mean = array.reduce((a, b) => a + b) / n;
return Math.floor(this.nPxChar * Math.max(
this.nCharsPad + min, mean + this.nStdCol * Math.sqrt(array
.map(x => Math.pow(x - mean, 2))
.reduce((a, b) => a + b) / n))).toString();
},
filterHandler(value, row, column) {
const property = column['property'];
return row[property] === value;
},
resetFilters() {
this.queryFilterText = "";
this.queryFilterField = "all";
this.$refs.resultTable.clearFilter();
}
}
}
};
</script>
<style scoped>
#search .el-select .el-input {
#search .el-select .el-input {
width: 110px;
}
.input-with-select .el-input-group__prepend {
}
.input-with-select .el-input-group__prepend {
background-color: #fff;
}
}
</style>
+139 -149
View File
@@ -1,173 +1,163 @@
<template>
<div>
<el-tabs tab-position="left" v-model="leftTab">
<el-tab-pane
name="code"
:label="locale[$i18n.locale]['code']"
class="layout-row layout-row-column overflow-hidden"
v-if="viewMode === 'agent'"
>
<div>
<div id="container" class="conteiner editor"></div>
<p class="conteiner layout-margin-s">
<el-button type="primary" @click="execCode"
>{{ locale[$i18n.locale]['buttonExec'] }}</el-button>
<el-button @click="saveSnippet"
>{{ locale[$i18n.locale]['buttonSave'] }}</el-button>
<el-button @click="loadSnippet"
>{{ locale[$i18n.locale]['buttonLoad'] }}</el-button>
<p class="conteiner layout-margin-m">
<el-button type="primary" @click="execCode"
>{{ locale[$i18n.locale]['buttonExec'] }}
</el-button>
<el-button @click="saveSnippet"
>{{ locale[$i18n.locale]['buttonSave'] }}
</el-button>
<el-button @click="loadSnippet"
>{{ locale[$i18n.locale]['buttonLoad'] }}
</el-button>
</p>
<div class="conteiner layout-row layout-row-column overflow-hidden">
<el-tabs
v-model="bottomTab"
class="layout-fill layout-row layout-row-column"
>
<el-tab-pane :label="locale[$i18n.locale]['output']" name="output">
<div class="layout-fill layout-row layout-row-column layout-margin-bottom-s">
<pre style="min-height: 30px">{{ response_out }}</pre>
</div>
</el-tab-pane>
<el-tab-pane :label="locale[$i18n.locale]['errors']" name="errors">
<div class="layout-fill layout-row layout-row-column layout-margin-bottom-s">
<pre style="min-height: 30px">{{ response_err }}</pre>
</div>
</el-tab-pane>
</el-tabs>
<div class="conteiner layout-column overflow-hidden">
<el-tabs
v-model="bottomTab"
class="layout-fill layout-column"
>
<el-tab-pane :label="locale[$i18n.locale]['output']" name="output">
<div class="layout-fill layout-column layout-margin-bottom-m">
<pre style="min-height: 30px">{{ response_out }}</pre>
</div>
</el-tab-pane>
<el-tab-pane :label="locale[$i18n.locale]['errors']" name="errors">
<div class="layout-fill layout-column layout-margin-bottom-m">
<pre style="min-height: 30px">{{ response_err }}</pre>
</div>
</el-tab-pane>
</el-tabs>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
const name = "lua_interpreter";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
data: () => ({
name,
leftTab: undefined,
bottomTab: "output",
connection: {},
response_out: "",
response_err: "",
editor: null,
locale: {
ru: {
code: "Редактор кода",
buttonExec: "Выполнить",
buttonSave: "Сохранить",
buttonLoad: "Загрузить",
output: "Результат",
errors: "Ошибки",
connected: "— подключение к серверу установлено",
recvError: "Не удалось выполнить операцию"
},
en: {
code: "Code editor",
buttonExec: "Execute",
buttonSave: "Save",
buttonLoad: "Load",
output: "Output",
errors: "Errors",
connected: "— connection to the server established",
recvError: "Unable to perform the operation"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => console.log(error)
);
}
},
mounted() {
this.leftTab = this.viewMode === 'agent' ? 'code' : 'events';
if (this.viewMode === 'agent') {
this.$nextTick(() => {
this.initEditor();
});
}
},
methods: {
recvData(msg) {
let data = new TextDecoder("utf-8").decode(msg.content.data);
let decoded_response = JSON.parse(data);
if (decoded_response.output) {
this.response_out = decoded_response.output;
this.bottomTab = "output";
}
if (decoded_response.err) {
this.response_err = decoded_response.err;
this.bottomTab = "errors";
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['recvError']);
}
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
name,
bottomTab: "output",
connection: {},
response_out: "",
response_err: "",
editor: null,
locale: {
ru: {
buttonExec: "Выполнить",
buttonSave: "Сохранить",
buttonLoad: "Загрузить",
output: "Результат",
errors: "Ошибки",
connected: "— подключение к серверу установлено",
recvError: "Не удалось выполнить операцию"
},
en: {
buttonExec: "Execute",
buttonSave: "Save",
buttonLoad: "Load",
output: "Output",
errors: "Errors",
connected: "— connection to the server established",
recvError: "Unable to perform the operation"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => console.log(error)
);
}
},
mounted() {
if (this.viewMode === 'agent') {
this.$nextTick(() => {
this.initEditor();
});
}
},
methods: {
recvData(msg) {
let data = new TextDecoder("utf-8").decode(msg.content.data);
let decoded_response = JSON.parse(data);
if (decoded_response.output) {
this.response_out = decoded_response.output;
this.bottomTab = "output";
}
if (decoded_response.err) {
this.response_err = decoded_response.err;
this.bottomTab = "errors";
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['recvError']);
}
if (decoded_response.status) {
} else {
if (decoded_response.ret) {
this.response_err += decoded_response.ret;
if (decoded_response.status) {
} else {
if (decoded_response.ret) {
this.response_err += decoded_response.ret;
}
}
},
execCode() {
this.response_out = "";
this.response_err = "";
const model = this.editor.getModel();
const value = model.getValue();
let safe_value = value.replace("\r", "\r");
safe_value = safe_value.replace("\n", "\n");
let data = JSON.stringify({type: "exec", code: safe_value});
this.connection.sendData(data);
},
saveSnippet() {
const model = this.editor.getModel();
const value = model.getValue();
localStorage.setItem("lastState", value);
},
loadSnippet() {
const model = this.editor.getModel();
if (localStorage.getItem("lastState")) {
model.setValue(localStorage.getItem("lastState"));
}
},
initEditor() {
if (!this.editor) {
let code = 'print("Hello world!")';
if (localStorage.getItem("lastState")) {
code = localStorage.getItem("lastState");
}
const cntr = document.getElementById("container");
this.editor = this.components.monaco.editor.create(cntr, {
value: code,
language: "lua"
});
const KM = this.components.monaco.KeyMod;
const KC = this.components.monaco.KeyCode;
this.editor.addCommand(KM.CtrlCmd | KC.Enter, this.execCode);
this.editor.addCommand(KM.CtrlCmd | KC.KEY_S, this.saveSnippet);
this.editor.addCommand(KM.CtrlCmd | KC.KEY_O, this.loadSnippet);
}
}
}
},
execCode() {
this.response_out = "";
this.response_err = "";
const model = this.editor.getModel();
const value = model.getValue();
let safe_value = value.replace("\r", "\r");
safe_value = safe_value.replace("\n", "\n");
let data = JSON.stringify({ type: "exec", code: safe_value });
this.connection.sendData(data);
},
saveSnippet() {
const model = this.editor.getModel();
const value = model.getValue();
localStorage.setItem("lastState", value);
},
loadSnippet() {
const model = this.editor.getModel();
if (localStorage.getItem("lastState")) {
model.setValue(localStorage.getItem("lastState"));
}
},
initEditor() {
if (!this.editor) {
let code = 'print("Hello world!")';
if (localStorage.getItem("lastState")) {
code = localStorage.getItem("lastState");
}
const cntr = document.getElementById("container");
this.editor = this.components.monaco.editor.create(cntr, {
value: code,
language: "lua"
});
const KM = this.components.monaco.KeyMod;
const KC = this.components.monaco.KeyCode;
this.editor.addCommand(KM.CtrlCmd | KC.Enter, this.execCode);
this.editor.addCommand(KM.CtrlCmd | KC.KEY_S, this.saveSnippet);
this.editor.addCommand(KM.CtrlCmd | KC.KEY_O, this.loadSnippet);
}
}
}
};
</script>
<style scoped>
.editor {
.editor {
height: 415px;
}
}
.conteiner {
.conteiner {
min-width: 650px;
max-width: 1200px;
width: 100%;
}
}
</style>
+126 -142
View File
@@ -1,163 +1,147 @@
<template>
<div>
<el-tabs tab-position="left" v-model="leftTab">
<el-tab-pane
name="api"
:label="locale[$i18n.locale]['api']"
class="layout-fill overflow-hidden"
v-if="viewMode === 'agent'"
>
<div id="exec_actions" class="layout-margin-xl limit-length">
<el-select v-model="actionName" slot="prepend" :placeholder="locale[$i18n.locale]['actionSelectPl']">
<el-option v-for="(id, idx) in module.info.actions"
:label="module.locale.actions[id][$i18n.locale].title"
:value="id"
:key="idx"
></el-option>
</el-select>
<div v-if="actionName">
<div id="inp_actions"
v-for="(id, idx) in module.current_action_config[actionName].fields"
:key="idx">
<el-input
:placeholder="module.locale.fields[id][$i18n.locale].description"
v-model="actionDataModel[id]">
</el-input>
<div>
<div id="exec_actions" class="layout-margin-bottom-xl">
<el-select v-model="actionName" slot="prepend" :placeholder="locale[$i18n.locale]['actionSelectPl']">
<el-option v-for="(id, idx) in module.info.actions"
:label="module.locale.actions[id][$i18n.locale].title"
:value="id"
:key="idx"
></el-option>
</el-select>
<div v-if="actionName">
<div id="inp_actions"
v-for="(id, idx) in module.current_action_config[actionName].fields"
:key="idx">
<el-input
:placeholder="module.locale.fields[id][$i18n.locale].description"
v-model="actionDataModel[id]">
</el-input>
</div>
<el-button @click="submitReqToExecAction" slot="append"
>{{ locale[$i18n.locale]["buttonExecAction"] }}
</el-button>
</div>
<el-button @click="submitReqToExecAction" slot="append"
>{{ locale[$i18n.locale]["buttonExecAction"] }}
</el-button>
</div>
</div>
<div class="layout-fill layout-row layout-row-column layout-row-between scrollable">
<ul>
<li :key="line" v-for="line in lines">{{ line }}</li>
</ul>
<div class="layout-column layout-align-space-between scrollable">
<ul>
<li :key="line" v-for="line in lines">{{ line }}</li>
</ul>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
const name = "responder";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
data: () => ({
leftTab: undefined,
connection: {},
lines: [],
actionName: undefined,
actionDataModel: {},
locale: {
ru: {
api: "Завершение процессов",
buttonExecAction: "Выполнить действие",
connected: "— подключение к серверу установлено",
connError: "Не удалось подключиться к серверу",
recvError: "Не удалось выполнить операцию",
checkError: "Данные введены некорректно",
actionError: "Выберите действие из списка",
actionSelectPl: "Выбрать действие"
},
en: {
api: "Process terminator",
buttonExecAction: "Exec action",
connected: "— connection to the server established",
connError: "Failed to connect to the server",
recvError: "Unable to perform the operation",
checkError: "Data entered incorrectly",
actionError: "Please choose action from list",
actionSelectPl: "Select action"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['connError']);
console.log(error);
},
);
}
},
mounted() {
this.leftTab = this.viewMode === 'agent' ? 'api' : undefined;
},
methods: {
recvData(msg) {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
this.lines.push(
`${date_ms} RECV DATA: ${new TextDecoder(
"utf-8"
).decode(msg.content.data)}`
);
},
submitReqToExecAction() {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
if (!this.actionName) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["actionError"]);
return;
}
const defActCfg = this.module.default_action_config[this.actionName]
if (typeof(defActCfg) !== "object" || !Array.isArray(defActCfg.fields) || defActCfg.fields.length === 0) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let actionData = {};
try {
for (let fieldID in this.module.default_action_config[this.actionName].fields) {
const fieldName = this.module.default_action_config[this.actionName].fields[fieldID];
switch (this.module.fields_schema.properties[fieldName]["type"]) {
case "number":
actionData[fieldName] = parseInt(this.actionDataModel[fieldName], 10);
break;
case "string":
actionData[fieldName] = this.actionDataModel[fieldName].toString();
break;
case "array":
case "object":
actionData[fieldName] = JSON.parse(this.actionDataModel[fieldName].toString());
break;
}
if (!actionData[fieldName]) {
throw "empty field value";
}
name,
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
connection: {},
lines: [],
actionName: undefined,
actionDataModel: {},
locale: {
ru: {
buttonExecAction: "Выполнить действие",
connected: "— подключение к серверу установлено",
connError: "Не удалось подключиться к серверу",
recvError: "Не удалось выполнить операцию",
checkError: "Данные введены некорректно",
actionError: "Выберите действие из списка",
actionSelectPl: "Выбрать действие"
},
en: {
buttonExecAction: "Exec action",
connected: "— connection to the server established",
connError: "Failed to connect to the server",
recvError: "Unable to perform the operation",
checkError: "Data entered incorrectly",
actionError: "Please choose action from list",
actionSelectPl: "Select action"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['connError']);
console.log(error);
},
);
}
},
methods: {
recvData(msg) {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
this.lines.push(
`${date_ms} RECV DATA: ${new TextDecoder(
"utf-8"
).decode(msg.content.data)}`
);
},
submitReqToExecAction() {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
if (!this.actionName) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["actionError"]);
return;
}
const defActCfg = this.module.default_action_config[this.actionName]
if (typeof (defActCfg) !== "object" || !Array.isArray(defActCfg.fields) || defActCfg.fields.length === 0) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let actionData = {};
try {
for (let fieldID in this.module.default_action_config[this.actionName].fields) {
const fieldName = this.module.default_action_config[this.actionName].fields[fieldID];
switch (this.module.fields_schema.properties[fieldName]["type"]) {
case "number":
actionData[fieldName] = parseInt(this.actionDataModel[fieldName], 10);
break;
case "string":
actionData[fieldName] = this.actionDataModel[fieldName].toString();
break;
case "array":
case "object":
actionData[fieldName] = JSON.parse(this.actionDataModel[fieldName].toString());
break;
}
if (!actionData[fieldName]) {
throw "empty field value";
}
}
} catch (e) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let data = JSON.stringify({
data: actionData,
actions: [`${this.module.info.name}.${this.actionName}`]
});
this.lines.push(
`${date_ms} SEND ACTION: ${data}`
);
this.connection.sendAction(data, this.actionName);
}
}
catch (e) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]["checkError"]);
return;
}
let data = JSON.stringify({
data: actionData,
actions: [`${this.module.info.name}.${this.actionName}`]
});
this.lines.push(
`${date_ms} SEND ACTION: ${data}`
);
this.connection.sendAction(data, this.actionName);
}
}
};
</script>
<style scoped>
#exec_actions .el-select, #inp_actions .el-input {
#exec_actions .el-select, #inp_actions .el-input {
max-width: 800px;
min-width: 400px;
width: 100%;
margin-bottom: 12px;
}
}
</style>
+20
View File
@@ -0,0 +1,20 @@
return {
pt_kill_object_process_by_file_path = "pt_kill_object_process_by_file_path",
pt_kill_object_process_by_image = "pt_kill_object_process_by_image",
pt_kill_object_process_by_name = "pt_kill_object_process_by_name",
pt_kill_object_process_by_name_and_id = "pt_kill_object_process_by_name_and_id",
pt_kill_object_process_by_image_and_id = "pt_kill_object_process_by_image_and_id",
pt_kill_object_process_tree_by_file_path = "pt_kill_object_process_tree_by_file_path",
pt_kill_object_process_tree_by_image = "pt_kill_object_process_tree_by_image",
pt_kill_object_process_tree_by_name = "pt_kill_object_process_tree_by_name",
pt_kill_object_process_tree_by_name_and_id = "pt_kill_object_process_tree_by_name_and_id",
pt_kill_object_process_tree_by_image_and_id = "pt_kill_object_process_tree_by_image_and_id",
pt_kill_subject_process_by_image = "pt_kill_subject_process_by_image",
pt_kill_subject_process_by_name = "pt_kill_subject_process_by_name",
pt_kill_subject_process_by_name_and_id = "pt_kill_subject_process_by_name_and_id",
pt_kill_subject_process_by_image_and_id = "pt_kill_subject_process_by_image_and_id",
pt_kill_subject_process_tree_by_image = "pt_kill_subject_process_tree_by_image",
pt_kill_subject_process_tree_by_name = "pt_kill_subject_process_tree_by_name",
pt_kill_subject_process_tree_by_name_and_id = "pt_kill_subject_process_tree_by_name_and_id",
pt_kill_subject_process_tree_by_image_and_id = "pt_kill_subject_process_tree_by_image_and_id",
}
+159 -357
View File
@@ -1,13 +1,13 @@
require("engine")
local bit = require("bit")
local ffi = require("ffi")
local lfs = require("lfs")
local glue = require("glue")
local cjson = require("cjson.safe")
local luapath = require("path")
local process_api = require("process_api")
local lk32
if ffi.os == "Windows" then
lk32 = require("waffi.windows.kernel32")
lk32.STILL_ALIVE = 259
else
ffi.cdef[[
typedef uint32_t pid_t;
@@ -93,7 +93,20 @@ local function push_event(event_name, action_name, action_data)
end
local function update_agent_info()
agent_id, agent_path = dyn_handlers.update_agent_info()
agent_id, agent_path = process_api.update_agent_info()
end
local function collect_process_info(object_type)
local process_list = {}
process_api.for_each_process(function(proc_info)
table.insert(process_list, {
[object_type .. ".process.id"] = proc_info.pid,
[object_type .. ".process.name"] = proc_info.name,
[object_type .. ".process.fullpath"] = proc_info.path,
[object_type .. ".process.parent.id"] = proc_info.parent_pid,
})
end)
return process_list
end
local function get_process_tree(check_ppid, object_type, full_process_info, depth)
@@ -104,7 +117,7 @@ local function get_process_tree(check_ppid, object_type, full_process_info, dept
return child_list
end
if full_process_info == nil then
full_process_info = dyn_handlers.collect_process_info(object_type)
full_process_info = collect_process_info(object_type)
end
for _, proc_info in ipairs(full_process_info) do
@@ -182,7 +195,7 @@ local function kill_proc_children(action_name, object_type, proc_id)
child_action_data.data = {
[object_type .. ".process.name"] = ch_proc_name,
[object_type .. ".process.id"] = ch_proc_id,
[object_type .. ".process.fullpath"] = dyn_handlers.get_process_path(ch_proc_id)
[object_type .. ".process.fullpath"] = process_api.get_process_path(ch_proc_id)
}
__log.info("Killing -> " .. ch_proc_id)
dyn_handlers.kill_process_by_name_and_id(action_name, child_action_data, object_type, false, true)
@@ -213,51 +226,35 @@ local function check_action_structure(action_name, action_data)
action_data.data["object.process.id"] = tonumber(action_data.data["object.process.id"])
action_data.data["subject.process.id"] = tonumber(action_data.data["subject.process.id"])
local actions = {
pt_kill_object_process_by_file_path = {{"object.fullpath", "string"}},
pt_kill_object_process_by_image = {{"object.process.fullpath", "string"}},
pt_kill_object_process_by_name = {{"object.process.name", "string"}},
pt_kill_object_process_by_name_and_id = {{"object.process.name", "string"}, {"object.process.id", "number"}},
pt_kill_object_process_by_image_and_id = {{"object.process.fullpath", "string"}, {"object.process.id", "number"}},
pt_kill_object_process_tree_by_file_path = {{"object.fullpath", "string"}},
pt_kill_object_process_tree_by_image = {{"object.process.fullpath", "string"}},
pt_kill_object_process_tree_by_name = {{"object.process.name", "string"}},
pt_kill_object_process_tree_by_name_and_id = {{"object.process.name", "string"}, {"object.process.id", "number"}},
pt_kill_object_process_tree_by_image_and_id = {{"object.process.fullpath", "string"}, {"object.process.id", "number"}},
pt_kill_subject_process_by_image = {{"subject.process.fullpath", "string"}},
pt_kill_subject_process_by_name = {{"subject.process.name", "string"}},
pt_kill_subject_process_by_name_and_id = {{"subject.process.name", "string"}, {"subject.process.id", "number"}},
pt_kill_subject_process_by_image_and_id = {{"subject.process.fullpath", "string"}, {"subject.process.id", "number"}},
pt_kill_subject_process_tree_by_image = {{"subject.process.fullpath", "string"}},
pt_kill_subject_process_tree_by_name = {{"subject.process.name", "string"}},
pt_kill_subject_process_tree_by_name_and_id = {{"subject.process.name", "string"}, {"subject.process.id", "number"}},
pt_kill_subject_process_tree_by_image_and_id = {{"subject.process.fullpath", "string"}, {"subject.process.id", "number"}}
}
-- check action data structure
return (action_name == "pt_kill_object_process_by_file_path" and
type(action_data.data["object.fullpath"]) == "string") or
(action_name == "pt_kill_object_process_by_image" and
type(action_data.data["object.process.fullpath"]) == "string") or
(action_name == "pt_kill_object_process_by_name" and
type(action_data.data["object.process.name"]) == "string") or
(action_name == "pt_kill_object_process_by_name_and_id" and
type(action_data.data["object.process.name"]) == "string" and
type(action_data.data["object.process.id"]) == "number") or
(action_name == "pt_kill_object_process_by_image_and_id" and
type(action_data.data["object.process.fullpath"]) == "string" and
type(action_data.data["object.process.id"]) == "number") or
(action_name == "pt_kill_object_process_tree_by_file_path" and
type(action_data.data["object.fullpath"]) == "string") or
(action_name == "pt_kill_object_process_tree_by_image" and
type(action_data.data["object.process.fullpath"]) == "string") or
(action_name == "pt_kill_object_process_tree_by_name" and
type(action_data.data["object.process.name"]) == "string") or
(action_name == "pt_kill_object_process_tree_by_name_and_id" and
type(action_data.data["object.process.name"]) == "string" and
type(action_data.data["object.process.id"]) == "number") or
(action_name == "pt_kill_object_process_tree_by_image_and_id" and
type(action_data.data["object.process.fullpath"]) == "string" and
type(action_data.data["object.process.id"]) == "number") or
(action_name == "pt_kill_subject_process_by_image" and
type(action_data.data["subject.process.fullpath"]) == "string") or
(action_name == "pt_kill_subject_process_by_name" and
type(action_data.data["subject.process.name"]) == "string") or
(action_name == "pt_kill_subject_process_by_name_and_id" and
type(action_data.data["subject.process.name"]) == "string" and
type(action_data.data["subject.process.id"]) == "number") or
(action_name == "pt_kill_subject_process_by_image_and_id" and
type(action_data.data["subject.process.fullpath"]) == "string" and
type(action_data.data["subject.process.id"]) == "number") or
(action_name == "pt_kill_subject_process_tree_by_image" and
type(action_data.data["subject.process.fullpath"]) == "string") or
(action_name == "pt_kill_subject_process_tree_by_name" and
type(action_data.data["subject.process.name"]) == "string") or
(action_name == "pt_kill_subject_process_tree_by_name_and_id" and
type(action_data.data["subject.process.name"]) == "string" and
type(action_data.data["subject.process.id"]) == "number") or
(action_name == "pt_kill_subject_process_tree_by_image_and_id" and
type(action_data.data["subject.process.fullpath"]) == "string" and
type(action_data.data["subject.process.id"]) == "number")
for _, value in ipairs(actions[action_name]) do
local data_name, data_type = unpack(value)
if type(action_data.data[data_name]) ~= data_type then
return false
end
end
return true
end
local function exec_action(action_name, action_data)
@@ -328,25 +325,24 @@ end
if ffi.os == "Windows" then
--[[ Windows handlers ]]
function handlers.windows.kill_proc_common(action_name, action_data, object_type, proc_handle)
local exitCode = ffi.new("DWORD[1]", 0)
if lk32.TerminateProcess(proc_handle, 0) == 1 then
if lk32.TerminateProcess(proc_handle, 0) ~= 0 then
lk32.WaitForSingleObject(proc_handle, wait_kill_timeout)
action_data.data.result = true
push_event("pt_" .. object_type .. "_process_killed_successful", action_name, action_data)
return action_data.data
-- we can fail if the process is already terminating, so check for it
else
-- https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess
if lk32.GetExitCodeProcess(proc_handle, exitCode) == 1 then
__log.debugf("exit code from requested kill process: %d", tonumber(exitCode[0]))
-- 259 - STILL_ALIVE
if tonumber(exitCode[0]) ~= 259 then
action_data.data.result = true
action_data.data.reason = "already terminating"
push_event("pt_" .. object_type .. "_process_killed_successful", action_name, action_data)
return action_data.data
end
elseif lk32.GetExitCodeProcess(proc_handle, exitCode) ~= 0 then
__log.debugf("exit code from requested kill process: %d", tonumber(exitCode[0]))
if tonumber(exitCode[0]) ~= lk32.STILL_ALIVE then
action_data.data.result = true
action_data.data.reason = "already terminating"
push_event("pt_" .. object_type .. "_process_killed_successful", action_name, action_data)
return action_data.data
end
end
@@ -362,64 +358,57 @@ if ffi.os == "Windows" then
name = name:lower()
__log.debug("kill_process_by_name", name, kill_children)
local full_process_info = dyn_handlers.collect_process_info(object_type)
-- expect that we do not fail this one
-- check proc name to ensure we really need to open process
local s_proc_name = luapath.file(name)
s_proc_name = s_proc_name:lower()
local proc_found = false
local action_data_bak = glue.update({}, action_data.data)
action_data_bak.result = true
for i = 1, #full_process_info do
local proc_id = full_process_info[i][object_type .. ".process.id"]
local proc_name = full_process_info[i][object_type .. ".process.name"]
proc_name = proc_name:lower()
-- check proc name to ensure we really need to open process
local s_proc_name = luapath.file(name)
s_proc_name = s_proc_name:lower()
if s_proc_name ~= proc_name then
goto continue
process_api.for_each_process(function(proc_info)
if (proc_info.name ~= s_proc_name) then
return false
end
local proc_path, err = handlers.windows.get_process_path(proc_id)
local proc_path_l = proc_path:lower()
local proc_path, err = process_api.get_process_path(proc_info.pid)
local proc_path_lower = proc_path:lower()
if err == nil then
if proc_path == "" and glue.indexof(proc_name, system_process_excludes_windows) then
if proc_path == "" and glue.indexof(proc_info.name, system_process_excludes_windows) then
proc_found = true
action_data.data = update_action_data(action_data, object_type, proc_id, proc_path)
action_data.data = update_action_data(action_data, object_type, proc_info.pid, proc_path)
push_event("pt_" .. object_type .. "_process_skipped", action_name, action_data)
goto continue
elseif proc_path == "" or (proc_path_l ~= name and luapath.file(proc_path_l) ~= name) then
goto continue
return false
elseif proc_path == "" or (proc_path_lower ~= name and luapath.file(proc_path_lower) ~= name) then
return false
end
else
goto continue
return false
end
proc_found = true
action_data.data = update_action_data(action_data, object_type, proc_id, proc_path)
action_data.data = update_action_data(action_data, object_type, proc_info.pid, proc_path)
if is_whitelisted(proc_path_l, proc_id, false) then
if is_whitelisted(proc_path_lower, proc_info.pid, false) then
push_event("pt_" .. object_type .. "_process_skipped", action_name, action_data)
goto continue
return false
end
if kill_children then
kill_proc_children(action_name, object_type, proc_id)
kill_proc_children(action_name, object_type, proc_info.pid)
end
local proc_handle
proc_handle, action_data.data = handlers.windows.get_termination_handle(action_name, action_data, object_type,
proc_id)
proc_handle, action_data.data = handlers.windows.get_termination_handle(
action_name, action_data, object_type, proc_info.pid)
if proc_handle == nil then
action_data_bak.result = false
goto continue
return false
end
action_data.data = handlers.windows.kill_proc_common(action_name, action_data, object_type, proc_handle)
action_data_bak.result = action_data.data.result
lk32.CloseHandle(proc_handle)
::continue::
end
end)
action_data.data = action_data_bak
action_data.data.reason = not action_data.data.result and "failed_to_kill_a_part_of_processes" or nil
@@ -452,7 +441,7 @@ if ffi.os == "Windows" then
__log.debug("kill_process_by_name_and_id", dproc_name, dproc_id, kill_children, ignore_whitelist)
local proc_handle
local proc_path, err = handlers.windows.get_process_path(dproc_id)
local proc_path, err = process_api.get_process_path(dproc_id)
proc_path = proc_path or ""
local proc_path_l = proc_path:lower()
local proc_name_l = luapath.file(proc_path_l)
@@ -492,51 +481,8 @@ if ffi.os == "Windows" then
return action_data.data.result
end
-- take snap, get required fields from it
-- name - *.process.name
-- proc_id - *.process.id
-- ppid - *.process.parent.id
function handlers.windows.collect_process_info(object_type)
local full_process_info = {}
local proc_entry = ffi.new("PROCESSENTRY32[1]")
proc_entry[0].dwSize = ffi.sizeof("PROCESSENTRY32")
local snap_handle = lk32.CreateToolhelp32Snapshot(0x00000002, 0)
if (lk32.Process32First(snap_handle, proc_entry[0]) == 1) then
while (lk32.Process32Next(snap_handle, proc_entry[0]) == 1) do
table.insert(full_process_info, {
[object_type .. ".process.id"] = tonumber(proc_entry[0].th32ProcessID),
[object_type .. ".process.name"] = ffi.string(proc_entry[0].szExeFile),
[object_type .. ".process.parent.id"] = tonumber(proc_entry[0].th32ParentProcessID)
})
end
else
__log.error("failed to get info from snapshot")
end
if snap_handle ~= ffi.NULL then
lk32.CloseHandle(snap_handle)
end
return full_process_info
end
function handlers.windows.get_last_error()
local err = lk32.GetLastError()
__log.debugf("winapi last err: %d", tonumber(err))
return err
end
function handlers.windows.get_process_handle(proc_id)
local handle = lk32.OpenProcess(bit.bor(lk32.PROCESS_QUERY_LIMITED_INFORMATION, lk32.PROCESS_TERMINATE,
lk32.PROCESS_VM_READ, 0x00100000 -- lk32.SYNCHRONIZE
), false, proc_id)
if handle == ffi.NULL then
return nil, handlers.windows.get_last_error()
end
return handle, nil
end
function handlers.windows.get_termination_handle(action_name, action_data, object_type, proc_id)
local proc_handle, err = handlers.windows.get_process_handle(proc_id)
local proc_handle, err = process_api.get_process_handle(proc_id)
if proc_handle == nil then
-- access denied
if err == 5 then
@@ -554,34 +500,10 @@ if ffi.os == "Windows" then
return proc_handle, action_data.data
end
-- by using less priveleged handle we can get path for any process
-- GetModuleFileNameExA didn't work on w7x64
function handlers.windows.get_process_path(proc_id)
local proc_handle, err = lk32.OpenProcess(lk32.PROCESS_QUERY_LIMITED_INFORMATION, false, proc_id)
if proc_handle == nil then
return "", err
end
local max_path = lk32.MAX_PATH
local filename = ffi.new("char[?]", max_path)
local size = ffi.new("DWORD[1]", 2048)
if lk32.QueryFullProcessImageNameA(proc_handle, 0, filename, size) == 1 then
lk32.CloseHandle(proc_handle)
return ffi.string(filename, size[0]), nil
end
lk32.CloseHandle(proc_handle)
__log.errorf("failed to get process path for proc_id '%d'", proc_id)
return "", nil
end
function handlers.windows.update_agent_info()
local aid, apath
aid = tonumber(lk32.GetCurrentProcessId())
apath = handlers.windows.get_process_path(aid):lower()
return aid, apath
end
else
--[[ Linux handlers ]]
function handlers.linux.kill_proc_common(action_name, action_data, object_type, proc_id)
assert(type(proc_id) == "number", "input process id has invalid type")
-- If proc_id is positive, then signal sig is sent to the process with
@@ -593,35 +515,89 @@ else
-- calling process has permission to send signals, except for
-- process 1 (init), but see below.
local err
if proc_id <= 0 then
action_data.data.result = false
action_data.data.reason = "Invalid PID specified"
push_event("pt_" .. object_type .. "process_not_found", action_name, action_data)
return action_data, nil
return action_data
end
__log.info("call linux.kill_proc_common", action_name, proc_id)
if ffi.C.kill(proc_id, 9) == 0 then
action_data.data.result = true
push_event("pt_" .. object_type .. "_process_killed_successful", action_name, action_data)
return action_data.data, nil
else
-- #define EPERM 1 /* Operation not permitted */
-- #define ESRCH 3 /* No such process */
err = ffi.errno()
if err == 1 then
action_data.data.result = false
action_data.data.reason = "Access denied"
push_event("pt_" .. object_type .. "_process_killed_failed", action_name, action_data)
elseif err == 3 then
action_data.data.result = false
action_data.data.reason = "Process not found"
push_event("pt_process_not_found", action_name, action_data)
end
return action_data.data
end
local EPERM = 1 -- Operation not permitted
local ESRCH = 3 -- No such process
action_data.data.result = false
local err = ffi.errno()
if err == ESRCH then
action_data.data.reason = "Process not found"
push_event("pt_process_not_found", action_name, action_data)
return action_data.data
end
action_data.data.reason = err == EPERM and "Access denied" or ("errno: %d"):format(err)
push_event("pt_" .. object_type .. "_process_killed_failed", action_name, action_data)
return action_data.data, err
end
function handlers.linux.kill_process_by_name(action_name, action_data, object_type, name, kill_children, ignore_whitelist)
assert(type(name) == "string", "input process name or path has invalid type")
assert(type(kill_children) == "boolean", "kill_children flag has invalid type")
__log.debug("kill_process_by_name", name, kill_children)
local proc_found = false
local action_data_bak = glue.update({}, action_data.data)
action_data_bak.result = true
process_api.for_each_process(function(proc_info)
clear_proc_data(action_data, object_type)
-- some processes on osx might start with dash
-- current way of getting process info does not guarantee getting full process name
-- instead it gets argv[0] of running process
if ffi.os == "OSX" then
if proc_info.path == "" or ( not glue.ends(proc_info.name, name)) then
return false
end
else
if proc_info.path == "" or (proc_info.path ~= name and proc_info.name ~= name) then
return false
end
end
proc_found = true
action_data.data = update_action_data(action_data, object_type, proc_info.pid, proc_info.path)
if is_whitelisted(proc_info.path, proc_info.pid, ignore_whitelist) then
action_data.data.result = true
push_event("pt_" .. object_type .. "_process_skipped", action_name, action_data)
return false
end
if kill_children then
kill_proc_children(action_name, object_type, proc_info.pid)
end
local err
action_data.data, err = dyn_handlers.kill_proc_common(action_name, action_data, object_type, proc_info.pid)
if err then
__log.debugf("process killed -> '%d' with error -> '%s'", proc_info.pid, err)
else
__log.debugf("process killed -> '%d'", proc_info.pid)
end
end)
if not proc_found then
action_data.data.result = false
action_data.data.reason = "Process not found"
push_event("pt_process_not_found", action_name, action_data)
end
return action_data.data.result
end
function handlers.linux.kill_process_by_name_and_id(action_name, action_data, object_type, kill_children, ignore_whitelist)
assert(type(kill_children) == "boolean", "kill_children flag has invalid type")
assert(type(ignore_whitelist) == "boolean", "ignore_whitelist flag has invalid type")
@@ -640,7 +616,7 @@ else
end
__log.debug("linux.kill_process_by_name_and_id", dproc_name, dproc_id, kill_children, ignore_whitelist)
local proc_path, err = dyn_handlers.get_process_path(dproc_id)
local proc_path, err = process_api.get_process_path(dproc_id)
__log.debugf("proc info -> '%s' error -> '%s'", proc_path, err)
if not err and proc_path ~= "" and proc_path ~= nil and (not dproc_path or dproc_path == proc_path) then
action_data.data = update_action_data(action_data, object_type, dproc_id, proc_path)
@@ -675,192 +651,18 @@ else
return action_data.data.result
end
function handlers.linux.kill_process_by_name(action_name, action_data, object_type, name, kill_children, ignore_whitelist)
assert(type(name) == "string", "input process name or path has invalid type")
assert(type(kill_children) == "boolean", "kill_children flag has invalid type")
__log.debug("kill_process_by_name", name, kill_children)
--[[ OSX handlers ]]
local proc_found = false
local action_data_bak = glue.update({}, action_data.data)
action_data_bak.result = true
local full_process_info = dyn_handlers.collect_process_info(object_type)
local proc_name, proc_id, proc_path, err
for _, proc_info in ipairs(full_process_info) do
clear_proc_data(action_data, object_type)
proc_name = proc_info[object_type .. ".process.name"]
proc_id = proc_info[object_type .. ".process.id"]
proc_path = proc_info[object_type .. ".process.fullpath"]
-- some processes on osx might start with dash
-- current way of getting process info does not guarantee getting full process name
-- instead it gets argv[0] of running process
if ffi.os == "OSX" then
if proc_path == "" or ( not glue.ends(proc_name, name)) then
goto continue
end
else
if proc_path == "" or (proc_path ~= name and proc_name ~= name) then
goto continue
end
end
proc_found = true
action_data.data = update_action_data(action_data, object_type, proc_id, proc_path)
if is_whitelisted(proc_path, proc_id, ignore_whitelist) then
action_data.data.result = true
push_event("pt_" .. object_type .. "_process_skipped", action_name, action_data)
goto continue
end
if kill_children then
kill_proc_children(action_name, object_type, proc_id)
end
action_data.data, err = dyn_handlers.kill_proc_common(action_name, action_data, object_type, proc_id)
__log.debugf("proc killed -> '%d' error -> '%s'", proc_id, err)
::continue::
end
if not proc_found then
action_data.data.result = false
action_data.data.reason = "Process not found"
push_event("pt_process_not_found", action_name, action_data)
end
return action_data.data.result
end
function handlers.linux.get_process_path(proc_id)
local attrs = lfs.symlinkattributes(string.format("/proc/%s/exe", proc_id))
if type(attrs) ~= "table" then
return "", "not found"
elseif attrs["mode"] ~= "link" then
return "", "invalid process id"
elseif type(attrs["target"]) ~= "string" then
return "", "permission deny"
end
return attrs["target"]
end
-- might be a number or "self"
function handlers.linux.get_process_info(proc_id_str)
assert(type(proc_id_str) == "string")
local file = io.open("/proc/" .. proc_id_str .. "/stat", "r")
if file ~= nil then
local info = file:read()
local _pid, _ppid = info:match("(%S+) %S+ %S+ (%S+)")
local proc_id = tonumber(_pid)
local ppid = tonumber(_ppid)
file:close()
if proc_id ~= nil and ppid ~= nil then
return proc_id, ppid, false
else
return nil, nil, true
end
end
return nil, nil, true
end
function handlers.linux.collect_process_info(object_type)
local process_list = {}
local imagepath, err
for file in lfs.dir("/proc") do
if file == "." or file == ".." or tonumber(file) == nil then
goto continue
end
local attrs = lfs.attributes(string.format("/proc/%s", file))
if type(attrs) ~= "table" or attrs["mode"] ~= "directory" then
goto continue
end
imagepath, err = handlers.linux.get_process_path(file)
if not err then
local proc_id, ppid
local name = luapath.file(imagepath)
proc_id, ppid, err = handlers.linux.get_process_info(file)
if not err then
if proc_id == nil or ppid == nil then
__log.info("Invalid PID: -> " .. proc_id .. " expected ->" .. file)
end
table.insert(process_list, {
[object_type .. ".process.id"] = proc_id,
[object_type .. ".process.parent.id"] = ppid,
[object_type .. ".process.fullpath"] = imagepath,
[object_type .. ".process.name"] = name
})
end
end
::continue::
end
return process_list
end
function handlers.linux.update_agent_info()
local aid, apath
aid = handlers.linux.get_process_info("self")
apath = handlers.linux.get_process_path("self")
return aid, apath
end
function handlers.osx.update_agent_info()
local aid, apath
aid = ffi.C.getpid()
apath = handlers.osx.get_process_path(aid)
return aid, apath
end
function handlers.osx.get_process_path(proc_id)
assert(type(proc_id) == "number", "PID should a number")
local cmd = "/bin/ps o comm=\"\" " .. tostring(proc_id) -- comm for osx, command for linux
local cmd_handle = assert(io.popen(cmd, "r"), "failed to call io.popen")
local imagepath = assert(cmd_handle:read("*all"), "failed to read from pipe")
imagepath = string.gsub(imagepath, '^%s*(.-)%s*$', '%1')
__log.debugf("handlers.osx.get_process_path for '%d' -> '%s'", proc_id, imagepath)
cmd_handle:close()
if imagepath ~= nil and imagepath ~= "" then
return imagepath, nil
else
return nil, "Not found"
end
end
function handlers.osx.collect_process_info(object_type)
local process_list = {}
-- TODO move this to sysctl syscall to retrieve the process table.
local cmd = "/bin/ps axo pid=\"\",ppid=\"\",comm=\"\"" -- comm for osx, command for linux
local cmd_handle = assert(io.popen(cmd, "r"), "failed to call io.popen")
local cmd_res = assert(cmd_handle:read("*all"), "failed to read from pipe")
cmd_handle:close()
for str in string.gmatch(cmd_res, "([^"..'\n'.."]+)") do
local _pid, _ppid, imagepath = str:match("%s+(%S+)%s+(%S+) ([^.]+)")
imagepath = string.gsub(imagepath, '^%s*(.-)%s*$', '%1')
__log.debugf("handlers.osx.collect_process_info PID -> '%s' PPID -> '%s' IMAGE -> '%s", _pid, _ppid , imagepath)
if _pid ~= nil and _ppid ~= nil and imagepath ~= nil then
table.insert(process_list, {
[object_type .. ".process.id"] = tonumber(_pid),
[object_type .. ".process.parent.id"] = tonumber(_ppid),
[object_type .. ".process.fullpath"] = imagepath,
[object_type .. ".process.name"] = luapath.file(imagepath),
})
end
end
return process_list
end
function handlers.osx.kill_process_by_name_and_id(action_name, action_data, object_type, kill_children, ignore_whitelist)
return handlers.linux.kill_process_by_name_and_id(action_name, action_data, object_type, kill_children, ignore_whitelist)
function handlers.osx.kill_proc_common(action_name, action_data, object_type, proc_id)
return handlers.linux.kill_proc_common(action_name, action_data, object_type, proc_id)
end
function handlers.osx.kill_process_by_name(action_name, action_data, object_type, name, kill_children, ignore_whitelist)
return handlers.linux.kill_process_by_name(action_name, action_data, object_type, name, kill_children, ignore_whitelist)
end
function handlers.osx.kill_proc_common(action_name, action_data, object_type, proc_id)
return handlers.linux.kill_proc_common(action_name, action_data, object_type, proc_id)
function handlers.osx.kill_process_by_name_and_id(action_name, action_data, object_type, kill_children, ignore_whitelist)
return handlers.linux.kill_process_by_name_and_id(action_name, action_data, object_type, kill_children, ignore_whitelist)
end
end
@@ -0,0 +1,296 @@
require("engine")
local bit = require("bit")
local ffi = require("ffi")
local lfs = require("lfs")
local luapath = require("path")
local lk32
if ffi.os == "Windows" then
lk32 = require("waffi.windows.kernel32")
lk32.TH32CS_SNAPPROCESS = 0x00000002
lk32.SYNCHRONIZE = 0x00100000
else
ffi.cdef[[
typedef uint32_t pid_t;
int kill( pid_t proc_id, int sig );
pid_t getpid();
]]
end
-- API for different OS types
local api = {windows = {}, linux = {}, osx = {}}
-- API for current OS type
local process_api
local function run_callback_safe(callback, args)
local result, need_stop = pcall(callback, args)
if not result then
__log.error("callback failure")
return false
end
return need_stop
end
if ffi.os == "Windows" then
--[[
Call given callback for each process
Callback arg table: {
pid - process id
name - process name
parent_pid - process parent id
path - process image path
}
Callback return value:
bool - whether to stop iteration
--]]
function api.windows.for_each_process(callback)
if not callback then
__log.error("no callback provided")
return
end
local proc_entry = ffi.new("PROCESSENTRY32[1]")
proc_entry[0].dwSize = ffi.sizeof("PROCESSENTRY32")
local snap_handle = lk32.CreateToolhelp32Snapshot(lk32.TH32CS_SNAPPROCESS, 0)
if (lk32.Process32First(snap_handle, proc_entry[0]) == 1) then
while (lk32.Process32Next(snap_handle, proc_entry[0]) == 1) do
local pid = tonumber(proc_entry[0].th32ProcessID)
local args = {
pid = pid,
name = ffi.string(proc_entry[0].szExeFile),
parent_pid = tonumber(proc_entry[0].th32ParentProcessID),
path = api.windows.get_process_path(pid),
}
if (run_callback_safe(callback, args)) then
break
end
end
else
__log.error("failed to get info from snapshot")
end
if snap_handle ~= ffi.NULL then
lk32.CloseHandle(snap_handle)
end
end
function api.windows.get_last_error()
local err = lk32.GetLastError()
__log.debugf("winapi last err: %d", tonumber(err))
return err
end
function api.windows.get_process_handle(pid)
local handle = lk32.OpenProcess(bit.bor(lk32.PROCESS_QUERY_LIMITED_INFORMATION, lk32.PROCESS_TERMINATE,
lk32.PROCESS_VM_READ, lk32.SYNCHRONIZE
), false, pid)
if handle == ffi.NULL then
return nil, api.windows.get_last_error()
end
return handle, nil
end
function api.windows.kill_process(pid)
local handle, error = api.windows.get_process_handle(pid)
if error then
return false
end
if lk32.TerminateProcess(handle, 0) == 0 then
return false
end
lk32.WaitForSingleObject(handle, 0)
return true
end
-- by using less priveleged handle we can get path for any process
-- GetModuleFileNameExA didn't work on w7x64
function api.windows.get_process_path(pid)
local proc_handle, err = lk32.OpenProcess(lk32.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
if proc_handle == nil then
return "", err
end
local max_path = lk32.MAX_PATH
local filename = ffi.new("char[?]", max_path)
local size = ffi.new("DWORD[1]", 2048)
if lk32.QueryFullProcessImageNameA(proc_handle, 0, filename, size) ~= 1 then
lk32.CloseHandle(proc_handle)
__log.errorf("failed to get process path for pid '%d'", pid)
return "", "failed to get process path"
end
lk32.CloseHandle(proc_handle)
local path = ffi.string(filename, size[0])
return path, nil
end
function api.windows.update_agent_info()
local aid, apath
aid = tonumber(lk32.GetCurrentProcessId())
apath = api.windows.get_process_path(aid):lower()
return aid, apath
end
else
function api.linux.get_process_path(pid)
local attrs = lfs.symlinkattributes(string.format("/proc/%s/exe", pid))
if type(attrs) ~= "table" then
return "", "not found"
elseif attrs["mode"] ~= "link" then
return "", "invalid process id"
elseif type(attrs["target"]) ~= "string" then
return "", "permission deny"
end
return attrs["target"]
end
function api.linux.kill_process(pid)
return ffi.C.kill(pid, 9) == 0
end
-- might be a number or "self"
local function get_process_info_linux(proc_id_str)
assert(type(proc_id_str) == "string")
local file = io.open("/proc/" .. proc_id_str .. "/stat", "r")
if file ~= nil then
local info = file:read()
local _pid, _ppid = info:match("(%S+) %S+ %S+ (%S+)")
local pid = tonumber(_pid)
local parent_pid = tonumber(_ppid)
file:close()
if pid ~= nil and parent_pid ~= nil then
return pid, parent_pid, false
else
return nil, nil, true
end
end
return nil, nil, true
end
function api.linux.for_each_process(callback)
if not callback then
error("no callback provided")
end
local imagepath, err
for file in lfs.dir("/proc") do
if file == "." or file == ".." or tonumber(file) == nil then
goto continue
end
local attrs = lfs.attributes(string.format("/proc/%s", file))
if type(attrs) ~= "table" or attrs["mode"] ~= "directory" then
-- __log.debugf("Wrong attributes for '%s'", file)
goto continue
end
imagepath, err = api.linux.get_process_path(file)
if err then
-- __log.debugf("Failed to get process path for '%s': %s", file, err)
goto continue
end
local pid, parent_pid
local name = luapath.file(imagepath)
pid, parent_pid, err = get_process_info_linux(file)
if err then
-- __log.debugf("Failed to get process info for '%s': %s", file, err)
goto continue
end
if pid == nil or parent_pid == nil then
__log.info("Invalid PID: -> " .. pid .. " expected ->" .. file)
end
local args = {
pid = tonumber(pid),
name = name,
parent_pid = tonumber(parent_pid),
path = imagepath,
}
if (run_callback_safe(callback, args)) then
return
end
::continue::
end
end
function api.linux.update_agent_info()
local aid, apath
aid = get_process_info_linux("self")
apath = api.linux.get_process_path("self")
return aid, apath
end
function api.osx.get_process_path(pid)
assert(type(pid) == "number", "PID should a number")
local cmd = "/bin/ps o comm=\"\" " .. tostring(pid) -- comm for osx, command for linux
local cmd_handle = assert(io.popen(cmd, "r"), "failed to call io.popen")
local imagepath = assert(cmd_handle:read("*all"), "failed to read from pipe")
imagepath = string.gsub(imagepath, '^%s*(.-)%s*$', '%1')
__log.debugf("handlers.osx.get_process_path for '%d' -> '%s'", pid, imagepath)
cmd_handle:close()
if imagepath ~= nil and imagepath ~= "" then
return imagepath, nil
else
return nil, "Not found"
end
end
function api.osx.kill_process(pid)
return api.linux.kill_process(pid)
end
function api.osx.for_each_process(callback)
-- TODO move this to sysctl syscall to retrieve the process table.
local cmd = "/bin/ps axo pid=\"\",ppid=\"\",comm=\"\"" -- comm for osx, command for linux
local cmd_handle = assert(io.popen(cmd, "r"), "failed to call io.popen")
local cmd_res = assert(cmd_handle:read("*all"), "failed to read from pipe")
cmd_handle:close()
for str in string.gmatch(cmd_res, "([^"..'\n'.."]+)") do
local pid, parent_pid, imagepath = str:match("%s*(%S+)%s+(%S+) ([^.]+)")
if (imagepath ~= nil) then
imagepath = imagepath:gsub('^%s*(.-)%s*$', '%1')
end
__log.debugf("api.osx.for_each_process PID -> '%s' PPID -> '%s' IMAGE -> '%s", pid, parent_pid , imagepath)
if pid ~= nil and parent_pid ~= nil and imagepath ~= nil then
local args = {
pid = tonumber(pid),
name = luapath.file(imagepath),
parent_pid = tonumber(parent_pid),
path = imagepath,
}
if (run_callback_safe(callback, args)) then
return
end
end
end
end
function api.osx.update_agent_info()
local aid, apath
aid = ffi.C.getpid()
apath = api.osx.get_process_path(aid)
return aid, apath
end
end
if ffi.os == "Windows" then
process_api = api.windows
elseif ffi.os == "Linux" then
process_api = api.linux
elseif ffi.os == "OSX" then
process_api = api.osx
else
__log.error("unsupported OS")
return
end
return process_api
+93 -108
View File
@@ -1,122 +1,107 @@
<template>
<div>
<el-tabs tab-position="left" v-model="leftTab">
<el-tab-pane
name="api"
:label="locale[$i18n.locale]['api']"
class="layout-fill overflow-hidden"
v-if="viewMode === 'agent'"
>
<div class="layout-margin-xl limit-length">
<el-input :placeholder="locale[$i18n.locale]['inputPlaceholder']" v-model="eventString">
<el-button
slot="append"
icon="el-icon-s-promotion"
class="layout-row-none"
@click="submitReqToSend"
>{{ locale[$i18n.locale]['buttonSendEventReq'] }}
</el-button>
</el-input>
<div>
<div class="layout-margin-bottom-xl">
<el-input :placeholder="locale[$i18n.locale]['inputPlaceholder']" v-model="eventString">
<el-button
slot="append"
icon="el-icon-s-promotion"
class="flex-none"
@click="submitReqToSend"
>{{ locale[$i18n.locale]['buttonSendEventReq'] }}
</el-button>
</el-input>
</div>
<div class="layout-fill layout-row layout-row-column layout-row-between scrollable">
<ul>
<li :key="line" v-for="line in lines">{{ line }}</li>
</ul>
<div class="layout-column layout-align-space-between scrollable">
<ul>
<li :key="line" v-for="line in lines">{{ line }}</li>
</ul>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
const name = "responder";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
data: () => ({
leftTab: undefined,
connection: {},
lines: [],
eventString: "",
locale: {
ru: {
api: "Отправка событий",
inputPlaceholder: "Событие в формате {'name': 'event_name', 'data': {'event_key': 'value'}}",
buttonSendEventReq: "Отправить",
connected: "— подключение к серверу установлено",
connError: "Не удалось подключиться к серверу",
recvError: "Не удалось выполнить операцию",
parseError: "Данные о событии введены некорректно"
},
en: {
api: "Event sender",
inputPlaceholder: "Event in format {'name': 'event_name', 'data': {'event_key': 'value'}}",
buttonSendEventReq: "Send",
connected: "— connection to the server established",
connError: "Failed to connect to the server",
recvError: "Unable to perform the operation",
parseError: "Event data entered incorrectly"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['connError']);
console.log(error);
},
);
}
},
mounted() {
this.leftTab = this.viewMode === 'agent' ? 'api' : undefined;
},
methods: {
recvData(msg) {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
this.lines.push(
`${date_ms} RECV DATA: ${new TextDecoder(
"utf-8"
).decode(msg.content.data)}`
);
},
parseJsonString(str) {
if (!(str && typeof(str) === "string")){
return [false];
}
try {
const event = JSON.parse(str);
if (typeof(event) !== "object" || typeof(event.name) !== "string" || typeof(event.data) !== "object") {
return [false];
name,
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
connection: {},
lines: [],
eventString: "",
locale: {
ru: {
inputPlaceholder: "Событие в формате {'name': 'event_name', 'data': {'event_key': 'value'}}",
buttonSendEventReq: "Отправить",
connected: "— подключение к серверу установлено",
connError: "Не удалось подключиться к серверу",
recvError: "Не удалось выполнить операцию",
parseError: "Данные о событии введены некорректно"
},
en: {
inputPlaceholder: "Event in format {'name': 'event_name', 'data': {'event_key': 'value'}}",
buttonSendEventReq: "Send",
connected: "— connection to the server established",
connError: "Failed to connect to the server",
recvError: "Unable to perform the operation",
parseError: "Event data entered incorrectly"
}
}
}),
created() {
if (this.viewMode === 'agent') {
this.protoAPI.connect().then(
connection => {
const date = new Date().toLocaleTimeString();
this.connection = connection;
this.connection.subscribe(this.recvData, "data");
this.$root.NotificationsService.success(`${date} ${this.locale[this.$i18n.locale]['connected']}`);
},
error => {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['connError']);
console.log(error);
},
);
}
return [true, event];
} catch (e) {
return [false];
}
},
submitReqToSend() {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
const event = this.parseJsonString(this.eventString);
if (!event[0]) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['parseError']);
return;
}
let data = JSON.stringify({ type: "events", message: [event[1]] });
this.lines.push(
`${date_ms} SEND DATA: ${data}`
);
this.connection.sendData(data);
methods: {
recvData(msg) {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
this.lines.push(
`${date_ms} RECV DATA: ${new TextDecoder(
"utf-8"
).decode(msg.content.data)}`
);
},
parseJsonString(str) {
if (!(str && typeof (str) === "string")) {
return [false];
}
try {
const event = JSON.parse(str);
if (typeof (event) !== "object" || typeof (event.name) !== "string" || typeof (event.data) !== "object") {
return [false];
}
return [true, event];
} catch (e) {
return [false];
}
},
submitReqToSend() {
const date = new Date();
const date_ms = date.toLocaleTimeString() + `.${date.getMilliseconds()}`;
const event = this.parseJsonString(this.eventString);
if (!event[0]) {
this.$root.NotificationsService.error(this.locale[this.$i18n.locale]['parseError']);
return;
}
let data = JSON.stringify({type: "events", message: [event[1]]});
this.lines.push(
`${date_ms} SEND DATA: ${data}`
);
this.connection.sendData(data);
}
}
}
};
</script>
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
+5 -5
View File
@@ -27,7 +27,7 @@
<el-button
icon="el-icon-plus"
type="primary"
class="layout-margin-left-s"
class="layout-margin-left-m"
@click="goToNewCheckView()">
{{ $t('BrowserModule.YaraManagement.ButtonText.NewCheck') }}
</el-button>
@@ -190,11 +190,11 @@
</el-tab-pane>
<el-tab-pane v-if="viewMode === 'agent'" name="rules" :label="$t('BrowserModule.YaraManagement.TabTitle.Rules')">
<!-- RULES LIST -->
<div class="layout-fill layout-row layout-row-row layout-row-stretch">
<div class="layout-fill layout-row layout-align-start-stretch">
<component
v-if="connection"
ref="rulesGrid"
class="layout-row-auto layout-fill"
class="flex-auto layout-fill"
storage-key="yara-rules-grid"
:is="components['grid']"
:data="rules"
@@ -272,7 +272,7 @@
<div class="rules-view__title">
{{ selectedRule.rule_name }}
</div>
<div class="rules-view__hash layout-margin-bottom-s">
<div class="rules-view__hash layout-margin-bottom-m">
<div v-for="hash of (selectedRule.hash || '').split('|')">
{{ hash }}
</div>
@@ -499,7 +499,7 @@ const EN_LOCALE = {
module.exports = {
name,
props: [ "protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode", "helpers", "entity" ],
props: [ "protoAPI", "hash", "module", "api", "components", "viewMode", "helpers", "entity" ],
data: () => ({
leftTab: undefined,
connection: undefined,
@@ -1,13 +1,13 @@
<template>
<div class="layout-row layout-row-column">
<div class="layout-row layout-row-none layout-row-row layout-row-middle">
<div class="layout-column">
<div class="flex-none layout-row layout-align-start-center">
<el-button type="text" icon="el-icon-back" @click="onBack()"></el-button>
<el-divider direction="vertical"></el-divider>
<header class="layout-row layout-row-auto quick-check-scope__header">
<header class="flex-auto quick-check-scope__header">
{{ $t('BrowserModule.YaraManagement.Label.QuickCheckParams') }}
</header>
</div>
<div class="layout-row layout-row-auto layout-row-column quick-check-scope__content">
<div class="flex-auto layout-column quick-check-scope__content">
<header class="quick-check-scope__subheader">
{{ $t('BrowserModule.YaraManagement.Label.CheckScope') }}
</header>
+35 -52
View File
@@ -3,13 +3,13 @@
<!-- VIEW -->
<div
v-if="task && !canShowParams"
class="layout-fill layout-row layout-row-column overflow-hidden">
<div class="layout-row layout-row-none layout-row-row layout-row-middle view-check__header">
<div class="layout-row layout-row-auto layout-row-column">
<div class="layout-row layout-row-none layout-row-row layout-row-middle">
class="layout-fill layout-column overflow-hidden">
<div class="flex-none layout-row layout-align-start-center view-check__header">
<div class="flex-auto layout-column">
<div class="flex-none layout-row layout-align-start-center">
<el-button type="text" icon="el-icon-back" @click="onBack()"></el-button>
<el-divider direction="vertical"></el-divider>
<div class="layout-row layout-row-auto layout-row-column">
<div class="flex-auto layout-column">
<header class="view-check__title">
{{
$t('BrowserModule.YaraManagement.Label.CheckFiles', { dt: toLocalizedDateTime(task.time_start) })
@@ -28,7 +28,7 @@
@stop-check="stopCheck">
</component>
</div>
<div class="layout-row flew-none">
<div class="flex-none">
<el-tooltip :content="$t('BrowserModule.YaraManagement.ButtonTooltip.ShowParams')">
<el-button class="el-icon-info" @click="showCheckParams()"></el-button>
</el-tooltip>
@@ -43,7 +43,7 @@
</el-button>
</div>
</div>
<div class="layout-row layout-row-auto layout-row-column view-check__content">
<div class="flex-auto layout-column view-check__content">
<component
class="layout-fill"
:is="components['grid']"
@@ -129,13 +129,13 @@
</div>
<!-- CHECK PARAMS -->
<div v-if="task && canShowParams" class="layout-fill layout-row layout-row-column overflow-hidden">
<div class="layout-row layout-row-none layout-row-row layout-row-middle view-check__header">
<div class="layout-row layout-row-auto layout-row-column">
<div class="layout-row layout-row-none layout-row-row layout-row-middle">
<div v-if="task && canShowParams" class="layout-fill layout-column overflow-hidden">
<div class="flex-none layout-row layout-align-start-center view-check__header">
<div class="flex-auto layout-column">
<div class="flex-none layout-row layout-align-start-center">
<el-button type="text" icon="el-icon-back" @click="showView()"></el-button>
<el-divider direction="vertical"></el-divider>
<div class="layout-row layout-row-auto layout-row-column">
<div class="flex-auto layout-column">
<header class="view-check__title">
{{
$t('BrowserModule.YaraManagement.Label.CheckFiles', { dt: toLocalizedDateTime(task.time_start) })
@@ -154,18 +154,18 @@
@stop-check="stopCheck">
</component>
</div>
<div class="layout-row flew-none">
<div class="flex-none">
<el-tooltip :content="$t('BrowserModule.YaraManagement.ButtonTooltip.Copy')">
<el-button class="el-icon-copy-document" @click="doCopy()"></el-button>
</el-tooltip>
</div>
</div>
<div class="layout-row-none layout-row layout-row-auto layout-row-column view-check__content">
<div class="layout-row layout-row-row layout-row-top view-check__params">
<div class="flex-auto layout-column view-check__content">
<div class="layout-row layout-align-start-start view-check__params">
<div class="view-check__params-label">
{{ $t('BrowserModule.YaraManagement.ColumnTitle.Type') }}
</div>
<div class="layout-row-auto">
<div class="flex-auto">
<span v-if="[TaskType.CustomFs, TaskType.FastFs, TaskType.FullFs].includes(task.task_type)">
{{ $t('BrowserModule.YaraManagement.Text.File') }}
</span>
@@ -176,13 +176,13 @@
</div>
</div>
<div class="layout-row-none layout-row layout-row-row layout-row-top view-check__params">
<div class="flex-none layout-row layout-align-start-start view-check__params">
<div
class="view-check__params-label"
:class="{'view-check__params-label_with-button': [TaskType.FastFs, TaskType.FastProc].includes(task.task_type)}">
{{ $t('BrowserModule.YaraManagement.ColumnTitle.Path') }}
</div>
<div class="layout-row-auto">
<div class="flex-auto">
<div v-if="[TaskType.CustomProc, TaskType.CustomFs].includes(task.task_type)">
{{ task.task_params.filepath || task.task_params.proc_image }}
</div>
@@ -201,32 +201,32 @@
</div>
<div v-if="task.task_type === TaskType.FastFs">
<header class="view-check__params-label layout-margin-bottom-s">
<header class="view-check__params-label layout-margin-bottom-m">
{{ $t('BrowserModule.YaraManagement.Label.CheckScopeFs') }}
</header>
<div v-for="item of fastScanFsItems"
class="layout-margin-bottom-s">
class="layout-margin-bottom-m">
{{ item.filepath }}
</div>
</div>
<div v-else-if="task.task_type === TaskType.FastProc">
<header class="view-check__params-label layout-margin-bottom-s">
<header class="view-check__params-label layout-margin-bottom-m">
{{ $t('BrowserModule.YaraManagement.Label.CheckScopeProc') }}
</header>
<div v-for="item of fastScanProcItems"
class="layout-margin-bottom-s">
class="layout-margin-bottom-m">
{{ item.proc_image }}
</div>
</div>
<div
v-if="task.task_type === TaskType.FastFs && excludeFsItems.length > 0">
<header class="view-check__params-label layout-margin-bottom-s">
<header class="view-check__params-label layout-margin-bottom-m">
{{ $t('BrowserModule.YaraManagement.Label.CheckExcludedScope') }}
</header>
<div v-for="item of excludeFsItems"
class="layout-margin-bottom-s">
class="layout-margin-bottom-m">
{{ item.filepath }}
</div>
</div>
@@ -238,7 +238,7 @@
</div>
</div>
<div class="layout-row-auto layout-row layout-row-row layout-row-stretch view-check__params">
<div class="flex-auto layout-row layout-align-start-stretch view-check__params">
<div
class="view-check__params-label"
:class="{'view-check__params-label_with-button': !task.custom_rules}">
@@ -246,12 +246,12 @@
</div>
<div
v-if="task.custom_rules"
class="layout-row-auto">
class="flex-auto">
<div
class="layout-row layout-row-row layout-row-right layout-margin-bottom-s">
class="layout-row layout-align-end layout-margin-bottom-m">
<el-link
type="primary"
class="layout-margin-left-s"
class="layout-margin-left-m"
icon="el-icon-download"
:underline="false"
@click="doExport">
@@ -264,7 +264,7 @@
</div>
<div
v-if="!task.custom_rules"
class="layout-row-auto layout-row layout-row-column layout-row-stretch">
class="flex-auto layout-column layout-align-start-stretch">
<div v-if="!canShowPolicyRules">
{{ $t('BrowserModule.YaraManagement.Text.FromPolicyConfig') }}
<el-button type="text" @click="showPolicyRules()">
@@ -277,14 +277,14 @@
{{ $t('BrowserModule.YaraManagement.ButtonText.Hide') }}
</el-button>
<div>
<div class="view-check__params-label layout-margin-bottom-s">
<div class="view-check__params-label layout-margin-bottom-m">
{{ $t('BrowserModule.YaraManagement.Label.Classes') }}
</div>
<div class="layout-row layout-row-row layout-row-wrap layout-row-left">
<div class="layout-row layout-wrap layout-align-start">
<div
v-for="item of module.current_config.malware_class_items"
class="view-check__class-item layout-margin-bottom-s">
class="view-check__class-item layout-margin-bottom-m">
<el-checkbox
:value="item.enabled"
:disabled="true">
@@ -294,14 +294,14 @@
</div>
<div v-if="module.current_config.exclude_rules.length > 0">
<div class="view-check__params-label layout-margin-bottom-s">
<div class="view-check__params-label layout-margin-bottom-m">
{{ $t('BrowserModule.YaraManagement.Label.ExcludedRules') }}
</div>
<div class="layout-row layout-row-column">
<div class="layout-column">
<div
v-for="item of module.current_config.exclude_rules"
class="layout-margin-bottom-s">
class="layout-margin-bottom-m">
<div>{{ item.rule_name }}</div>
</div>
</div>
@@ -310,23 +310,6 @@
</div>
</div>
</div>
<!-- <div class="layout-row-auto layout-row layout-row-row layout-row-stretch view-check__params">-->
<!-- <div class="view-check__params-label">-->
<!-- {{ $t('BrowserModule.YaraManagement.Label.CheckOptions') }}-->
<!-- </div>-->
<!-- <div class="layout-row-auto layout-row layout-row-column layout-row-stretch">-->
<!-- <div-->
<!-- v-for="key of Object.keys(check.options)"-->
<!-- class="view-check__check-option layout-margin-bottom-s">-->
<!-- <el-checkbox-->
<!-- :value="check.options[key]"-->
<!-- :disabled="true">-->
<!-- {{ key }}-->
<!-- </el-checkbox>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</div>
</div>
</div>