@hclsoftware/secagent
Version:
IAST agent
85 lines (78 loc) • 4.21 kB
JavaScript
//IASTIGNORE
/*
* ****************************************************
* Licensed Materials - Property of HCL.
* (c) Copyright HCL Technologies Ltd. 2017, 2025.
* Note to U.S. Government Users *Restricted Rights.
* ****************************************************
*/
const HookRule = require('./HookRule')
const TaintTracker = require('../../TaintTracker')
const StackInfo = require("../../StackInfo");
const {EntityType} = require("../../Entity");
/**
* Used to propagate taint from object to the string or a String or a Buffer target object or Object's properties.
*
* Note:
* 1. If the target is one of the arguments, and it's a value type string then the taint propagation
* to that argument will NOT have any effect on the actual argument in the user code.
* 2. And if the target is Reference type then the taint propagation to that argument will still hold the taint in the user code.
* 3. If target is the result or the returned, and if is a value type string we wrap it with a String Object and return the target.
* then it would still hold taint in the user's code.
* @class PropagatorRule
* @extends HookRule
* @see HookRule
*/
class PropagatorRule extends HookRule {
doHook (hookValues) {
const source = HookRule.getActualParam(this.data.from, hookValues, this.data.optional)
const parameters = StackInfo.getParamsStringArray(hookValues.that, hookValues.simpleThat, hookValues.methodName, hookValues.args, hookValues.simpleRet)
if (TaintTracker.isObjectTainted(source)){
// if target was a string then we need to wrap it with a String object and set hookValues.ret
if (this.data.to === HookRule.constantArgs.RETURN) {
// eslint-disable-next-line no-new-wrappers
hookValues.ret = typeof hookValues.ret === 'string' ? new String(hookValues.ret) : hookValues.ret
}
let target = HookRule.getActualParam(this.data.to, hookValues);
if (target == null) return // if target is null then we don't need to propagate taint
if (target instanceof String || Buffer.isBuffer(target)) {
if(source instanceof String || Buffer.isBuffer(source)){
TaintTracker.propagateTaint(source, target, hookValues.simpleThat, hookValues.methodName, hookValues.args, hookValues.simpleRet)
} else if (typeof source === 'object') {
TaintTracker.propagateTaintFromObject(source, target, hookValues.simpleThat, hookValues.methodName, hookValues.args, hookValues.simpleRet)
}
} else if (typeof target === 'object') {
this.propagateTaintToObject(source, target, parameters)
} else {
throw new Error(`PropagatorRule: Target is not a String or a Buffer or an Object: Current Type ${typeof target} and target = ${target}. Consider using other Propagator rules`)
}
}
}
/**
* Propagate taint from source to object by recursively traversing the object properties.
* 1. if source is object we iterate over the properties of the source and recursively call this function.
* 2. if the source is a string we propagate taint to the object properties
*/
propagateTaintToObject(source, obj, parameters, entityName, memoization) {
memoization = memoization || new Set()
if (memoization.has(obj)) return obj
memoization.add(obj)
if (typeof obj === 'string') { // could be either string or tainted data
if (obj !== Object(obj)) {
obj = new String(obj)
}
let newTaintedObject = TaintTracker.propagateTaintWithParameters(source, obj, parameters);
if (entityName !== null) {
newTaintedObject.updateEntity(entityName, obj.origToString(), EntityType.PARAMETER)
}
} else {
for (const property in obj) {
if (Object.prototype.hasOwnProperty.call(obj, property) && obj[property] != null) {
obj[property] = this.propagateTaintToObject(source, obj[property], parameters, property, memoization)
}
}
}
return obj
}
}
module.exports = PropagatorRule