mirror of
https://github.com/vxcontrol/soldr-modules.git
synced 2026-07-01 12:47:17 -04:00
Merge pull request #35 from vxcontrol/sync_modules
sync modules 20.12.22
This commit is contained in:
@@ -74,6 +74,7 @@ globals = {
|
||||
"__mock",
|
||||
|
||||
-- utils classes
|
||||
"CSystemInfo",
|
||||
"CFileReader",
|
||||
"CReader",
|
||||
|
||||
|
||||
+10
-10
@@ -37,8 +37,8 @@
|
||||
"reason",
|
||||
"version"
|
||||
],
|
||||
"last_module_update": "2022-12-08 00:00:00",
|
||||
"last_update": "2022-12-08 00:00:00"
|
||||
"last_module_update": "2022-12-20 00:00:00",
|
||||
"last_update": "2022-12-20 00:00:00"
|
||||
},
|
||||
{
|
||||
"group_id": "",
|
||||
@@ -181,8 +181,8 @@
|
||||
"subject.fullpath",
|
||||
"subject.process.fullpath"
|
||||
],
|
||||
"last_module_update": "2022-12-08 00:00:00",
|
||||
"last_update": "2022-12-08 00:00:00"
|
||||
"last_module_update": "2022-12-20 00:00:00",
|
||||
"last_update": "2022-12-20 00:00:00"
|
||||
},
|
||||
{
|
||||
"group_id": "",
|
||||
@@ -249,8 +249,8 @@
|
||||
"subject.process.id",
|
||||
"subject.process.name"
|
||||
],
|
||||
"last_module_update": "2022-12-08 00:00:00",
|
||||
"last_update": "2022-12-08 00:00:00"
|
||||
"last_module_update": "2022-12-20 00:00:00",
|
||||
"last_update": "2022-12-20 00:00:00"
|
||||
},
|
||||
{
|
||||
"group_id": "",
|
||||
@@ -419,8 +419,8 @@
|
||||
"subject.process.parent.id",
|
||||
"subject.process.path"
|
||||
],
|
||||
"last_module_update": "2022-12-08 12:52:12",
|
||||
"last_update": "2022-12-08 12:52:12"
|
||||
"last_module_update": "2022-12-20 00:00:00",
|
||||
"last_update": "2022-12-20 00:00:00"
|
||||
},
|
||||
{
|
||||
"group_id": "",
|
||||
@@ -511,7 +511,7 @@
|
||||
"fields": [
|
||||
"reason"
|
||||
],
|
||||
"last_module_update": "2022-12-08 00:00:00",
|
||||
"last_update": "2022-12-08 00:00:00"
|
||||
"last_module_update": "2022-12-20 00:00:00",
|
||||
"last_update": "2022-12-20 00:00:00"
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+126
-142
@@ -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-column layout-align-space-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", "api", "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>
|
||||
|
||||
@@ -5,9 +5,10 @@ local cjson = require("cjson.safe")
|
||||
local luapath = require("path")
|
||||
local process_api = require("process_api")
|
||||
local lk32
|
||||
local win_const = {}
|
||||
if ffi.os == "Windows" then
|
||||
lk32 = require("waffi.windows.kernel32")
|
||||
lk32.STILL_ALIVE = 259
|
||||
win_const.STILL_ALIVE = 259
|
||||
else
|
||||
ffi.cdef[[
|
||||
typedef uint32_t pid_t;
|
||||
@@ -285,10 +286,6 @@ local function exec_action(action_name, action_data)
|
||||
|
||||
action_data = set_action_data_fields(action_data, object_type)
|
||||
|
||||
if ffi.os == "OSX" and (glue.ends(action_name, "by_image") or glue.ends(action_name, "by_file_path")) then
|
||||
return set_osx_unsupported()
|
||||
end
|
||||
|
||||
if action_name == "pt_kill_object_process_by_file_path" then
|
||||
object_value = action_data.data[object_type .. ".fullpath"]
|
||||
dyn_handlers.kill_process_by_name(action_name, action_data, object_type, object_value, false)
|
||||
@@ -338,7 +335,7 @@ if ffi.os == "Windows" then
|
||||
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
|
||||
if tonumber(exitCode[0]) ~= win_const.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)
|
||||
@@ -560,13 +557,13 @@ else
|
||||
-- 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
|
||||
if proc_info.path == "" or ( not glue.ends(proc_info.name, name) and not glue.ends(proc_info.path, name)) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
if proc_info.path == "" or (proc_info.path ~= name and proc_info.name ~= name) then
|
||||
return false
|
||||
end
|
||||
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)
|
||||
|
||||
@@ -4,10 +4,11 @@ local ffi = require("ffi")
|
||||
local lfs = require("lfs")
|
||||
local luapath = require("path")
|
||||
local lk32
|
||||
local win_const = {}
|
||||
if ffi.os == "Windows" then
|
||||
lk32 = require("waffi.windows.kernel32")
|
||||
lk32.TH32CS_SNAPPROCESS = 0x00000002
|
||||
lk32.SYNCHRONIZE = 0x00100000
|
||||
win_const.TH32CS_SNAPPROCESS = 0x00000002
|
||||
win_const.SYNCHRONIZE = 0x00100000
|
||||
else
|
||||
ffi.cdef[[
|
||||
typedef uint32_t pid_t;
|
||||
@@ -53,7 +54,7 @@ if ffi.os == "Windows" then
|
||||
|
||||
local proc_entry = ffi.new("PROCESSENTRY32[1]")
|
||||
proc_entry[0].dwSize = ffi.sizeof("PROCESSENTRY32")
|
||||
local snap_handle = lk32.CreateToolhelp32Snapshot(lk32.TH32CS_SNAPPROCESS, 0)
|
||||
local snap_handle = lk32.CreateToolhelp32Snapshot(win_const.TH32CS_SNAPPROCESS, 0)
|
||||
|
||||
if (lk32.Process32First(snap_handle, proc_entry[0]) == 1) then
|
||||
while (lk32.Process32Next(snap_handle, proc_entry[0]) == 1) do
|
||||
@@ -85,7 +86,7 @@ if ffi.os == "Windows" then
|
||||
|
||||
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
|
||||
lk32.PROCESS_VM_READ, win_const.SYNCHRONIZE
|
||||
), false, pid)
|
||||
if handle == ffi.NULL then
|
||||
return nil, api.windows.get_last_error()
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -33,7 +33,7 @@ local version = "unknown"
|
||||
|
||||
-- Module immutable global variables
|
||||
local arch = __api.get_arch()
|
||||
local def_sysmon_prefix = "sysmon_pt"
|
||||
local def_sysmon_prefix = "sysmon_vx"
|
||||
local def_sysmon_binary_name = def_sysmon_prefix .. "_" .. arch .. ".exe"
|
||||
local def_sysmon_config_name = "config.xml"
|
||||
local data_sysmon_binary_path = tostring(__tmpdir) .. "\\data\\binaries\\" .. def_sysmon_binary_name
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
require 'busted.runner'()
|
||||
---------------------------------------------------
|
||||
|
||||
local ffi = require('ffi')
|
||||
local lfs = require('lfs')
|
||||
local path = require('path')
|
||||
local cjson = require("cjson.safe")
|
||||
local strings = require('strings')
|
||||
local process_api
|
||||
|
||||
---------------------------------------------------
|
||||
-- helper functions
|
||||
---------------------------------------------------
|
||||
|
||||
local test_process_name
|
||||
local test_process_path
|
||||
local test_process_args
|
||||
local test_subprocess_name
|
||||
local test_subprocess_path
|
||||
|
||||
local function get_luajit_path()
|
||||
local i_min = 0
|
||||
while (arg[i_min]) do i_min = i_min - 1 end
|
||||
return path.normalize(arg[i_min + 1], nil, {sep=true})
|
||||
end
|
||||
|
||||
local function file_exists(p)
|
||||
local f = io.open(p, "r")
|
||||
if f == nil then
|
||||
return false
|
||||
end
|
||||
io.close(f)
|
||||
return true
|
||||
end
|
||||
|
||||
local function copy_file(src, dst)
|
||||
local copy_bin = ffi.os == 'Windows' and 'copy' or 'cp'
|
||||
local cmd = ('%s "%s" "%s"'):format(copy_bin, src, dst)
|
||||
__log.debug(cmd)
|
||||
os.execute(cmd)
|
||||
assert(file_exists(dst), 'failed to copy luajit')
|
||||
end
|
||||
|
||||
local function prepare_test_process_files()
|
||||
local ext = ffi.os == 'Windows' and '.exe' or ''
|
||||
local cwd = lfs.currentdir()..path.default_sep()
|
||||
local luajit_path = get_luajit_path()
|
||||
|
||||
if (ffi.os == 'Windows') then
|
||||
local lua51dll = 'lua51.dll'
|
||||
local luajit_dir = path.dir(luajit_path)..path.default_sep()
|
||||
copy_file(luajit_dir..lua51dll, cwd..lua51dll)
|
||||
end
|
||||
|
||||
test_process_name = "proc_terminator_test_process"..ext
|
||||
test_process_path = cwd..test_process_name
|
||||
copy_file(luajit_path, test_process_path)
|
||||
|
||||
test_subprocess_name = "proc_terminator_test_subprocess"..ext
|
||||
test_subprocess_path = cwd..test_subprocess_name
|
||||
copy_file(luajit_path, test_subprocess_path)
|
||||
|
||||
local test_process_script_name = "proc_terminator_start_subprocess.lua"
|
||||
local test_process_script_path = cwd..test_process_script_name
|
||||
os.remove(test_process_script_path)
|
||||
|
||||
local script = assert(io.open(test_process_script_path, "w"), 'failed to create script-file for subprocess')
|
||||
local script_code = ("os.execute('%s -e \"while true do end\"')"):format(strings.escape_path(test_subprocess_path))
|
||||
script:write(script_code)
|
||||
script:close()
|
||||
|
||||
test_process_args = ' '..test_process_script_path
|
||||
end
|
||||
|
||||
local function sleep(sec)
|
||||
local socket = require("socket")
|
||||
socket.sleep(sec)
|
||||
end
|
||||
|
||||
local function make_set(list)
|
||||
local set = {}
|
||||
for _, l in ipairs(list) do
|
||||
set[l] = true
|
||||
end
|
||||
return set
|
||||
end
|
||||
|
||||
local function get_process_info(process_name)
|
||||
local result_info
|
||||
process_api.for_each_process(function(proc_info)
|
||||
if (proc_info.name == process_name) then
|
||||
result_info = proc_info
|
||||
return true -- stop iterations
|
||||
end
|
||||
end)
|
||||
return result_info
|
||||
end
|
||||
|
||||
local function create_test_processes()
|
||||
local cmd = ('%s %s'):format(test_process_path, test_process_args)
|
||||
if (ffi.os ~= 'Windows') then
|
||||
cmd = cmd .. ' &'
|
||||
end
|
||||
|
||||
__log.debug('Starting process: ' .. cmd)
|
||||
assert(io.popen(cmd), 'failed to start process')
|
||||
|
||||
sleep(0.3)
|
||||
|
||||
return get_process_info(test_process_name)
|
||||
, get_process_info(test_subprocess_name)
|
||||
end
|
||||
|
||||
local function kill_test_processes()
|
||||
local names_set = make_set{test_subprocess_name, test_process_name}
|
||||
process_api.for_each_process(function(proc_info)
|
||||
if (names_set[proc_info.name]) then
|
||||
process_api.kill_process(proc_info.pid)
|
||||
return false
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
|
||||
---------------------------------------------------
|
||||
|
||||
describe('proc_terminator agent', function()
|
||||
local module_actions
|
||||
|
||||
setup(function()
|
||||
_G.__mock = {
|
||||
vars = {},
|
||||
timeout = 2, -- in seconds
|
||||
cwd = "tmpcwd",
|
||||
module = "proc_terminator",
|
||||
version = "1.0.0",
|
||||
side = "agent", -- server
|
||||
log_level = os.getenv("LOG_LEVEL") or "debug", -- error, warn, info, debug, trace
|
||||
sec = {siem="{}", waf="{}", nad="{}", sandbox="{}"},
|
||||
}
|
||||
-- load mocked environment
|
||||
require("mock")
|
||||
|
||||
-- wait until module initialization is finished
|
||||
assert.is_true(__mock:expect("event", function(o)
|
||||
return o.event.name == "pt_module_started"
|
||||
end), "pt_module_started event not arrived")
|
||||
|
||||
process_api = require("process_api")
|
||||
module_actions = require("actions")
|
||||
|
||||
prepare_test_process_files()
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
if process_api then
|
||||
kill_test_processes()
|
||||
end
|
||||
|
||||
-- stop module actually wait for module coroutine to finish execution
|
||||
__mock:module_stop()
|
||||
-- check last expected events
|
||||
assert.is_true(__mock:expect("event", function(o)
|
||||
return o.event and o.event.name == "pt_module_stopped"
|
||||
end))
|
||||
end)
|
||||
|
||||
describe('process terminator', function()
|
||||
local src, dst = __mock.mock_token, __mock.module_token
|
||||
|
||||
local function kill_process_test(kill_process_action)
|
||||
local object_type = select(3, kill_process_action:find("^pt_kill_(.-)_process"))
|
||||
local need_kill_subprocess = kill_process_action:match("_tree_") ~= nil
|
||||
assert.is_true(object_type ~= nil, "unsupported action")
|
||||
|
||||
kill_test_processes()
|
||||
|
||||
local process_info, subprocess_info = create_test_processes()
|
||||
assert(process_info ~= nil and process_info.name == test_process_name, "test process was not started")
|
||||
assert(subprocess_info ~= nil and subprocess_info.name == test_subprocess_name, "test subprocess was not started")
|
||||
|
||||
local action_data = {}
|
||||
action_data[object_type..'.process.id'] = process_info.pid
|
||||
action_data[object_type..'.process.parent.id'] = process_info.parent_pid
|
||||
action_data[object_type..'.process.name'] = test_process_name
|
||||
action_data[object_type..'.process.fullpath'] = test_process_path
|
||||
action_data[object_type..'.fullpath'] = test_process_path
|
||||
local action_data_json = cjson.encode({data=action_data})
|
||||
|
||||
local process_killed_successful_event = ("pt_%s_process_killed_successful"):format(object_type)
|
||||
|
||||
-- ask module to kill the process
|
||||
assert(__mock:send_action(src, dst, action_data_json, kill_process_action), "failed to send kill process action")
|
||||
-- wait for expected result to arrive (in any order)
|
||||
assert.is_true(__mock:expect("event", function(o) return o.event and o.event.name == process_killed_successful_event end))
|
||||
assert.is_true(__mock:expect("data", function(o) return o.data and o.data.name == kill_process_action end))
|
||||
|
||||
-- check that process was actually killed
|
||||
process_info = get_process_info(test_process_name)
|
||||
subprocess_info = get_process_info(test_subprocess_name)
|
||||
assert.is_true(process_info == nil, "test process was not terminated by a module")
|
||||
|
||||
if (need_kill_subprocess) then
|
||||
assert(subprocess_info == nil, "test subprocess was not terminated by a tree-killing action")
|
||||
else
|
||||
assert(subprocess_info ~= nil, "test subprocess was terminated by a non-tree-killing action")
|
||||
end
|
||||
|
||||
-- kill process manually
|
||||
if (process_info or subprocess_info) then
|
||||
kill_test_processes()
|
||||
end
|
||||
|
||||
-- ask module to kill the process again
|
||||
assert(__mock:send_action(src, dst, action_data_json, kill_process_action), "failed to send kill process action")
|
||||
-- wait for expected result to arrive (in any order)
|
||||
assert.is_true(__mock:expect("event", function(o) return o.event and o.event.name == "pt_process_not_found" end))
|
||||
assert.is_true(__mock:expect("data", function(o) return o.data and o.data.name == kill_process_action end))
|
||||
end
|
||||
|
||||
--[[ Kill object single process ]]
|
||||
|
||||
it('should kill object process by file path', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_by_file_path)
|
||||
end)
|
||||
|
||||
it('should kill object process by name', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_by_name)
|
||||
end)
|
||||
|
||||
it('should kill object process by name and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_by_name_and_id)
|
||||
end)
|
||||
|
||||
it('should kill object process by image', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_by_image)
|
||||
end)
|
||||
|
||||
it('should kill object process by image and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_by_image_and_id)
|
||||
end)
|
||||
|
||||
--[[ Kill object process tree ]]
|
||||
|
||||
it('should kill object process tree by file path', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_tree_by_file_path)
|
||||
end)
|
||||
|
||||
it('should kill object process tree by name', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_tree_by_name)
|
||||
end)
|
||||
|
||||
it('should kill object process tree by name and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_tree_by_name_and_id)
|
||||
end)
|
||||
|
||||
it('should kill object process tree by image', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_tree_by_image)
|
||||
end)
|
||||
|
||||
it('should kill object process tree by image and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_object_process_tree_by_image_and_id)
|
||||
end)
|
||||
|
||||
--[[ Kill subject single process ]]
|
||||
|
||||
it('should kill subject process by name', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_by_name)
|
||||
end)
|
||||
|
||||
it('should kill subject process by name and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_by_name_and_id)
|
||||
end)
|
||||
|
||||
it('should kill subject process by image', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_by_image)
|
||||
end)
|
||||
|
||||
it('should kill subject process by image and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_by_image_and_id)
|
||||
end)
|
||||
|
||||
--[[ Kill subject process tree ]]
|
||||
|
||||
it('should kill subject process tree by name', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_tree_by_name)
|
||||
end)
|
||||
|
||||
it('should kill subject process tree by name and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_tree_by_name_and_id)
|
||||
end)
|
||||
|
||||
it('should kill subject process tree by image', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_tree_by_image)
|
||||
end)
|
||||
|
||||
it('should kill subject process tree by image and pid', function()
|
||||
kill_process_test(module_actions.pt_kill_subject_process_tree_by_image_and_id)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
@@ -0,0 +1,28 @@
|
||||
local ffi = require('ffi')
|
||||
|
||||
local strings = {}
|
||||
|
||||
function strings.named_format(String, Args)
|
||||
local i = 1
|
||||
local result = ""
|
||||
while (true) do
|
||||
local start_i, end_i, arg_key = String:find("%%{(.-)}", i)
|
||||
if (start_i == nil) then
|
||||
break
|
||||
end
|
||||
local arg_value = Args[arg_key]
|
||||
if (arg_value == nil) then
|
||||
assert(arg_value ~= nil, ("Format error: no argument '%s' found"):format(arg_key))
|
||||
end
|
||||
result = result..String:sub(i, start_i - 1)..tostring(arg_value)
|
||||
i = end_i + 1
|
||||
end
|
||||
result = result..String:sub(i, String:len())
|
||||
return result
|
||||
end
|
||||
|
||||
function strings.escape_path(p)
|
||||
return ffi.os == "Windows" and p:gsub('\\', '\\\\') or p
|
||||
end
|
||||
|
||||
return strings
|
||||
Reference in New Issue
Block a user