From 96de3be650b3687b6886fd6ac1361b53f002d4b5 Mon Sep 17 00:00:00 2001 From: Nathan Brahms Date: Wed, 8 May 2024 20:33:41 +0000 Subject: [PATCH 1/4] add p0_security/direct-response-write-copy.yaml --- p0_security/direct-response-write-copy.yaml | 238 ++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 p0_security/direct-response-write-copy.yaml diff --git a/p0_security/direct-response-write-copy.yaml b/p0_security/direct-response-write-copy.yaml new file mode 100644 index 0000000000..00a3c1f7d7 --- /dev/null +++ b/p0_security/direct-response-write-copy.yaml @@ -0,0 +1,238 @@ +rules: +- id: direct-response-write + message: Detected directly writing to a Response object from user-defined input. + This bypasses any HTML escaping and may expose your application to a Cross-Site-scripting + (XSS) vulnerability. Instead, use 'resp.render()' to render safely escaped HTML. + options: + interfile: true + metadata: + interfile: true + references: + - https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html + owasp: + - A07:2017 - Cross-Site Scripting (XSS) + - A03:2021 - Injection + cwe: + - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site + Scripting'')' + category: security + technology: + - express + cwe2022-top25: true + cwe2021-top25: true + subcategory: + - vuln + likelihood: MEDIUM + impact: MEDIUM + confidence: MEDIUM + license: Commons Clause License Condition v1.0[LGPL-2.1-only] + vulnerability_class: + - Cross-Site-Scripting (XSS) + languages: + - javascript + - typescript + severity: WARNING + mode: taint + pattern-sources: + - patterns: + - pattern-either: + - pattern-inside: function ... ($REQ, $RES) {...} + - pattern-inside: function ... ($REQ, $RES, $NEXT) {...} + - patterns: + - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES) {...}) + - metavariable-regex: + metavariable: $METHOD + regex: ^(get|post|put|head|delete|options) + - pattern-not-inside: | + function ... ($REQ, $RES) { + ... + $RES.$SET('Content-Type', '$TYPE') + } + - pattern-not-inside: | + $APP.$METHOD(..., function $FUNC($REQ, $RES) { + ... + $RES.$SET('Content-Type', '$TYPE') + }) + - pattern-not-inside: | + function ... ($REQ, $RES, $NEXT) { + ... + $RES.$SET('Content-Type', '$TYPE') + } + - pattern-not-inside: | + function ... ($REQ, $RES) { + ... + $RES.set('$TYPE') + } + - pattern-not-inside: | + $APP.$METHOD(..., function $FUNC($REQ, $RES) { + ... + $RES.set('$TYPE') + }) + - pattern-not-inside: | + function ... ($REQ, $RES, $NEXT) { + ... + $RES.set('$TYPE') + } + - pattern-either: + - pattern: $REQ.query + - pattern: $REQ.body + - pattern: $REQ.params + - patterns: + - pattern-either: + - pattern-inside: | + ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) => + {...} + - pattern-inside: | + ({ $REQ }: Request,$RES: Response) => {...} + - pattern-not-inside: | + ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) => + { + ... + $RES.$SET('Content-Type', '$TYPE') + } + - pattern-not-inside: | + ({ $REQ }: Request,$RES: Response) => { + ... + $RES.$SET('Content-Type', '$TYPE') + } + - pattern-not-inside: | + ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) => + { + ... + $RES.set('$TYPE') + } + - focus-metavariable: $REQ + - pattern-either: + - pattern: params + - pattern: query + - pattern: body + pattern-sinks: + - patterns: + - pattern-inside: function ... (..., $RES,...) {...} + - pattern-either: + - pattern: $RES.write($ARG) + - pattern: $RES.send($ARG) + - pattern-not: $RES. ... .set('...'). ... .send($ARG) + - pattern-not: $RES. ... .type('...'). ... .send($ARG) + - pattern-not-inside: $RES.$METHOD({ ... }) + - focus-metavariable: $ARG + pattern-sanitizers: + - patterns: + - pattern-either: + - pattern-inside: | + import $S from "underscore.string" + ... + - pattern-inside: | + import * as $S from "underscore.string" + ... + - pattern-inside: | + import $S from "underscore.string" + ... + - pattern-inside: | + $S = require("underscore.string") + ... + - pattern-either: + - pattern: $S.escapeHTML(...) + - patterns: + - pattern-either: + - pattern-inside: | + import $S from "dompurify" + ... + - pattern-inside: | + import { ..., $S,... } from "dompurify" + ... + - pattern-inside: | + import * as $S from "dompurify" + ... + - pattern-inside: | + $S = require("dompurify") + ... + - pattern-inside: | + import $S from "isomorphic-dompurify" + ... + - pattern-inside: | + import * as $S from "isomorphic-dompurify" + ... + - pattern-inside: | + $S = require("isomorphic-dompurify") + ... + - pattern-either: + - patterns: + - pattern-inside: | + $VALUE = $S(...) + ... + - pattern: $VALUE.sanitize(...) + - patterns: + - pattern-inside: | + $VALUE = $S.sanitize + ... + - pattern: $S(...) + - pattern: $S.sanitize(...) + - pattern: $S(...) + - patterns: + - pattern-either: + - pattern-inside: | + import $S from 'xss'; + ... + - pattern-inside: | + import * as $S from 'xss'; + ... + - pattern-inside: | + $S = require("xss") + ... + - pattern: $S(...) + - patterns: + - pattern-either: + - pattern-inside: | + import $S from 'sanitize-html'; + ... + - pattern-inside: | + import * as $S from "sanitize-html"; + ... + - pattern-inside: | + $S = require("sanitize-html") + ... + - pattern: $S(...) + - patterns: + - pattern-either: + - pattern-inside: | + $S = new Remarkable() + ... + - pattern: $S.render(...) + - patterns: + - pattern-either: + - pattern-inside: | + import $S from 'express-xss-sanitizer'; + ... + - pattern-inside: | + import * as $S from "express-xss-sanitizer"; + ... + - pattern-inside: | + const { ..., $S, ... } = require('express-xss-sanitizer'); + ... + - pattern-inside: | + var { ..., $S, ... } = require('express-xss-sanitizer'); + ... + - pattern-inside: | + let { ...,$S,... } = require('express-xss-sanitizer'); + ... + - pattern-inside: | + $S = require("express-xss-sanitizer") + ... + - pattern: $S(...) + - patterns: + - pattern: $RES. ... .type('$F'). ... .send(...) + - metavariable-regex: + metavariable: $F + regex: (?!.*text/html) + - patterns: + - pattern-inside: | + $X = [...]; + ... + - pattern: | + if(<... !$X.includes($SOURCE)...>) { + ... + return ... + } + ... + - pattern: $SOURCE From 964079d92abebebc87a924d5fee8aede9a2f92f7 Mon Sep 17 00:00:00 2001 From: Nathan Brahms Date: Wed, 8 May 2024 20:33:42 +0000 Subject: [PATCH 2/4] add p0_security/direct-response-write-copy.jsx --- p0_security/direct-response-write-copy.jsx | 172 +++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 p0_security/direct-response-write-copy.jsx diff --git a/p0_security/direct-response-write-copy.jsx b/p0_security/direct-response-write-copy.jsx new file mode 100644 index 0000000000..f0718cd16f --- /dev/null +++ b/p0_security/direct-response-write-copy.jsx @@ -0,0 +1,172 @@ + +const express = require('express') +const router = express.Router() +var xss = require("xss"); + +import { + AdminUpdateUserAttributesCommand, + CognitoIdentityProviderClient, +} from "@aws-sdk/client-cognito-identity-provider"; + + +router.get('/greeting', (req, res) => { + const { name } = req.query; + // ruleid: direct-response-write + res.send('

Hello :' + name + "

") +}) + +//template handle escaping +router.get('/greet-template', (req, res) => { + name = req.query.name + // ok: direct-response-write + res.render('index', { user_name: name }); +}) + +//template handle escaping +router.get('/greet-template', (req, res) => { + a = req.query.name + // ok: direct-response-write + res.send('

Hello :' + xss(a) + "

") +}) + + +module.exports = router + + +app.get('/', function (req, res) { + var user = req.query.name; + + msg = "Hi " + user + // ruleid: direct-response-write + res.send('Response
' + msg); +}); + + +var msg = ''; +app.get('/3', function (req, res) { + var user = req.query.name; + + msg = "Hi " + user + // ruleid: direct-response-write + res.send('Response
' + msg); +}); + +app.get('/2', function (req, res) { + var user = { user: req.query.name }; + // ruleid: direct-response-write + res.send('Response
' + user.name); +}); + + +app.get('/4', function (req, res) { + var user = req.query.name; + var header = ""; + var msg = 'Hi ' + user; + var footer = ""; + var output = header + msg + footer; + // ruleid: direct-response-write + res.send(output); +}); + +app.get('/4', function (req, res) { + var user = req.query.name; + var header = ""; + var msg = 'Hi ' + user; + var footer = ""; + var output = header + msg + footer; + // ok: direct-response-write + res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); +}); + +var express = require('express'); +var app = express(); +app.get('/', function (req, res) { + var resp = req.query.name; + // ruleid: direct-response-write + res.send('Response
' + resp); +}); +app.get('/3', function (req, res) { + var resp = req.query.name; + // ruleid: direct-response-write + res.write('Response
' + resp); +}); + +app.get('/3', function (req, res) { + var resp = req.foo; + var x = 1; + // ok: direct-response-write + res.write('Response
' + resp); +}); + +app.get('/xss', function (req, res) { + var html = "ASadad" + req.query.name + "Asdadads" + // ruleid: direct-response-write + res.write('Response
' + html); +}); +app.get('/xss', function (req, res) { + // ruleid: direct-response-write + res.write('Response
' + req.query('doo')); +}); +app.get('/xss', function (req, res) { + // ok: direct-response-write + res.set('Content-Type','text/plain') + res.write('Response
' + req.query.name); +}); + +app.get('/noxss', function (req, res) { + var resp = req.query.name; + // ok: direct-response-write + res.write('Response
'); +}); + +app.get('/noxs2s', function (req, res) { + var resp = req.query.name; + // ruleid: direct-response-write + res.write('Response
' + resp); +}); + +app.get('/xss', function (req, res) { + var resp = req.query.name; + var html = "ASadad" + resp + "Asdadads" + // ruleid: direct-response-write + res.write('Response
' + html); +}); + +const jsonRouter = express.Router(); +jsonRouter.use(express.json()); +jsonRouter.get('/noxss-json', function (req, res) { + var name = req.query.name; + // ok: direct-response-write + res.write({ name }); +}); +app.use(jsonRouter); + +// For https://github.com/returntocorp/semgrep-rules/issues/2872 +app.post( + "/:id", + async (req, res, next) => { + const userId = req.params?.id; + + if (user.email !== req.body.email) { + const command = new AdminUpdateUserAttributesCommand({ + Username: user.cognitoUserId, + UserPoolId: process.env.COGNITO_USER_POOL_ID, + UserAttributes: [ + { + Name: "email", + Value: req.body.email, + } + ], + }); + + // ok: direct-response-write + await client.send(command); + } + + res.status(200).send(); + } +); + + +app.listen(8000); + From 335ac432030ed6681fb05adfc6e82f1ef1782dea Mon Sep 17 00:00:00 2001 From: Vasilii Date: Thu, 9 May 2024 11:59:21 +0900 Subject: [PATCH 3/4] move direct-response-write rule to xss folder --- .../audit/xss/direct-response-write.js | 9 + .../audit/xss/direct-response-write.yaml | 17 +- p0_security/direct-response-write-copy.jsx | 172 ------------- p0_security/direct-response-write-copy.yaml | 238 ------------------ 4 files changed, 19 insertions(+), 417 deletions(-) delete mode 100644 p0_security/direct-response-write-copy.jsx delete mode 100644 p0_security/direct-response-write-copy.yaml diff --git a/javascript/express/security/audit/xss/direct-response-write.js b/javascript/express/security/audit/xss/direct-response-write.js index 3898522ff5..f0718cd16f 100644 --- a/javascript/express/security/audit/xss/direct-response-write.js +++ b/javascript/express/security/audit/xss/direct-response-write.js @@ -132,6 +132,15 @@ app.get('/xss', function (req, res) { res.write('Response
' + html); }); +const jsonRouter = express.Router(); +jsonRouter.use(express.json()); +jsonRouter.get('/noxss-json', function (req, res) { + var name = req.query.name; + // ok: direct-response-write + res.write({ name }); +}); +app.use(jsonRouter); + // For https://github.com/returntocorp/semgrep-rules/issues/2872 app.post( "/:id", diff --git a/javascript/express/security/audit/xss/direct-response-write.yaml b/javascript/express/security/audit/xss/direct-response-write.yaml index 370edab86c..00a3c1f7d7 100644 --- a/javascript/express/security/audit/xss/direct-response-write.yaml +++ b/javascript/express/security/audit/xss/direct-response-write.yaml @@ -1,10 +1,8 @@ rules: - id: direct-response-write - message: >- - Detected directly writing to a Response object from user-defined input. This bypasses - any HTML escaping and may expose your application to a Cross-Site-scripting - (XSS) vulnerability. Instead, use 'resp.render()' to render - safely escaped HTML. + message: Detected directly writing to a Response object from user-defined input. + This bypasses any HTML escaping and may expose your application to a Cross-Site-scripting + (XSS) vulnerability. Instead, use 'resp.render()' to render safely escaped HTML. options: interfile: true metadata: @@ -15,7 +13,8 @@ rules: - A07:2017 - Cross-Site Scripting (XSS) - A03:2021 - Injection cwe: - - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" + - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site + Scripting'')' category: security technology: - express @@ -26,6 +25,9 @@ rules: likelihood: MEDIUM impact: MEDIUM confidence: MEDIUM + license: Commons Clause License Condition v1.0[LGPL-2.1-only] + vulnerability_class: + - Cross-Site-Scripting (XSS) languages: - javascript - typescript @@ -112,6 +114,7 @@ rules: - pattern: $RES.send($ARG) - pattern-not: $RES. ... .set('...'). ... .send($ARG) - pattern-not: $RES. ... .type('...'). ... .send($ARG) + - pattern-not-inside: $RES.$METHOD({ ... }) - focus-metavariable: $ARG pattern-sanitizers: - patterns: @@ -222,7 +225,7 @@ rules: - metavariable-regex: metavariable: $F regex: (?!.*text/html) - - patterns: + - patterns: - pattern-inside: | $X = [...]; ... diff --git a/p0_security/direct-response-write-copy.jsx b/p0_security/direct-response-write-copy.jsx deleted file mode 100644 index f0718cd16f..0000000000 --- a/p0_security/direct-response-write-copy.jsx +++ /dev/null @@ -1,172 +0,0 @@ - -const express = require('express') -const router = express.Router() -var xss = require("xss"); - -import { - AdminUpdateUserAttributesCommand, - CognitoIdentityProviderClient, -} from "@aws-sdk/client-cognito-identity-provider"; - - -router.get('/greeting', (req, res) => { - const { name } = req.query; - // ruleid: direct-response-write - res.send('

Hello :' + name + "

") -}) - -//template handle escaping -router.get('/greet-template', (req, res) => { - name = req.query.name - // ok: direct-response-write - res.render('index', { user_name: name }); -}) - -//template handle escaping -router.get('/greet-template', (req, res) => { - a = req.query.name - // ok: direct-response-write - res.send('

Hello :' + xss(a) + "

") -}) - - -module.exports = router - - -app.get('/', function (req, res) { - var user = req.query.name; - - msg = "Hi " + user - // ruleid: direct-response-write - res.send('Response
' + msg); -}); - - -var msg = ''; -app.get('/3', function (req, res) { - var user = req.query.name; - - msg = "Hi " + user - // ruleid: direct-response-write - res.send('Response
' + msg); -}); - -app.get('/2', function (req, res) { - var user = { user: req.query.name }; - // ruleid: direct-response-write - res.send('Response
' + user.name); -}); - - -app.get('/4', function (req, res) { - var user = req.query.name; - var header = ""; - var msg = 'Hi ' + user; - var footer = ""; - var output = header + msg + footer; - // ruleid: direct-response-write - res.send(output); -}); - -app.get('/4', function (req, res) { - var user = req.query.name; - var header = ""; - var msg = 'Hi ' + user; - var footer = ""; - var output = header + msg + footer; - // ok: direct-response-write - res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); -}); - -var express = require('express'); -var app = express(); -app.get('/', function (req, res) { - var resp = req.query.name; - // ruleid: direct-response-write - res.send('Response
' + resp); -}); -app.get('/3', function (req, res) { - var resp = req.query.name; - // ruleid: direct-response-write - res.write('Response
' + resp); -}); - -app.get('/3', function (req, res) { - var resp = req.foo; - var x = 1; - // ok: direct-response-write - res.write('Response
' + resp); -}); - -app.get('/xss', function (req, res) { - var html = "ASadad" + req.query.name + "Asdadads" - // ruleid: direct-response-write - res.write('Response
' + html); -}); -app.get('/xss', function (req, res) { - // ruleid: direct-response-write - res.write('Response
' + req.query('doo')); -}); -app.get('/xss', function (req, res) { - // ok: direct-response-write - res.set('Content-Type','text/plain') - res.write('Response
' + req.query.name); -}); - -app.get('/noxss', function (req, res) { - var resp = req.query.name; - // ok: direct-response-write - res.write('Response
'); -}); - -app.get('/noxs2s', function (req, res) { - var resp = req.query.name; - // ruleid: direct-response-write - res.write('Response
' + resp); -}); - -app.get('/xss', function (req, res) { - var resp = req.query.name; - var html = "ASadad" + resp + "Asdadads" - // ruleid: direct-response-write - res.write('Response
' + html); -}); - -const jsonRouter = express.Router(); -jsonRouter.use(express.json()); -jsonRouter.get('/noxss-json', function (req, res) { - var name = req.query.name; - // ok: direct-response-write - res.write({ name }); -}); -app.use(jsonRouter); - -// For https://github.com/returntocorp/semgrep-rules/issues/2872 -app.post( - "/:id", - async (req, res, next) => { - const userId = req.params?.id; - - if (user.email !== req.body.email) { - const command = new AdminUpdateUserAttributesCommand({ - Username: user.cognitoUserId, - UserPoolId: process.env.COGNITO_USER_POOL_ID, - UserAttributes: [ - { - Name: "email", - Value: req.body.email, - } - ], - }); - - // ok: direct-response-write - await client.send(command); - } - - res.status(200).send(); - } -); - - -app.listen(8000); - diff --git a/p0_security/direct-response-write-copy.yaml b/p0_security/direct-response-write-copy.yaml deleted file mode 100644 index 00a3c1f7d7..0000000000 --- a/p0_security/direct-response-write-copy.yaml +++ /dev/null @@ -1,238 +0,0 @@ -rules: -- id: direct-response-write - message: Detected directly writing to a Response object from user-defined input. - This bypasses any HTML escaping and may expose your application to a Cross-Site-scripting - (XSS) vulnerability. Instead, use 'resp.render()' to render safely escaped HTML. - options: - interfile: true - metadata: - interfile: true - references: - - https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html - owasp: - - A07:2017 - Cross-Site Scripting (XSS) - - A03:2021 - Injection - cwe: - - 'CWE-79: Improper Neutralization of Input During Web Page Generation (''Cross-site - Scripting'')' - category: security - technology: - - express - cwe2022-top25: true - cwe2021-top25: true - subcategory: - - vuln - likelihood: MEDIUM - impact: MEDIUM - confidence: MEDIUM - license: Commons Clause License Condition v1.0[LGPL-2.1-only] - vulnerability_class: - - Cross-Site-Scripting (XSS) - languages: - - javascript - - typescript - severity: WARNING - mode: taint - pattern-sources: - - patterns: - - pattern-either: - - pattern-inside: function ... ($REQ, $RES) {...} - - pattern-inside: function ... ($REQ, $RES, $NEXT) {...} - - patterns: - - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES) {...}) - - metavariable-regex: - metavariable: $METHOD - regex: ^(get|post|put|head|delete|options) - - pattern-not-inside: | - function ... ($REQ, $RES) { - ... - $RES.$SET('Content-Type', '$TYPE') - } - - pattern-not-inside: | - $APP.$METHOD(..., function $FUNC($REQ, $RES) { - ... - $RES.$SET('Content-Type', '$TYPE') - }) - - pattern-not-inside: | - function ... ($REQ, $RES, $NEXT) { - ... - $RES.$SET('Content-Type', '$TYPE') - } - - pattern-not-inside: | - function ... ($REQ, $RES) { - ... - $RES.set('$TYPE') - } - - pattern-not-inside: | - $APP.$METHOD(..., function $FUNC($REQ, $RES) { - ... - $RES.set('$TYPE') - }) - - pattern-not-inside: | - function ... ($REQ, $RES, $NEXT) { - ... - $RES.set('$TYPE') - } - - pattern-either: - - pattern: $REQ.query - - pattern: $REQ.body - - pattern: $REQ.params - - patterns: - - pattern-either: - - pattern-inside: | - ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) => - {...} - - pattern-inside: | - ({ $REQ }: Request,$RES: Response) => {...} - - pattern-not-inside: | - ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) => - { - ... - $RES.$SET('Content-Type', '$TYPE') - } - - pattern-not-inside: | - ({ $REQ }: Request,$RES: Response) => { - ... - $RES.$SET('Content-Type', '$TYPE') - } - - pattern-not-inside: | - ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) => - { - ... - $RES.set('$TYPE') - } - - focus-metavariable: $REQ - - pattern-either: - - pattern: params - - pattern: query - - pattern: body - pattern-sinks: - - patterns: - - pattern-inside: function ... (..., $RES,...) {...} - - pattern-either: - - pattern: $RES.write($ARG) - - pattern: $RES.send($ARG) - - pattern-not: $RES. ... .set('...'). ... .send($ARG) - - pattern-not: $RES. ... .type('...'). ... .send($ARG) - - pattern-not-inside: $RES.$METHOD({ ... }) - - focus-metavariable: $ARG - pattern-sanitizers: - - patterns: - - pattern-either: - - pattern-inside: | - import $S from "underscore.string" - ... - - pattern-inside: | - import * as $S from "underscore.string" - ... - - pattern-inside: | - import $S from "underscore.string" - ... - - pattern-inside: | - $S = require("underscore.string") - ... - - pattern-either: - - pattern: $S.escapeHTML(...) - - patterns: - - pattern-either: - - pattern-inside: | - import $S from "dompurify" - ... - - pattern-inside: | - import { ..., $S,... } from "dompurify" - ... - - pattern-inside: | - import * as $S from "dompurify" - ... - - pattern-inside: | - $S = require("dompurify") - ... - - pattern-inside: | - import $S from "isomorphic-dompurify" - ... - - pattern-inside: | - import * as $S from "isomorphic-dompurify" - ... - - pattern-inside: | - $S = require("isomorphic-dompurify") - ... - - pattern-either: - - patterns: - - pattern-inside: | - $VALUE = $S(...) - ... - - pattern: $VALUE.sanitize(...) - - patterns: - - pattern-inside: | - $VALUE = $S.sanitize - ... - - pattern: $S(...) - - pattern: $S.sanitize(...) - - pattern: $S(...) - - patterns: - - pattern-either: - - pattern-inside: | - import $S from 'xss'; - ... - - pattern-inside: | - import * as $S from 'xss'; - ... - - pattern-inside: | - $S = require("xss") - ... - - pattern: $S(...) - - patterns: - - pattern-either: - - pattern-inside: | - import $S from 'sanitize-html'; - ... - - pattern-inside: | - import * as $S from "sanitize-html"; - ... - - pattern-inside: | - $S = require("sanitize-html") - ... - - pattern: $S(...) - - patterns: - - pattern-either: - - pattern-inside: | - $S = new Remarkable() - ... - - pattern: $S.render(...) - - patterns: - - pattern-either: - - pattern-inside: | - import $S from 'express-xss-sanitizer'; - ... - - pattern-inside: | - import * as $S from "express-xss-sanitizer"; - ... - - pattern-inside: | - const { ..., $S, ... } = require('express-xss-sanitizer'); - ... - - pattern-inside: | - var { ..., $S, ... } = require('express-xss-sanitizer'); - ... - - pattern-inside: | - let { ...,$S,... } = require('express-xss-sanitizer'); - ... - - pattern-inside: | - $S = require("express-xss-sanitizer") - ... - - pattern: $S(...) - - patterns: - - pattern: $RES. ... .type('$F'). ... .send(...) - - metavariable-regex: - metavariable: $F - regex: (?!.*text/html) - - patterns: - - pattern-inside: | - $X = [...]; - ... - - pattern: | - if(<... !$X.includes($SOURCE)...>) { - ... - return ... - } - ... - - pattern: $SOURCE From 2be6991d14129d1aabbf1080aefb89c64f6bc1eb Mon Sep 17 00:00:00 2001 From: Vasilii Date: Thu, 9 May 2024 11:59:56 +0900 Subject: [PATCH 4/4] update direct-response-write metadata --- .../express/security/audit/xss/direct-response-write.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/express/security/audit/xss/direct-response-write.yaml b/javascript/express/security/audit/xss/direct-response-write.yaml index 00a3c1f7d7..25959b5687 100644 --- a/javascript/express/security/audit/xss/direct-response-write.yaml +++ b/javascript/express/security/audit/xss/direct-response-write.yaml @@ -1,6 +1,7 @@ rules: - id: direct-response-write - message: Detected directly writing to a Response object from user-defined input. + message: >- + Detected directly writing to a Response object from user-defined input. This bypasses any HTML escaping and may expose your application to a Cross-Site-scripting (XSS) vulnerability. Instead, use 'resp.render()' to render safely escaped HTML. options: