@hclsoftware/secagent
Version:
IAST agent
225 lines (186 loc) • 6.91 kB
JavaScript
//IASTIGNORE
/*
* ****************************************************
* Licensed Materials - Property of HCL.
* (c) Copyright HCL Technologies Ltd. 2017, 2025.
* Note to U.S. Government Users *Restricted Rights.
* ****************************************************
*/
const os = require('os')
const process = require('process')
const path = require('path')
const fs = require('fs')
const { v4: uuidv4, v5: uuidv5 } = require('uuid')
require('./Hooks/SaveOrigMethods')
const Globals = require("./Globals")
const PackageJsonComponentsReader = require('./FileSystemComponentsReader')
const AppInfo = require("./AppInfo")
let IastLogger = null
let Distributor = null
let ConfigFileManager = null
let TasksManager = null
let RequestRule = null
let hookParser = null
let MemoryInfo = null
let logger = null
function start() {
const nodeArgsMessage = `Node process has started with args : ${process.argv}`
if (process.argv.length < 2) { // when running in REPL Mode (evaluation mode)
printStartupError(nodeArgsMessage, 'Node process command does not include entry file')
return
}
AppInfo.init()
addComponentsToAppInfo()
if (AppInfo.hasComponent("react-scripts")) {
printStartupError(nodeArgsMessage, 'react-scripts usage was found')
return
}
if (!AppInfo.hasComponent("express")) {
printStartupError(nodeArgsMessage, 'express server was not found')
return
}
console.origLog(`${nodeArgsMessage}`)
initAgent()
}
function printStartupError(nodeArgsMessage, reason) {
console.origError(`${nodeArgsMessage}`)
console.origError('IAST agent will not be installed')
console.origError(`reason: ${reason}`)
console.origError('\n')
}
function initAgent () {
// place replacement methods on global scope
require('./Hooks/GlobalOperatorHooks')
if (!Globals.ScaProductionMode) {
require('./Injector') // code that replaces operators with global functions on compile
}
IastLogger = require('./Logger/IastLogger')
Distributor = require('./Distributor/Distributor')
ConfigFileManager = require('./ConfigFile/ConfigFileManager')
TasksManager = require('./Tasks/TasksManager')
RequestRule = require('./RequestRules/RequestRule')
hookParser = require('./Hooks/HookParser')
MemoryInfo = require('./Utils/MemoryInfo').MemoryInfo
require('./Hooks/RequireDelegator')
logger = IastLogger.eventLog
ConfigFileManager.init()
logger.info(`working dir : ${process.cwd()}` )
logger.info(`OS : ${process.platform} Node version: ${process.version}` )
const version = require('../package.json').version
if (version != null) {
logger.info(`Loading agent ${version}`)
}
logger.info(`Agent deployment path: ${Globals.IastRootDir}`)
const agentId = calcUUID()
logger.info(`Agent id ${agentId}`)
logger.debug("Memory usage before installation start:");
logger.debug(MemoryInfo.updateAndGetMemoryInfo());
if (Globals.ScaProductionMode) {
logger.info("Running in SCA Production mode. Standard IAST functionality is disabled.");
}
if (Globals.EnableRuntimeSca) {
logger.info("Runtime SCA mode enabled");
}
Distributor.init(agentId)
TasksManager.start()
if (!Globals.ScaProductionMode) {
RequestRule.createInstances()
}
ConfigFileManager.notifyConfigFileUploadedFromServer() // after all listeners are registered
global.origError.stackTraceLimit = 25
if (!Globals.ScaProductionMode) {
/// ///////////// load hooks /////////////////////////////
// hooks should be loaded before AsocConnector is started since it toggles HookParser.hooksActive
hookParser.loadHooks()
}
Distributor.startAsocConnector()
// reference: https://medium.com/@becintec/building-graceful-node-applications-in-docker-4d2cd4d5d392
// The signals we want to handle
// NOTE: although it is tempting, the SIGKILL signal (9) cannot be intercepted and handled
const signals = {
SIGHUP: 1,
SIGINT: 2,
SIGTERM: 15
}
// Create a listener for each of the signals that we want to handle
Object.keys(signals).forEach((signal) => {
process.on(signal, () => {
IastLogger.eventLog.info(`process received a ${signal} signal`)
shutDown()
})
})
process.on('beforeExit', () => {
shutDown()
})
IastLogger.eventLog.info("Initialization complete")
IastLogger.eventLog.debug("Memory usage after installation:");
IastLogger.eventLog.debug(MemoryInfo.updateAndGetMemoryInfo());
}
function addComponentsToAppInfo() {
const components = new PackageJsonComponentsReader().readComponentsUpwards(path.dirname(process.argv[1]))
components.forEach(c => AppInfo.addComponent(c))
}
function shutDown() {
IastLogger.eventLog.info(`shutting down IAST resources`)
Distributor.shutDown()
TasksManager.shutDown()
ConfigFileManager.shutDown()
IastLogger.eventLog.shutDown()
IastLogger.findingsLog.shutDown()
}
function calcUUID() {
let agentId = uuidv4()
// try to get MAC info
const stringBuilder = []
const salt = 'iast'
stringBuilder.push(salt)
try{
const netInterfaces = os.networkInterfaces()
for (let key in netInterfaces) {
if (netInterfaces.hasOwnProperty(key)){
let netInfos=netInterfaces[key];
if (netInfos.length > 0){
const info = netInfos[0]
if (!info.internal){
stringBuilder.push(info.mac)
// TODO: remove print, this is only to debug rare test_agent_id failure
logger.debug("network-interface: " + key + ", mac: " + info.mac)
}
}
}
}
}catch (e) {
logger.error(e)
}
stringBuilder.push(salt)
// if found any MAC address information, create UUID from that.
if (stringBuilder.length > 2){
const nameSpace = "0d64ce40-4610-4d4a-bf4e-54680775a002"
agentId = uuidv5(stringBuilder.join(''), nameSpace)
}
else{
//if we can't get mac info, we read from JsonDir, or generate random UUID (generated in advance so we always return a valid UUID)
// and write it to the jsonDir.
logger.warning("Using second strategy for agent-id");
const uuidFilePath = ConfigFileManager.jsonDir + path.sep + "Secagent-uuid-js"
try{
agentId = fs.readFileSync(uuidFilePath)
}catch (e) {
try {
fs.writeFileSync(uuidFilePath, agentId)
}catch (e) {
logger.error(e)
}
}
}
return agentId;
}
// TODO: hook the following functions for propagation/sanitization in the
// express view plugin called pug:
// pug-runtime.escape(), pug-runtime.attr(), pug-runtime,atrs(), etc...
// TODO:
// - keep track of requestInfo
// - understand if/how threads
// - do not hook inside hook : add a property on the object?
start()