Name
sn_agent.AgentAllowListGenerator
Description
Code to generate an allow-list for Agent Client Collector Users should execute this script and take the contents to use for the allow-list file specified in the acc.yml file for each agent.
Script
var AgentAllowListGenerator = Class.create();
/* Function algorithm:
* - starting at the check definition, ordered by the command, iterate through each record
* - the first part of the check command is considered the entry's exec(uting command)
* - the remainder of the check command is considered part of the entry's arg list
* - iterate through the checks parameters and secure parameters, if any param is active=false,
* set entry's skip_arguments=true
* - iterate through the check instances of this check definition
* - from this check command, add to the entry's arg list
* - iterate through the check instance parameters and secure parameters as before
* - continue to the next check instance until no more
* - continue to the next check definition
* - if the exec is the same as the previous, continue to populate the entry
* - if the exec is a new exec, validate the entry, save it to the allowList array
* and start a new entry
* - continue scanning the check definition, then its parameters,
* then its check instances, then the instance parameters as before until there are no more
*/
AgentAllowListGenerator.generate = function() {
// contains template for API used by ITSM Playbook
var ITSM_PLAYBOOK_CK_NAME = 'utils.playbook_osquery';
var allowList = [];
var entry = AgentAllowListGenerator.initEntry();
var fullCommand;
var exec;
var lastExec = '';
var args;
var sp;
// iterate through the check def ordered by command
var check = new GlideRecord('sn_agent_check_def');
check.orderBy('command');
check.query();
while (check.next()) {
// parse out the executing command from the arguments
fullCommand = check.getValue('command');
var result = AgentAllowListGenerator.parseCheckCommand(fullCommand);
exec = result[0];
args = result[1];
var checkSysId = check.getValue('sys_id');
var checkName = check.getValue('name');
// is the executing command match the current entry's command
if (lastExec == exec) {
// if so, update it
AgentAllowListGenerator.populateEntry(checkSysId, entry, args, true);
AgentAllowListGenerator.populateEntryFromCheckInstances(checkSysId, entry);
} else {
// else, push the last entry into allowList
if (lastExec != '') {
AgentAllowListGenerator.validateEntry(entry);
allowList.push(entry);
}
// re-initialize the entry and populate the new data
entry = AgentAllowListGenerator.initEntry(exec);
// if osqueryi, handle ITSM Playbook queries first
if (exec == 'osqueryi' && ITSM_PLAYBOOK_CK_NAME.equals(checkName)) {
AgentAllowListGenerator.handleITSMPlaybook(entry, args);
} else {
AgentAllowListGenerator.populateEntry(checkSysId, entry, args, true);
AgentAllowListGenerator.populateEntryFromCheckInstances(checkSysId, entry);
}
// flag this as the current entry
lastExec = exec;
}
}
// push the last entry
if (entry.exec != "") {
AgentAllowListGenerator.validateEntry(entry);
allowList.push(entry);
}
// create the JSON
var json = new global.JSON();
json.prettify();
return json.encode(allowList);
};
// function to initialize the allowList entry
AgentAllowListGenerator.initEntry = function(execCommand) {
if (!execCommand)
execCommand = '';
entry = {
'exec': execCommand,
'args': [],
'skip_arguments': false
};
return entry;
};
// parse check command string to executing command and argument string
AgentAllowListGenerator.parseCheckCommand = function(command) {
var exec = '';
var args = '';
if (!command)
return [exec, args];
command = command.replaceAll("\r\n", "\n");
sp = command.indexOf(' ');
if (sp > 0) {
exec = command.substring(0, sp);
args = command.substring(sp + 1, command.length);
} else {
exec = command;
args = '';
}
return [exec, args];
};
// function to populate the allowList entry based on the check arguments
AgentAllowListGenerator.populateEntry = function(checkSysId, entry, args, isDef) {
if (args != '') {
if (args.indexOf('.labels.') > 0)
entry.skip_arguments = true;
else
entry.args.push(args); // only push args that don't contain labels
}
AgentAllowListGenerator.populateEntryFromParameters(checkSysId, entry, isDef);
};
// function to populate the allowList entry based on check instances
AgentAllowListGenerator.populateEntryFromCheckInstances = function(checkSysId, entry) {
var fullCommand;
var exec;
var lastExec = '';
var args;
var sp;
var inst = new GlideRecord('sn_agent_check');
inst.addQuery('check_def', checkSysId);
inst.query();
while (inst.next()) {
// parse out the executing command from the arguments
fullCommand = inst.getValue('command');
var result = AgentAllowListGenerator.parseCheckCommand(fullCommand);
exec = result[0];
args = result[1];
var checkInstSysId = inst.getValue('sys_id');
if (exec != entry.exec) {
gs.info('skipping check instance ' + checkInstSysId + ' since command does not match check def');
continue;
}
AgentAllowListGenerator.populateEntry(checkInstSysId, entry, args, false);
}
};
// function to set skip_arguments based on parameters
AgentAllowListGenerator.populateEntryFromParameters = function(checkSysId, entry, isDef) {
var TABLE_PARAM;
var TABLE_SECURE_PARM;
var FIELD_CHECK_REF;
if (isDef) {
// for check definitions
TABLE_PARAM = 'sn_agent_check_param_def';
TABLE_SECURE_PARAM = 'sn_agent_check_secure_param_def';
FIELD_CHECK_REF = 'check_def';
} else {
// for check instances
TABLE_PARAM = 'sn_agent_check_param';
TABLE_SECURE_PARAM = 'sn_agent_check_secure_param';
FIELD_CHECK_REF = 'check';
}
// can't do anything if there is no check to look up parameters for
if (gs.nil(checkSysId))
return;
// if this is already true, just return
if (entry.skip_arguments == true)
return;
// if there are any inactive check parameters, we should set skip_arguments = true
// so that the whitelist will still accomodate it when the check parameter is changed to active
var params = new GlideRecord(TABLE_PARAM);
params.addQuery(FIELD_CHECK_REF, checkSysId);
params.addQuery('active', 'false');
params.query();
if (params.getRowCount() > 0) {
entry.skip_arguments = true;
return;
}
// repeat for secure parameters (for now)
params = new GlideRecord(TABLE_SECURE_PARAM);
params.addQuery(FIELD_CHECK_REF, checkSysId);
params.query();
if (params.getRowCount() > 0) {
entry.skip_arguments = true;
return;
}
};
AgentAllowListGenerator.handleITSMPlaybook = function(entry, args) {
// get any other arguments in the check commands
var strippedArgs = args.replace('"{{.labels.params_query}}"', '');
// find flags with a number value (--logger_min_status 1)
var re = /--(?:[a-z_]+)\s(?:\d+)/g;
var result = strippedArgs.match(re);
if (result) {
result.forEach(function(item) {
entry.args.push(item);
strippedArgs = strippedArgs.replace(item, '');
});
}
var idvArgs = strippedArgs.split(' ');
for (var i = 0; i < idvArgs.length; i++) {
var arg = idvArgs[i].trim();
if (arg)
entry.args.push(arg);
}
// table with the actual query statements
var queries = new GlideRecord('sn_pb_content_query_definition');
if (!queries.isValid())
return;
queries.query();
while (queries.next()) {
// since the label is double-quoted, these values need to be as well
entry.args.push('"' + queries.getValue('command') + '"');
}
};
// function to make sure the entry meets all requirements before adding it to the allowList
AgentAllowListGenerator.validateEntry = function(entry) {
// args cannot be empty
if (entry.args.length == 0)
entry.args.push('');
// if skip_arguments set, clear the args list
if (entry.skip_arguments == true)
entry.args = [''];
// only store the unique arguments
if (entry.args.length > 1)
entry.args = new global.ArrayUtil().unique(entry.args);
};
AgentAllowListGenerator.prototype = {
initialize: function() {},
type: 'AgentAllowListGenerator'
};
Sys ID
fa1d5cf689989010f877f8e7a467caaa