@hclsoftware/secagent
Version:
IAST agent
95 lines (81 loc) • 3.64 kB
JavaScript
//IASTIGNORE
/*
* ****************************************************
* Licensed Materials - Property of HCL.
* (c) Copyright HCL Technologies Ltd. 2017, 2025.
* Note to U.S. Government Users *Restricted Rights.
* ****************************************************
*/
const {RequestRule, AdditionalInfoKey} = require('./RequestRule')
const Vulnerability = require('../Vulnerability')
const { ConfigInfo } = require('../ConfigFile/ConfigInfo')
const SessionTracker = require('../SessionTracker')
class Csrf extends RequestRule {
constructor () {
super(Vulnerability.CSRF, true)
this.currentRequestInfo = null
}
isVulnerable (requestInfo, responseText, additionalInfo) {
this.currentRequestInfo = requestInfo
const isVulnerable = this.isCurrentRequestImportant(additionalInfo) && this.sessionCookieUsed(additionalInfo) && !this.isLoginRequest() && !this.currentRequestHasValidCsrfToken() && this.isSessionLoggedIn(responseText)
if (isVulnerable) {
additionalInfo[AdditionalInfoKey] = additionalInfo[AdditionalInfoKey].origConcat("; not a login request; no valid csrf token detected; session logged in")
}
return isVulnerable
}
isSessionLoggedIn (responseText) {
return SessionTracker.responseTextMatchesInSessionPattern(responseText)
}
isCurrentRequestImportant (additionalInfo) {
const isImportant = this.currentRequestInfo.method.origToUpperCase() === 'POST' || this.isUserConfigRequest(additionalInfo)
if (isImportant) {
additionalInfo[AdditionalInfoKey] = additionalInfo[AdditionalInfoKey].origConcat(`HTTP method: [${this.currentRequestInfo.method.origToUpperCase()}]`)
}
return isImportant
}
isUserConfigRequest (additionalInfo) {
for (const userRequest of ConfigInfo.ConfigInfo.csrfRequests) {
if (userRequest.equalsToActualRequest(this.currentRequestInfo)) {
additionalInfo[AdditionalInfoKey] = additionalInfo[AdditionalInfoKey].origConcat("CSRF request specified in user config; ")
return true
}
}
return false
}
sessionCookieUsed(additionalInfo){
if (this.currentRequestInfo.session != null){
additionalInfo[AdditionalInfoKey] = additionalInfo[AdditionalInfoKey].origConcat("; active session detected")
return true
}
for (const cookieName of this.currentRequestInfo.usedCookies){
if (SessionTracker.isSessionCookie(cookieName)){
additionalInfo[AdditionalInfoKey] = additionalInfo[AdditionalInfoKey].origConcat(`; session cookie used: [${cookieName}]`)
return true
}
}
return false
}
isLoginRequest(){
return SessionTracker.isLoginRequest(this.currentRequestInfo)
}
currentRequestHasValidCsrfToken () {
return this.hasValidToken(this.currentRequestInfo.allParameters, this.currentRequestInfo.isUsedParameter) ||
this.hasValidToken(this.currentRequestInfo.headers, this.currentRequestInfo.isUsedHeader)
}
hasValidToken (container, isUsedToken) {
for (const tokenCandidate in container) {
if (this.isValidTokenName(tokenCandidate.toLowerCase())) {
const csrfTokenValue = container[tokenCandidate]
if (csrfTokenValue && isUsedToken.call(this.currentRequestInfo, tokenCandidate)) {
return true
}
}
}
return false
}
isValidTokenName (parameterName) {
return parameterName != null &&
(parameterName.origStringIncludes('csrf') || parameterName.origStringIncludes('xsrf')) && parameterName.origStringIncludes('token')
}
}
module.exports = Csrf