mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-19 04:59:28 +00:00
The census recorded this family completing 44 questions on a Node application and finding
nothing, which is what a client-only vocabulary must do there: all six declared sinks were DOM
sinks. NodeGoat's cross-site scripting is server-side.
Decision: the family asks about a value reaching a rendering call, and states in its own
declaration that it cannot decide template-mediated escaping. Whether res.render is safe depends
on the engine's autoescape setting and on the template's markup, neither of which is at the
call, so res.render is not a sink and the family's limits field says so. A new CWE-79 sink
covers res.write, res.end, res.send and the response.* spellings; res.json stays out because an
application/json body is not a document.
Template auto-escaping is claimed where its evidence is, as a configuration defect:
swig.setDefaults, nunjucks.configure and marked.setOptions are security settings now.
It establishes a finding: app/routes/research.js:25, model_entailed,
`req.query.url + req.query.symbol -> res.write(body)`. The handler fetches a URL the caller
chooses and writes the response body into a text/html response, so the document served from
this origin is the attacker's. The record states how coarse the step is: the path from url to
body runs through the client's callback parameter, which the graph treats as reachable from the
call's arguments, so the conclusion rests on the call's semantics rather than on what the graph
established. Five of seven families now establish findings.
Four defects were found on the way, three of them false positives this change would otherwise
have shipped.
- The permissive-value set was shared across every setting, while a boolean's polarity belongs
to the setting: true leaves the door open in cors({origin: true}) and closes it in
swig.setDefaults({autoescape: true}). Adding the template engines would have reported the fix
as the defect on the line that offers it. ConfigSpec now carries per-setting values, and
Jinja2's Environment moved out of Python's shared list for the same latent reason.
- config.sc matched a dotted setting on its trailing segment alone, so marked.setOptions would
have claimed an editor's setOptions. dominance.sc already required the qualifier.
- taint.sc asked its flow query of every argument including the receiver at index 0. On a
response sink the receiver is the response object, so res.end() -- which takes no data -- was
reported exactly like the res.write(body) two lines above it. Flows now use the real
arguments, as arity already did.
- The arbiter sorted a file's settings by line number as TEXT, so line 126 preceded line 78 and
which setting a file reported depended on the spelling of its line numbers.
Template escaping disabled at server.js:135 is detected, measured model_entailed against the
real query and the real arbiter, and still not reported: the arbiter returns one verdict per
question and that file has an earlier permissive setting. Task 14.6 carries it rather than
hiding it.
1,415 tests pass, thirteen skipped; nine Joern-gated query tests pass in the image.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
129 lines
5.4 KiB
TOML
129 lines
5.4 KiB
TOML
# Closed family taxonomy (learning-harness, Req 1). A family is the ONLY key that routes work and
|
|
# scores a pair; CWE ids and mechanism ids are attributes looked up through a family and are never
|
|
# keys themselves. `verifier` says what can confirm a claim in this family: a canary oracle in the
|
|
# sandbox, static corroboration from another checker, or nothing (the claim stays a suspicion).
|
|
# `unknown` carries the generic mechanisms and the deliberately out-of-scope weaknesses, so a row we
|
|
# have considered and declined is visible instead of silently unresolved. Bump `version` on any change.
|
|
version = "1"
|
|
|
|
[[family]]
|
|
id = "injection"
|
|
description = "Untrusted input reaches an interpreter: SQL, OS command, code, expression or server-side template."
|
|
cwes = ["CWE-77", "CWE-78", "CWE-88", "CWE-89", "CWE-90", "CWE-91", "CWE-94", "CWE-95", "CWE-564", "CWE-917", "CWE-943", "CWE-1336"]
|
|
mechanisms = []
|
|
verifier = "canary"
|
|
|
|
[[family]]
|
|
id = "path"
|
|
description = "Untrusted input reaches a filesystem path: traversal, unsafe join, file inclusion."
|
|
cwes = ["CWE-22", "CWE-23", "CWE-36", "CWE-73", "CWE-98", "CWE-99"]
|
|
mechanisms = ["path_join_user_input"]
|
|
verifier = "canary"
|
|
|
|
[[family]]
|
|
id = "deserialization"
|
|
description = "Untrusted data is deserialized into objects the process then trusts."
|
|
cwes = ["CWE-502"]
|
|
mechanisms = []
|
|
verifier = "canary"
|
|
|
|
[[family]]
|
|
id = "access_control"
|
|
description = "An operation on a protected resource is reached without the guard or ownership constraint its siblings carry."
|
|
cwes = ["CWE-284", "CWE-285", "CWE-306", "CWE-425", "CWE-566", "CWE-639", "CWE-862", "CWE-863", "CWE-1220"]
|
|
mechanisms = [
|
|
"identity_from_request_body",
|
|
"missing_auth_guard",
|
|
"unconstrained_protected_read",
|
|
"unconstrained_protected_write",
|
|
"unguarded_privileged_action",
|
|
]
|
|
verifier = "static"
|
|
|
|
[[family]]
|
|
id = "output_encoding"
|
|
description = "Untrusted data reaches a rendered document or a header without encoding: cross-site scripting, response splitting."
|
|
cwes = ["CWE-79", "CWE-80", "CWE-83", "CWE-113", "CWE-116"]
|
|
mechanisms = []
|
|
verifier = "none"
|
|
limits = """Models a value reaching a rendering CALL -- a DOM sink, or a response body written directly. It cannot decide template-mediated escaping, which is the commonest shape a server-rendered application has: whether `res.render` is safe depends on the template engine's autoescape configuration and on the template's own markup, neither of which is at the call. That setting is claimed instead as a configuration defect, where a permissive literal can be read: `swig.setDefaults`, `nunjucks.configure` and Jinja2's `Environment` are security settings in the obligation facts, and turning escaping off is reported by config_secrets. Response splitting (CWE-113) is claimed by no sink here at all, because modern Node rejects a newline in a header value and the claim would be about an engine this project does not model. A scan of a server-rendered application that reports nothing from this family has established that no tainted value reached a modelled rendering call, and nothing about whether its templates escape."""
|
|
|
|
[[family]]
|
|
id = "untrusted_destination"
|
|
description = "Untrusted input controls where a request or a redirect goes: server-side request forgery and open redirect."
|
|
cwes = ["CWE-441", "CWE-601", "CWE-611", "CWE-918"]
|
|
mechanisms = []
|
|
verifier = "none"
|
|
|
|
[[family]]
|
|
id = "prototype"
|
|
description = "Untrusted keys reach an object merge or attribute assignment and change behaviour other code relies on."
|
|
cwes = ["CWE-471", "CWE-915", "CWE-1321"]
|
|
mechanisms = ["prototype_pollution"]
|
|
verifier = "none"
|
|
|
|
[[family]]
|
|
id = "config_secrets"
|
|
description = "A security setting ships permissive or a credential ships in the artifact: CORS, debug, cookie flags, weak crypto, hardcoded secrets."
|
|
cwes = [
|
|
"CWE-16",
|
|
"CWE-215",
|
|
"CWE-259",
|
|
"CWE-311",
|
|
"CWE-312",
|
|
"CWE-315",
|
|
"CWE-326",
|
|
"CWE-327",
|
|
"CWE-346",
|
|
"CWE-489",
|
|
"CWE-522",
|
|
"CWE-614",
|
|
"CWE-798",
|
|
"CWE-942",
|
|
"CWE-1004",
|
|
]
|
|
mechanisms = ["config_sink_weak_literal", "permissive_default", "secret_in_client"]
|
|
verifier = "none"
|
|
|
|
[[family]]
|
|
id = "memory"
|
|
description = "Misuse of unbounded string and format APIs in C and C++: a tainted value reaching gets, strcpy, strcat, sprintf or a non-literal format string."
|
|
limits = """Models CALLS, not arithmetic. It cannot decide an overflow expressed as an index, a pointer offset, or a memcpy whose size is computed -- which is how most real C overflows are written, and how every one of libpng's is. A scan of C code that reports nothing from this family has established that no tainted value reached a modelled string API, and nothing at all about its buffer arithmetic."""
|
|
cwes = [
|
|
"CWE-119",
|
|
"CWE-120",
|
|
"CWE-121",
|
|
"CWE-122",
|
|
"CWE-125",
|
|
"CWE-126",
|
|
"CWE-131",
|
|
"CWE-134",
|
|
"CWE-190",
|
|
"CWE-191",
|
|
"CWE-401",
|
|
"CWE-415",
|
|
"CWE-416",
|
|
"CWE-476",
|
|
"CWE-680",
|
|
"CWE-770",
|
|
"CWE-787",
|
|
"CWE-843",
|
|
]
|
|
mechanisms = [
|
|
"fixed_buffer_capacity",
|
|
"null_deref_unguarded",
|
|
"signed_size_operand",
|
|
"type_confusion",
|
|
"uaf",
|
|
"unchecked_alloc_size",
|
|
"unchecked_length_copy",
|
|
]
|
|
verifier = "none"
|
|
|
|
[[family]]
|
|
id = "unknown"
|
|
description = "No family admitted: a mechanism that spans families, or a weakness this project deliberately does not target."
|
|
cwes = ["CWE-20", "CWE-835"]
|
|
mechanisms = ["container_taint", "cross_artifact", "other", "redos", "source_reaches_sink", "validation_strength"]
|
|
verifier = "none"
|