UNPKG

@hclsoftware/secagent

Version:

IAST agent

195 lines (175 loc) 6.14 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 Utils = require('./Utils/Utils'); const {ConfigInfo} = require("./ConfigFile/ConfigInfo"); const TaintTracker = require("./TaintTracker"); const StackTraceUtils = require("./Utils/StackTraceUtils"); const maxParamLength = 300; const maxParamLengthModificationInfo = 50; const NON_PRINTABLE_REGEX = /[\x00-\x08\x0E-\x1F\x7F\uFFFD]/; class StackInfo { constructor(type, parameters, vulnerability, rawStack, propagatorTargetAsString) { this.type = type if (vulnerability != null) { this.from_vulnerability = vulnerability } this.parameters = parameters this['method-signature'] = this.parameters.methodSignature if (rawStack != null) { this.rawStack = rawStack.stack } else{ this.rawStack = null } this.stack = null this.object = '' this.arguments = [] this.return = '' if (propagatorTargetAsString != null) { this.propagatorTargetAsString = propagatorTargetAsString } } updateHash(vulnerability) { const hash = Utils.createHashObject() hash.update(this.type) // don't add method signature to the hash if stack is empty. In that case method signature is the request url if (this.parameters['method-signature'] != null && this.stack != null) { hash.update(this.parameters['method-signature']) } if (this.stack != null) { for (const item of this.stack) { hash.update(item) } } return hash.produce() } static getSimpleParamsStringArray (origThat, simpleThat, methodDescriptor, args, ret) { return { that: this.asString(simpleThat), ret: this.asString(ret), methodSignature: this.asString(methodDescriptor), methodArguments: (args == null) ? [] : Array.from(args, item => this.asString(item)), } } static getParamsStringArray (origThat, simpleThat, methodDescriptor, args, ret) { const res = this.getSimpleParamsStringArray(origThat, simpleThat, methodDescriptor, args, ret) res.objects = { that: simpleThat, ret : ret, args : args } return res } static getParamsStringArrayPostHook (origThat, simpleThat, methodDescriptor, args, ret) { let res = StackInfo.getParamsStringArray(origThat, simpleThat, methodDescriptor, args, ret) StackInfo.replacePasswordParams(res) return res } static replacePasswordParams(params){ const objects = params.objects if (ConfigInfo.ConfigInfo.hidePasswords) { if (objects.that != null) { StackInfo.replacePasswordValue(params, "that", objects.that); } if (objects.ret != null) { StackInfo.replacePasswordValue(params, "ret", objects.ret); } if (objects.args != null) { for (let i = 0; i < objects.args.length; i++) { if (objects.args[i] != null){ StackInfo.replacePasswordValue(params, "methodArguments", objects.args[i], i); } } } } } static replacePasswordValue(params, key, obj, index = undefined) { if (StackInfo.hasPasswordFlow(obj)) { if (index == null) { params[key] = Utils.PASSWORD_TEXT } else { params[key][index] = Utils.PASSWORD_TEXT } } } static hasPasswordFlow(obj) { let taintData = TaintTracker.getTaintedData(obj) if (taintData != null) { for (const flow of taintData.flows) { if (flow.entity.isPassword) { return true } } } return false } static asString(obj, forModificationInfo = false) { let result = '' const limit = forModificationInfo ? maxParamLengthModificationInfo : maxParamLength if (typeof obj === 'string') { result = obj } else if (obj instanceof RegExp || obj instanceof String) { result = obj.toString() } else if (Buffer.isBuffer(obj)) { // Create a string representation of the buffer content const bufferContent = obj.origToString(); const displayContent = this.isNonPrintableText(bufferContent) ? "[binary data]" : bufferContent; return `<buffer: ${displayContent.length > limit ? displayContent.origSubstring(0, limit) + '...' : displayContent} (${obj.length} bytes)>`; } else { try { result = JSON.origStringify(obj) } catch (err) { console.origLog(err) console.origLog(obj) } if (result === null || result === undefined || result === "" || result === "{}") { return undefined } } if (result == null || result.length <= limit) return result return result.origSubstring(0, limit) + '...' } static isNonPrintableText(data){ // If it contains the Unicode replacement character (�) or non-printable characters, it's probably not text content return NON_PRINTABLE_REGEX.test(data); } updateStack() { if (this.stack == null && this.rawStack != null) { this.stack = StackTraceUtils.getStackTraceArray(this.rawStack); } this.object = (this.parameters.that == null || this.parameters.that === "null") ? '' : this.parameters.that this.arguments = this.parameters.methodArguments == null ? [] : this.parameters.methodArguments this.return = (this.parameters.ret == null || this.parameters.ret === "null") ? '' : this.parameters.ret } // called by JSON.stringify, we use this function to ignore specific StackInfo properties. toJSON() { const excludedKeys = new Set(["parameters"]) return Object.fromEntries(Object.entries(this).filter(([key]) => !excludedKeys.has(key))) } getStackTraceString() { if (this.stack == null) { this.updateStack() } // if stack is still null if (this.stack == null) { return '' } return this.stack.toString() } setStackStr(stackStr) { this.stack = stackStr; } } module.exports = StackInfo