JS: Why does SanitizingRegExpTest treat /^...$/m as a whole-string check?
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 45/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- javascript
- Ambito
- security
Direzione di ricerca
Start with javascript/ql/lib/semmle/javascript/Regexp.qll, especially isFullyAnchoredTerm, isMultiline, and maybeMultiline, then trace how isGenericRegExpSanitizer feeds SanitizingRegExpTest in javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll. Check the relevant JavaScript security query tests for anchored regular expressions with and without the m flag. Done means multiline anchored checks no longer suppress taint findings while non-multiline anchored checks retain their current behavior.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Description of the issue
JavaScript taint-tracking queries treat a test against a regular expression anchored with ^ and $ as a check of the whole string, also when the regular expression has the m flag. I'm wondering whether this is intended, because with m, ^ and $ also match at line boundaries, so any string that contains one matching line passes the check: /^[\w.-]+$/m.test("x\ntouch /tmp/marker") is true.
Affected sanitizer
SanitizingRegExpTest, which is "considered to be a sanitizer for all configurations", uses RegExp::isGenericRegExpSanitizer for re.test(x), re.exec(x), x.match(re) and x.matchAll(re). For the true outcome, that predicate only requires isFullyAnchoredTerm, which looks for a leading RegExpCaret and a trailing RegExpDollar. The flags of the regular expression are not consulted, although the same module has isMultiline and maybeMultiline.
Steps to reproduce
const cp = require("child_process");
const app = require("express")();
app.get("/a", (req, res) => {
const dir = req.query.dir;
if (/^[\w.-]+$/m.test(dir)) {
cp.exec("ls " + dir); // not reported
}
res.send("ok");
});
app.get("/b", (req, res) => {
const dir = req.query.dir;
if (/^[\w.-]+$/.test(dir)) {
cp.exec("ls " + dir); // not reported (correct: no newline can pass)
}
res.send("ok");
});
app.get("/c", (req, res) => {
const dir = req.query.dir;
cp.exec("ls " + dir); // reported
res.send("ok");
});
app.get("/d", (req, res) => {
const name = req.query.name;
if (/^\w+$/m.test(name)) {
res.send("<p>Hello " + name + "</p>"); // not reported
} else {
res.send("bad name");
}
});
app.get("/e", (req, res) => {
res.send("<p>Hello " + req.query.name + "</p>"); // reported
});
app.listen(3000);
With javascript-security-extended.qls, only /c (js/command-line-injection) and /e (js/reflected-xss) are reported. /a and /d are not, although at runtime their checks do not restrict the input:
dir = "x\ntouch /tmp/marker"(sent as?dir=x%0Atouch%20/tmp/marker) passes the check in/a, andcp.exec("ls x\ntouch /tmp/marker")runstouch /tmp/markeras a second command. The file is created.name = "x\n<script>alert(1)</script>"passes the check in/d, and the script element is sent in the response.
Without the m flag (/b), the check does restrict the input, so not reporting it is right.
The same mistake has led to real vulnerabilities, e.g. GHSA-3px3-54cx-rmw9 (CVE-2026-83609, @xmldom/xmldom 0.9.0–0.9.11), where an anchored validation regular expression was compiled with the m flag and a name with an embedded line terminator passed the check.
Question
Is it intended that the m flag is ignored here? Would it be reasonable to stop treating an anchored regular expression as a whole-string check when it has the m flag?
Environment
- CodeQL CLI 2.27.2 (
codeql/javascript-queries2.4.7) - CodeQL repository commit for the links above:
781e068896eb7a1a104f1378e8f3f68a1074a015(the files are unchanged since 2.27.2) - Node.js v24.18.0 on Linux/amd64
- Lingua principale
- CodeQL
- Stelle
- 10.2k
- Fork
- 2.1k
- Merge medio
- 2g 18h
- PR unite (30g)
- 142
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di github/codeql
-
JavaScript externs model StorageEvent() with no type argumentForse già presa @asgerf l’ha presa 2 giorni fa. Apertafalse-positive javascript
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
github/codeql#22632 · 3 commenti · 1 reazione · 2 assegnatari ·
I maintainer di solito rispondono entro 1 giorno
-
Add AlertSuppression.ql for Rust (inline // codeql[...] suppression)Forse già presa @cnuss l’ha presa 203 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
github/codeql#21637 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
false-positive
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
github/codeql#21076 · 3 commenti · 3 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
false-positive
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 1 giorno
-
question
Difficoltà 4/5 3-5 giorni Idoneità per principianti 52/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di github/codeql
Issue simili
-
state:triage-needed
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
confirmed enhancement go
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
DennisVlaanderen/aerendil#154 ·
I maintainer di solito rispondono entro 1 giorno
-
backend good first issue level1 security
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
FrostByte-49/Wharf#50 ·
-
bug good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
yunaremaia/taintrace#187 ·
I maintainer di solito rispondono entro 1 giorno