UNPKG

@hclsoftware/secagent

Version:

IAST agent

252 lines (212 loc) 8.46 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 HookParser = require('../Hooks/HookParser') const HookValidator = require('./ReadOnlyHookValidator') const TaintTracker = require('../TaintTracker') const {property} = require('./IastProperties') // replace for operators '+' and '+=' console.__iastPlus = (a, b) => { /* for array argument, the '+' operator calls internally to join in order to convert it to string. Join is propagator, * so in case this array contains tainted elements, the result will be tainted as well (of type String object). As a * result, the '+' operator 'think' that this array cannot be converted into primitive value and therefore throw an * error. To prevent it, we first call join in case of array argument: */ a = Array.isArray(a) ? a.join() : a b = Array.isArray(b) ? b.join() : b const c = a + b return registerTaintAfterConcat(a, b, c) } // replace for operator '==' console.__iastEqualsEquals = (a, b) => { let c = a == b if (a != null && b != null) { const isParameterTainted = a[property.TAINTED_DATA] != null const isOtherTainted = b[property.TAINTED_DATA] != null if (isParameterTainted || isOtherTainted || a[property.SANITIZED] != null || b[property.SANITIZED] != null) { c = a.valueOf() == b.valueOf() if (c) { doEqualsSanitation(a, b, isParameterTainted, isOtherTainted) } } } return c } // replace for operator '!=' console.__iastNotEquals = (a, b) => { let c = a != b if (a != null && b != null) { const isParameterTainted = a[property.TAINTED_DATA] != null const isOtherTainted = b[property.TAINTED_DATA] != null if (isParameterTainted || isOtherTainted || a[property.SANITIZED] != null || b[property.SANITIZED] != null) { c = a.valueOf() != b.valueOf() if (!c) { doEqualsSanitation(a, b, isParameterTainted, isOtherTainted) } } } return c } // replace for operator '===' console.__iastEqualsEqualsEquals = (a, b) => { let c = a === b if (a != null && b != null) { const isParameterTainted = a[property.TAINTED_DATA] != null const isOtherTainted = b[property.TAINTED_DATA] != null if (isParameterTainted || isOtherTainted || a[property.SANITIZED] != null || b[property.SANITIZED] != null) { c = Object.getPrototypeOf(a) == Object.getPrototypeOf(b) && a.valueOf() == b.valueOf() if (c) { doEqualsSanitation(a, b, isParameterTainted, isOtherTainted) } } } return c } // replace for operator '!==' console.__iastNotEqualsEquals = (a, b) => { let c = a !== b if (a != null && b != null) { const isParameterTainted = a[property.TAINTED_DATA] != null const isOtherTainted = b[property.TAINTED_DATA] != null if (isParameterTainted || isOtherTainted || a[property.SANITIZED] != null || b[property.SANITIZED] != null) { c = (Object.getPrototypeOf(a) != Object.getPrototypeOf(b) || a.valueOf() != b.valueOf()) if (!c) { doEqualsSanitation(a, b, isParameterTainted, isOtherTainted) } } } return c } // TBD: here we're still assuming that if an object is tainted, it must be a string primitive console.__iastIsObject = (a) => { let c = a === Object(a) if (a != null && (a[property.TAINTED_DATA] != null || a[property.SANITIZED] != null)) { return false } return c } // TBD: here we're still assuming that if an object is tainted, it must be a string primitive console.__iastIsNotObject = (a) => { let c = a !== Object(a) if (a != null && (a[property.TAINTED_DATA] != null || a[property.SANITIZED] != null)) { return true } return c } console.__iastTypeof = (a) => { let c = typeof a if ((a != null && a instanceof String && (a[property.TAINTED_DATA] != null || a[property.SANITIZED] != null))) { c = 'string' } else if (a != null && a[property.FUNCTION_PROXY] != null) { c = 'function' } return c } console.__iastNot = (a) => { let c = !a if ((a != null && a instanceof String && (a[property.TAINTED_DATA] != null || a[property.SANITIZED] != null))) { c = !(a.toString()) } return c } console.__iastToString = (a) => { return a instanceof String && (a[property.TAINTED_DATA] != null || a[property.SANITIZED] != null) ? a.toString() : a } console.__iastCryptoCreateCipher = (that, ...args) => { const validator = HookValidator.validators.CRYPTO_CREATE_CIPHER return doReadOnlyHook('crypto.createCipher', validator, that, args) } console.__iastSqlite3DatabaseExec = (that, ...args) => { const validator = HookValidator.validators.SQLITE3_DATABASE_EXEC return doReadOnlyHook('sqlite3.Database.prototype.exec', validator, that, args) } console.__iastConcat = (a, b) => { /* for array argument, the '+' operator calls internally to join in order to convert it to string. Join is propagator, * so in case this array contains tainted elements, the result will be tainted as well (of type String object). As a * result, the '+' operator 'think' that this array cannot be converted into primitive value and therefore throw an * error. To prevent it, we first call join in case of array argument: */ a = Array.isArray(a) ? a.join() : a b = Array.isArray(b) ? b.join() : b const c = `${a}${b}` return registerTaintAfterConcat(a, b, c) } global.__iastEvalCheck = (that, origArg) => { if (that != null && !Array.isArray(that) && that.toString() === '[object global]'){ return global.__iastIndirectEval(origArg) } return that['eval'].apply(that, origArg) } global.__iastEval = (origArg, ret) => { if (origArg[property.TAINTED_DATA]) { const hookFunc = HookParser.getHookFunctionFor('global.eval') return hookFunc(global, [origArg, ret], global.eval) } return ret } global.__iastIndirectEval = (origArg) => { let errorThrown let origRet try { origRet = eval.call(global, origArg[property.TAINTED_DATA] != null ? origArg.toString() : origArg) } catch (err) { errorThrown = err } if (origArg[property.TAINTED_DATA]) { const hookFunc = HookParser.getHookFunctionFor('global.eval') return hookFunc(global, [origArg, origRet], global.eval) } if (errorThrown) { throw errorThrown } return origRet } function doEqualsSanitation(parameter, other, isParameterTainted, isOtherTainted) { if (isParameterTainted && !isOtherTainted) { TaintTracker.sanitizeAll(parameter) } else if (isOtherTainted && !isParameterTainted) { TaintTracker.sanitizeAll(other) } } function doReadOnlyHook (fullName, thatValidator, that, args) { const methodName = fullName.substring(fullName.lastIndexOf('.') + 1) const origMethod = that[methodName] // we need to make sure this object is the correct one. otherwise just run the original method: if (HookParser.hooksActive && thatValidator(that)) { const hookFunc = HookParser.getHookFunctionFor(fullName) return hookFunc(that, args, origMethod) } else { return origMethod.apply(that, args) } } function registerTaintAfterConcat(op1, op2, res) { if ((op1 != null && op1[property.TAINTED_DATA] != null) || (op2 != null && op2[property.TAINTED_DATA] != null)) { const cString = new String(res) if (op1 != null && op1[property.TAINTED_DATA] != null) { TaintTracker.registerTaint(cString, op1[property.TAINTED_DATA].getCopy()) // cString[property.TAINTED_DATA] = a[property.TAINTED_DATA].getCopy() if (op2 != null && op2[property.TAINTED_DATA] != null) { cString[property.TAINTED_DATA].merge(op2[property.TAINTED_DATA], null) } } else // cString[property.TAINTED_DATA] = b[property.TAINTED_DATA].getCopy() { TaintTracker.registerTaint(cString, op2[property.TAINTED_DATA].getCopy()) } const stackObj = new global.origError() global.origError.captureStackTrace(stackObj) cString[property.TAINTED_DATA].addDataToStackList('propagator', null, '+', [op1, op2], cString, stackObj) return cString } return res }