UNPKG

@hclsoftware/secagent

Version:

IAST agent

225 lines (186 loc) 6.91 kB
//IASTIGNORE /* * **************************************************** * Licensed Materials - Property of HCL. * (c) Copyright HCL Technologies Ltd. 2017, 2025. * Note to U.S. Government Users *Restricted Rights. * **************************************************** */ 'use strict' 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()