version = "1" language = "javascript" [[source]] id = "req" patterns = ["req.query", "req.body", "req.params", "req.cookies", "req.headers", "request.query"] [[sink]] id = "eval" cwe = "CWE-95" calls = ["eval"] rule_ids = ["js-eval"] [[sink]] id = "exec" cwe = "CWE-78" calls = ["exec", "execSync"] rule_ids = ["js-command-exec"] [[sink]] id = "query" cwe = "CWE-89" calls = ["query", "execute"] rule_ids = ["js-sql-injection"] [[sanitizer]] id = "parameterized_query" calls = ["query", "execute"] parameterized = true # --- model-grounded-detection: raw-Node request sources ------------------------------------------------- # The shipped `req` source fact carries the Express idioms (req.query, req.body, req.params). A raw Node # http server -- which is what a static file server is -- reads the path straight off `req.url` # (http.IncomingMessage.url), and `url.parse(req.url)` is the documented way to split it. Measured need: # with these absent, the angular-http-server path-traversal pair resolved its flow to the captured `res` # rather than to attacker input, because no modelled source matched. [[source]] id = "node-request" patterns = ["req.url", "request.url", "req.rawHeaders", "url.parse", "parsedUrl"] # --- model-grounded-detection task 4.2 ------------------------------------------------------------------- # See the note in python.toml: documented dangerous APIs, provenance per entry, reviewed as data. [[sink]] # CWE-1321. Prototype pollution reaches the object graph through a recursive merge or an unchecked # dynamic key. These are the documented merge helpers plus the assignment forms. id = "prototype.merge" cwe = "CWE-1321" calls = ["merge", "mergeDeep", "extend", "defaultsDeep", "Object.assign", "set", "setWith"] rule_ids = [] [[sink]] # CWE-79. The documented "trusted HTML" sinks in the DOM and the major frameworks. id = "html.unescaped" cwe = "CWE-79" calls = ["innerHTML", "outerHTML", "insertAdjacentHTML", "document.write", "dangerouslySetInnerHTML", "v-html"] rule_ids = [] [[sink]] # CWE-79, the SERVER half. Every sink above is a DOM sink, so this family could only ever ask about code # running in a browser -- and the 2026-09-19 family census recorded it completing 44 questions on a Node # application and finding nothing, which is exactly what a client-only vocabulary must do there. These are the # documented ways an Express or raw-Node handler puts bytes in a response body without an encoder in between. # # `res.json` is deliberately absent: it sets an application/json content type, so a value reaching it is not # a script in a document. `res.render` is deliberately absent too, and for a different reason -- whether a # template escapes is a property of the engine's configuration rather than of the call, so it is claimed as a # configuration defect (`swig.setDefaults`, `nunjucks.configure` in the obligation facts) and stated as a # limit on this family rather than guessed at here. id = "html.response" cwe = "CWE-79" calls = ["res.write", "res.end", "res.send", "response.write", "response.end", "response.send"] rule_ids = [] [[sink]] # CWE-22. Node filesystem entry points that accept a caller-supplied path. id = "path.open" cwe = "CWE-22" calls = ["readFile", "readFileSync", "createReadStream", "sendFile", "unlink", "unlinkSync"] rule_ids = [] [[sink]] # CWE-918 / CWE-601: outbound requests and redirects to a caller-supplied destination. # # The set shipped with five clients and missed the one the measured repository calls. NodeGoat's documented # SSRF is `needle.get(req.query.url + req.query.symbol)` at `app/routes/research.js:16`, and the 2026-09-19 # family census recorded this family completing 44 questions there, finding the open redirect two files away, # and having no way to ask about the request. A thin vocabulary is a silent false negative, so what follows is # the client surface a Node server actually calls. # # Bare where the module ITSELF is the request function, which is the documented call form for all of these # (`axios(config)`, `got(url)`, `needle('get', url)`, `request(opts)`), and which also covers every verb # method through it: a token is matched word-bounded against the callee, and `axios` is bounded inside # `axios.get`. Dotted for `http` and `https`, whose namespace also holds `createServer` -- a server is not an # outbound request, and the bare form would have claimed every one of them. # # Measured on NodeGoat: `needle` matches exactly the research handler's request and nothing else; none of the # other clients appears in that repository, in first-party code or vendored. id = "outbound.request" cwe = "CWE-918" calls = [ "axios", # axios, and axios.get/.post/.put/.patch/.delete/.head/.request through it "got", # got "needle", # needle "request", # request / request-promise "superagent", # superagent "unirest", # unirest "undici", # undici, and undici.fetch/.request through it "fetch", # global fetch, node-fetch, cross-fetch "http.get", "http.request", "https.get", "https.request", ] rule_ids = [] [[sink]] id = "redirect" cwe = "CWE-601" calls = ["redirect", "location.assign", "location.replace"] rule_ids = [] # --- model-grounded-detection: sanitizers ----------------------------------------------------------------- # This file shipped with no [[sanitizer]] entries at all, so no JavaScript fix of any family could be # recognised and the model entailed a fixed twin exactly as readily as its vulnerable one. Measured on the # angular-http-server path-traversal pair, whose fix is precisely `path.normalize(...).replace(/^(\.\.[\/\\])+/, '')`. # Documented normalising / escaping APIs, provenance per entry, reviewed as data. [[sanitizer]] # CWE-22. Node's documented path normalisation; `resolve` and `join` against a fixed base collapse `..`. id = "path.normalize" calls = ["path.normalize", "path.resolve"] [[sanitizer]] # CWE-79. The standard encoders: the DOM/global URI encoders and the common escaping helpers. id = "html.escape" calls = ["encodeURIComponent", "encodeURI", "escapeHtml", "escape", "sanitizeHtml", "DOMPurify.sanitize"] [[layout]] # First-party test conventions are retained as graph context, ordered as unshipped. # These declarations cover JS module/JSX and TS/TSX filenames; they do not assert # TypeScript property support or a browser/server runtime classification. id = "javascript_first_party_tests" vendored = ["node_modules/"] tests = [ "tests/", "test/", "spec/", "__tests__/", "__mocks__/", "e2e/", "*.test.js", "*.test.mjs", "*.test.cjs", "*.test.jsx", "*.test.ts", "*.test.mts", "*.test.cts", "*.test.tsx", "*.spec.js", "*.spec.mjs", "*.spec.cjs", "*.spec.jsx", "*.spec.ts", "*.spec.mts", "*.spec.cts", "*.spec.tsx", "*.mock.js", "*.mock.mjs", "*.mock.cjs", "*.mock.jsx", "*.mock.ts", "*.mock.mts", "*.mock.cts", "*.mock.tsx", "*.e2e.js", "*.e2e.mjs", "*.e2e.cjs", "*.e2e.jsx", "*.e2e.ts", "*.e2e.mts", "*.e2e.cts", "*.e2e.tsx", ]