@hclsoftware/secagent
Version:
IAST agent
177 lines (150 loc) • 5.67 kB
JavaScript
//IASTIGNORE
/*
* ****************************************************
* Licensed Materials - Property of HCL.
* (c) Copyright HCL Technologies Ltd. 2017, 2025.
* Note to U.S. Government Users *Restricted Rights.
* ****************************************************
*/
'use strict'
const SessionTracker = require("./SessionTracker");
const Utils = require("./Utils/Utils");
const {ConfigInfo} = require("./ConfigFile/ConfigInfo");
const DastResponseData = require("./IastDast/DastResponseData")
const Logger = require('./Logger/IastLogger')
const {RequestBody} = require("./Utils/RequestBody")
const StackInfo = require("./StackInfo");
function parseQueryString(request, hidePasswords){
return request.query != null ? Object.keys(request.query).map(key => `${key}=${ hidePasswords && SessionTracker.isPasswordName(key) ? Utils.PASSWORD_TEXT : request.query[key]}`).origJoin('&') : request.query
}
function getFullUrlWithoutQuery(request) {
try {
const protocol = request.protocol;
const host = request.headers.host;
const urlWithoutQuery = getUrlWithoutQuery(request.url);
return `${protocol}://${host}${urlWithoutQuery}`;
} catch (e) {
return ''; // Fallback for invalid request object fields or exceptions
}
}
function getUrlWithoutQuery(url) {
const queryIndex = url.origIndexOf('?');
if (queryIndex !== -1) {
return url.origSubstring(0, queryIndex);
}
return url;
}
/**
* Safely retrieves a property value or executes a function to get a value from an object.
*
* @param {Object} obj - The object from which to retrieve the value.
* @param {string|string[]|Function} accessor - The property key (string) or a function that takes the object as the first argument.
* @param {*} defaultValue - The fallback value to return if retrieval fails.
* @param {...any} args - Additional arguments to pass to the accessor function if it's a function.
* @returns {*} - The retrieved value or the fallback value.
*/
function safeGetFrom(obj, accessor, defaultValue, ...args) {
try {
if (typeof accessor === 'function') {
return accessor(obj, ...args);
}
// accessor is an array
if (Array.isArray(accessor)) {
for (const key of accessor) {
if (key in obj) {
obj = obj[key];
}
}
return obj
}
// if accessor is a string
return obj[accessor];
} catch (error) {
//Logger.eventLog.info(`Error accessing '${typeof accessor === 'function' ? accessor.name : accessor}': ${error.message}`);
return defaultValue;
}
}
class RequestInfo {
/* use Object.Assign method for each source container because the original containers are replaced by proxies
* and we dont want to apply their hooks when we access them in our code.
*/
constructor (request, dastRequestData, serverFlowTags) {
this.uri = safeGetFrom(request, '_parsedUrl', 'null') != null ? safeGetFrom(request, ['_parsedUrl', 'pathname'], '') : safeGetFrom(request, 'url', '')
this.url = safeGetFrom(request, 'url', '')
this.fullUrlWithoutQuery = safeGetFrom(request, getFullUrlWithoutQuery, '');
this.queryString = safeGetFrom(request, parseQueryString, '', ConfigInfo.ConfigInfo.hidePasswords)
this.method = safeGetFrom(request, 'method', '')
this.queryParameters = Object.assign({}, safeGetFrom(request, 'query', {}))
this.routeParameters = Object.assign({}, safeGetFrom(request, 'params', {}))
this.allParameters = Object.assign({}, this.queryParameters, this.routeParameters)
this.headers = Object.assign({}, safeGetFrom(request, 'headers', {}))
this.body = new RequestBody(this.headers["content-type"])
this.isSecure = safeGetFrom(request, 'secure', undefined)
this.usedParameters = new Set()
this.usedheaders = new Set()
this.usedCookies = new Set()
this.dastRequestData = dastRequestData
if (this.dastRequestData != null) {
// create empty response data
this.dastResponseData = new DastResponseData();
}
this.serverFlowTags = serverFlowTags
}
clone() {
return Object.assign(Object.create(Object.getPrototypeOf(this)), this)
}
updateBodyParameters(body) {
this.body.data = body
Object.assign(this.allParameters, body)
}
getInfoForReporting() {
const reportObject = {
uri: this.uri,
queryString: this.queryString,
method: this.method,
}
try {
const reportingBody = this.body.getReportingJson()
if (reportingBody != null) {
reportObject.body = reportingBody
}
}
catch (error) {
Logger.eventLog.error(error)
}
return this.trimReportingValuesIfNeeded(reportObject)
}
trimReportingValuesIfNeeded(reportObject) {
reportObject.uri = StackInfo.asString(reportObject.uri)
reportObject.queryString = StackInfo.asString(reportObject.queryString)
return reportObject
}
addUsedParameter(name) {
this.usedParameters.add(name)
}
addUsedHeader(name) {
this.usedheaders.add(name)
}
addUsedCookie(name) {
this.usedCookies.add(name)
}
isUsedParameter(name) {
return this.usedParameters.has(name)
}
isUsedHeader(name) {
return this.usedheaders.has(name)
}
clearUsedCookies() {
this.usedCookies.clear()
}
hasDastRequestData() {
return this.dastRequestData != null;
}
addSinkReportToDastResponse(url, entity, stack, vulnerability, isExtra) {
this.dastResponseData.addSinkReport(url, entity, stack, vulnerability, isExtra);
}
setRouteTemplateInDastResponse(routeTemplate) {
this.dastResponseData.setRouteTemplate(routeTemplate);
}
}
module.exports = RequestInfo