var ActionHelper = Class.create();
ActionHelper.prototype = {
initialize: function (import_set_table, utils) {
this.import_set_table = import_set_table;
this.util = utils;
this.DeploymentImportController = new DeploymentImportController();
this.ServiceImportController = new ServiceImportController();
this.minWaitTimeInMs = 20000;
this.maxWaitTimeInMs = 100000;
this.MAX_RETRY_COUNT = 3;
gs.include('Constants');
},
loadData: function (connection_alias) {
var config = this.util.getConfiguration(connection_alias);
var firstRun = true;
for (var item in config) {
var connectionAliasGr = this.getAuth(connection_alias);
if (config[item].get_host_details == 1) {
var hostFilter = this.util.buildFilter(config[item].host_details_filter, config[item].timestamp, "host")[0];
gs.info('Beginning falcon device Sync. Filter: {0}', hostFilter);
var deviceData = this.loadDeviceIds(connectionAliasGr, hostFilter);
}
for (var i = 0; i < deviceData.length; i++) {
var idList = deviceData[i];
var dataToLoad = this.loadDeviceData(connectionAliasGr, idList);
dataToLoad = this.util.keepKeys(dataToLoad, SUPPORTED_HOST_FIELDS);
if (config[item].get_asset_details == 1) {
var assetFilters = this.util.buildFilter(config[item].asset_details_filter, config[item].timestamp, "asset", dataToLoad);
var assetData = [];
for (var assetFilter in assetFilters) {
gs.info('Beginning Falcon managed asset Sync. Filter: {0}', assetFilters[assetFilter]);
assetData = [...assetData, ...this.loadDiscoverData(connectionAliasGr, "/discover/queries/hosts/v1", "/discover/entities/hosts/v1", assetFilters[assetFilter], 'asset')];
}
var assetFlatten = JSON.stringify(assetData[0]);
if (typeof (assetFlatten) != "undefined" && firstRun) {
this.util.flattenObjectWithPrefix(JSON.parse(assetFlatten), "asset.", this.import_set_table, ["groups", "field_metadata", "local_ip_addresses", "mac_addresses", "triage"]);
}
}
if (config[item].get_iot_details == 1) {
var iotFilters = this.util.buildFilter(config[item].iot_data_filter, config[item].timestamp, "iot", dataToLoad);
var iotData = [];
for (var filter in iotFilters) {
gs.info('Beginning Falcon IoT Sync. Filter: {0}', iotFilters[filter]);
iotData = [...iotData, ...this.loadDiscoverData(connectionAliasGr, "/discover/queries/iot-hosts/v1", "/discover/entities/iot-hosts/v1", iotFilters[filter], 'IOT')];
}
var iotFlatten = JSON.stringify(iotData[0]);
if (typeof (iotFlatten) != "undefined" && firstRun) {
this.util.flattenObjectWithPrefix(JSON.parse(iotFlatten), "iot.", this.import_set_table, ["groups", "field_metadata", "triage", "ou"]);
}
}
if (dataToLoad) {
if ((typeof assetData != "undefined") & (typeof iotData != "undefined")) {
//merge Asset&IoT Data
//merge with Device Details
var assetMerge = this.util.mergeLists(this.util.mapAssets(assetData), this.util.mapIOT(iotData), "asset.id", "iot.id");
dataToLoad = this.util.mergeLists(dataToLoad, assetMerge, "device_id", "asset.aid");
} else if (typeof assetData != "undefined") {
//merge Asset & Device
dataToLoad = this.util.mergeLists(dataToLoad, this.util.mapAssets(assetData), "device_id", "asset.aid");
} else if (typeof iotData != "undefined") {
//merge IoT & Device
dataToLoad = this.util.mergeLists(dataToLoad, this.util.mapAssets(iotData), "device_id", "iot.aid");
}
this.util.mapComputer(this.import_set_table, dataToLoad);
}
firstRun = false;
}
this.util.setLastRunTime(config[item].sys_id);
}
},
loadSofwareData: function (connection_alias) {
var config = this.util.getConfiguration(connection_alias);
for (var item in config) {
var connectionAliasGr = this.getAuth(connection_alias);
if (config[item].get_application_details == 1) {
var appFilter = this.util.buildFilter(config[item].application_details_filter, config[item].application_timestamp, "application");
gs.info('Beginning Falcon app Sync. Filter: {0}', appFilter);
this.loadDiscoverData(connectionAliasGr, "/discover/queries/applications/v1", "/discover/entities/applications/v1", appFilter[0], 'application');
this.util.setLastRunTime(config[item].sys_id, "application");
}
}
},
loadASPMDeployments: function (connection_alias) {
var config = this.util.getConfiguration(connection_alias);
var maxRow = this.import_set_table.getMaximumRows();
for (var item in config) {
var connectionAliasGr = this.getAuth(connection_alias);
if (config[item].get_deployment_details == 1) {
//var appFilter = this.util.buildFilter(config[item].application_details_filter, config[item].application_timestamp, "application");
//gs.info('Beginning Falcon app Sync. Filter: {0}', appFilter);
this.DeploymentImportController.beginImport(import_set_table, connectionAliasGr, config[item].description, maxRow);
//this.util.setLastRunTime(config[item].sys_id, "deployment");
}
}
},
loadASPMServices: function (connection_alias) {
var config = this.util.getConfiguration(connection_alias);
var maxRow = this.import_set_table.getMaximumRows();
for (var item in config) {
var connectionAliasGr = this.getAuth(connection_alias);
if (config[item].get_deployment_details == 1) {
//var appFilter = this.util.buildFilter(config[item].application_details_filter, config[item].application_timestamp, "application");
//gs.info('Beginning Falcon app Sync. Filter: {0}', appFilter);
this.ServiceImportController.beginImport(import_set_table, connectionAliasGr, config[item].description, maxRow);
//this.util.setLastRunTime(config[item].sys_id, "deployment");
}
}
},
loadASPMServicesExtra: function (connection_alias) {
var config = this.util.getConfiguration(connection_alias);
var maxRow = this.import_set_table.getMaximumRows();
for (var item in config) {
var connectionAliasGr = this.getAuth(connection_alias);
if (config[item].get_deployment_details == 1) {
//var appFilter = this.util.buildFilter(config[item].application_details_filter, config[item].application_timestamp, "application");
//gs.info('Beginning Falcon app Sync. Filter: {0}', appFilter);
this.ServiceImportController.beginExtrasImport(import_set_table, connectionAliasGr, config[item].description, maxRow);
//this.util.setLastRunTime(config[item].sys_id, "deployment");
}
}
},
loadUnmanagedData: function(connection_alias) {
var config = this.util.getConfiguration(connection_alias);
var connectionAliasGr = this.getAuth(connection_alias);
var mergedData = [];
// Process each configuration item
for (var item in config) {
var unmanaged_asset_data = [];
var unmanaged_iot_data = [];
// Load Unmanaged Asset Data if enabled
if (config[item].get_unmanaged_asset_details == 1) {
var assetFilters = this.util.buildFilter(
config[item].unmanaged_asset_details_filter,
config[item].unmanaged_timestamp,
"unmanaged"
);
for (var filter in assetFilters) {
gs.info('Beginning Unmanaged Asset Sync. Filter: {0}', assetFilters[filter]);
unmanaged_asset_data = [...unmanaged_asset_data,
...this.loadDiscoverDataInChunks(
connectionAliasGr,
"/discover/queries/hosts/v1",
"/discover/entities/hosts/v1",
assetFilters[filter],
'unmanaged',
1000,
function(chunk) {
// Optional: Process chunks as they come in
gs.debug('Processing chunk of unmanaged assets: ' + chunk.length);
}
)
];
}
var assetFlatten = JSON.stringify(unmanaged_asset_data[0]);
this.util.flattenObjectWithPrefix(JSON.parse(assetFlatten), "asset.", this.import_set_table, ["groups", "field_metadata"]);
}
// Load Unmanaged IOT Data if enabled
if (config[item].get_unmanaged_iot_hosts == 1) {
var iotFilters = this.util.buildFilter(
config[item].unmanaged_iot_details_filter,
config[item].unmanaged_timestamp,
"unmanaged"
);
for (var filter in iotFilters) {
gs.info('Beginning Unmanaged IoT Sync. Filter: {0}', iotFilters[filter]);
unmanaged_iot_data = [...unmanaged_iot_data,
...this.loadDiscoverDataInChunks(
connectionAliasGr,
"/discover/queries/iot-hosts/v1",
"/discover/entities/iot-hosts/v1",
iotFilters[filter],
'IOT',
1000,
function(chunk) {
// Optional: Process chunks as they come in
gs.debug('Processing chunk of unmanaged IoT devices: ' + chunk.length);
}
)
];
}
var iotFlatten = JSON.stringify(unmanaged_iot_data[0]);
this.util.flattenObjectWithPrefix(JSON.parse(iotFlatten), "iot.", this.import_set_table, ["groups", "field_metadata"]);
}
// Merge data if both sources are present
if (unmanaged_asset_data.length > 0 && unmanaged_iot_data.length > 0) {
gs.info('Merging Unmanaged Asset and IoT data');
mergedData = this.util.mergeLists(
this.util.mapAssets(unmanaged_asset_data),
this.util.mapIOT(unmanaged_iot_data),
'asset.id',
'iot.id'
);
} else if (unmanaged_asset_data.length > 0) {
mergedData = unmanaged_asset_data;
} else if (unmanaged_iot_data.length > 0) {
mergedData = unmanaged_iot_data;
}
// Process merged data
if (mergedData.length > 0) {
// Insert or update records in import set table
this.processUnmanagedData(mergedData);
}
// Update last run time
this.util.setLastRunTime(config[item].sys_id, "unmanaged");
}
return mergedData;
},
processUnmanagedData: function(data) {
if (!data || !data.length) {
gs.warn('No unmanaged data to process');
return;
}
var batchSize = 1000;
var processedCount = 0;
try {
// Process data in batches
for (var i = 0; i < data.length; i += batchSize) {
var batch = data.slice(i, Math.min(i + batchSize, data.length));
// Call utility method to map and process the batch
this.util.mapUnmanaged(this.import_set_table, batch);
processedCount += batch.length;
gs.info('Processed ' + processedCount + ' of ' + data.length + ' records');
}
gs.info('Successfully processed ' + processedCount + ' unmanaged device records');
} catch (ex) {
gs.error('Error processing unmanaged data: ' + ex);
throw ex;
}
},
loadDiscoverDataInChunks: function (connectionAliasGr, idsPath, detailPath, filter, type, chunkSize, chunkProcessor) {
gs.debug("Filter Is: " + filter);
var records = [];
var sort = (type == "application") ? "last_updated_timestamp.asc" : "last_seen_timestamp.asc";
var totalProcessed = 0;
var batchSize = 10000;
while (true) {
var response = this.getDiscoverIds(connectionAliasGr, filter, sort, idsPath);
gs.info("Collected " + response.length + " Discover IDs to update.");
var ids = response.map(function (item) {
return item.application_id;
});
var chunked_ids = this.util.chunkArray(ids, 100);
for (var i = 0; i < chunked_ids.length; i++) {
var formatIdList = this.util.formatIdList(chunked_ids[i]);
var data = this.getDiscoverDetails(connectionAliasGr, formatIdList, detailPath);
if (type == 'application') {
var newTime = this.util.mapSoftware(this.import_set_table, data);
} else {
// Process other types in chunks
var processedData;
switch (type) {
case 'unmanaged':
processedData = this.util.keepKeys(data, SUPPORTED_UNMANAGED_FIELDS);
break;
case 'asset':
processedData = this.util.keepKeys(data, SUPPORTED_ASSET_FIELDS);
break;
case 'IOT':
processedData = this.util.keepKeys(data, SUPPORTED_IOT_FIELDS);
break;
}
if (processedData) {
var dataChunks = this.util.chunkArray(processedData, chunkSize);
dataChunks.forEach(function (chunk) {
if (chunkProcessor) {
chunkProcessor(chunk);
}
records = records.concat(chunk);
});
}
}
}
totalProcessed += response.length;
if (response.length < batchSize) {
break;
}
// Update filter for next iteration
if (type == 'application') {
filter = this.util.replaceTimestamp(filter, newTime);
} else {
var lastItem = records[records.length - 1];
var newTimestamp = lastItem.last_seen_timestamp;
filter = this.util.replaceTimestamp(filter, newTimestamp);
}
gs.debug("Processed " + totalProcessed + " records. Updating timestamp for next batch: " + filter);
}
gs.info("Total records processed: " + totalProcessed);
return records;
},
loadDeviceIds: function (connectionAliasGr, filter) {
var retryCount = 0;
var isSuccess = true;
do {
try {
var response = this.invokeAction('x_crowd_cmdb.crowdstrike_falcon_device_details', {
"connectionalias": connectionAliasGr,
"filter": filter
});
}
catch (e) {
if (retryCount++ < this.MAX_RETRY_COUNT) {
isSuccess = false;
gs.info("getIDs call failed due to bad return status in one or more calls. Retrying, retryCount:" + retryCount);
this.sleepInScopedApplication(retryCount - 1);
} else {
gs.error("Retry limit reached. Unable to gather IDs");
isSuccess = true;
}
}
} while (!isSuccess);
gs.info("Collected " + response.length + " Device IDs to update.");
var ids = [];
for (var i = 0; i < response.length; i++) {
ids.push(response[i].aid);
}
return this.util.chunkArray(ids, 1000);
},
loadDeviceData: function (connectionAliasGr, chunked_ids) {
var formatIdList = {};
var records = [];
formatIdList.ids = chunked_ids;
retryCount = 0;
var isSuccess = true;
do {
try {
var data = this.invokeAction('x_crowd_cmdb.sgcrowdstrike_details', {
"connectionalias": connectionAliasGr,
"ids": JSON.stringify(formatIdList)
});
for (var item in data) {
var str = JSON.stringify(data[item]);
var result = JSON.parse(str);
records.push(JSON.parse(result.device));
}
} catch (e) {
if (retryCount++ < this.MAX_RETRY_COUNT) {
isSuccess = false;
gs.info("Batch failed due to bad return status in one or more calls. Retrying, retryCount:" + retryCount);
this.sleepInScopedApplication(retryCount - 1);
} else {
gs.error("Retry limit reached. Unable to process IDs: " + JSON.stringify(formatIdList.ids));
isSuccess = true;
}
}
} while (!isSuccess);
return records;
},
loadDiscoverData: function(connectionAliasGr, idsPath, detailPath, filter, type) {
return this.loadDiscoverDataInChunks(connectionAliasGr, idsPath, detailPath, filter, type, 1000, null);
},
getDiscoverIds: function (connectionAliasGr, filter, sort, idsPath) {
var retryCount = 0;
while (retryCount <= this.MAX_RETRY_COUNT) {
try {
return this.invokeAction('x_crowd_cmdb.sgcrowdstrike_getapplicationids', {
"connectionalias": connectionAliasGr,
"filter": filter,
"sort": sort,
"path": idsPath
});
} catch (e) {
if (retryCount++ < this.MAX_RETRY_COUNT) {
gs.info("getIDs call failed. Retrying, retryCount:" + retryCount);
this.sleepInScopedApplication(retryCount - 1);
} else {
gs.error("Retry limit reached. Unable to gather IDs");
throw e;
}
}
}
},
getDiscoverDetails: function (connectionAliasGr, formatIdList, detailPath) {
var retryCount = 0;
while (retryCount <= this.MAX_RETRY_COUNT) {
try {
return this.invokeAction('x_crowd_cmdb.sgcrowdstrike_getapplicationdetails', {
"connectionalias": connectionAliasGr,
"ids": formatIdList,
"path": detailPath
});
} catch (e) {
if (retryCount++ < this.MAX_RETRY_COUNT) {
gs.info("Batch failed. Retrying, retryCount:" + retryCount);
this.sleepInScopedApplication(retryCount - 1);
} else {
gs.error("Retry limit reached. Unable to process IDs: " + JSON.stringify(formatIdList.ids));
throw e;
}
}
}
},
getAuth: function (sys_id) {
var glideRecordInput = new GlideRecord('sys_alias');
if (glideRecordInput.get(sys_id)) {
return glideRecordInput;
}
},
invokeAction: function (name, args) {
var stream;
var maxRows = 0;
try {
maxRows = import_set_table.getMaximumRows();
} catch (err) { }
if (!maxRows)
maxRows = 0;
var data = [];
try {
stream = sn_fd.FlowAPI.executeDataStreamAction(name, args);
if (gs.nil(stream)) {
gs.error(gs.getMessage("No response from action {0}", [name]));
return;
}
// Process each item in the data stream
while (stream.hasNext()) {
// Get a single item from the data stream.
var item = stream.next();
// Use the item.
data.push(item);
//only get the max rows for the import set
if (stream.getItemIndex() >= maxRows && maxRows > 0) {
gs.info("Max Rows Reached: " + maxRows);
break;
}
}
return data;
} catch (err) {
var message = err.toString();
gs.error("SG CrowdStrike Error: " + message);
if (message == 'Null stream found') {
return;
} else {
throw message;
}
}
},
sleepInScopedApplication: function (retryCount) {
var waitTime = Math.min(this.maxWaitTimeInMs, Math.pow(2, retryCount) * this.minWaitTimeInMs);
this._waitInMs(waitTime);
gs.info("Waiting for " + waitTime + " ms at retry count " + (retryCount + 1));
},
_waitInMs: function (time) {
var startTime = new GlideDateTime().getNumericValue();
var currentTime;
do {
currentTime = new GlideDateTime().getNumericValue();
} while (currentTime - startTime < time);
},
type: 'ActionHelper'
};
This has something to do above script.