mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-19 04:59:28 +00:00
feat(model-grounded-detection): 4.2 constant abstraction, the third arbiter form
A configuration bug has no flow to follow and no guard to dominate.
CORS(app, origins="*") is dangerous because of the value it is SET TO, so the
arbiter evaluates the setting's arguments against the closed permissive set in
the obligation facts -- the third form beside taint reachability and guard
dominance, and the one Req 6.3 names.
ENTAILED a security setting is passed a literal in the permissive set
CORROBORATED the argument is computed, so constant abstraction cannot decide
it -- that residual is the judge's, never silently "safe"
(none) every literal is outside the set, or the call is not a setting
Two Joern node-shape bugs, both found by running it and both of the same class
as group 2's receiver bug -- the graph's indexing is not what the source text
suggests:
* a keyword argument (`origins="*"`) is an assignment-shaped node, so
isLiteral is false on the argument itself and the literal sits in its
subtree; literals are collected from the argument's AST now.
* Joern indexes the receiver at 0, positional arguments from 1, and NAMED
arguments at -1. Settings are configured overwhelmingly by keyword, so
argumentIndexGt(0) -- correct when counting arity for the taint shape test
-- dropped exactly the arguments that matter and every keyword-configured
setting read as "computed".
Live, deterministic, zero model calls:
create_app -> model_entailed CORS(app, origins = "*"): permissive literal "*"
safe_app -> no finding (origins = "https://app.example")
All three arbiter forms now work against a real CPG: taint reachability for the
flow families, guard dominance for absence, constant abstraction for config.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
co-authored by
Claude Opus 5
parent
fec67253f3
commit
d9ceeb6a09
@@ -122,6 +122,35 @@ class DominanceSpec:
|
||||
dischargers: tuple[str, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ConfigSpec:
|
||||
"""What a configuration family's constant-abstraction query matches on, for one language.
|
||||
|
||||
A config bug has no flow and no guard: ``CORS(app, origins="*")`` is dangerous because of the value it is
|
||||
*set to*, not because anything reaches it. So the arbiter evaluates the argument against a closed
|
||||
permissive set — the third form beside taint reachability and guard dominance.
|
||||
"""
|
||||
|
||||
family: str
|
||||
language: str
|
||||
settings: tuple[str, ...]
|
||||
permissive: tuple[str, ...]
|
||||
|
||||
|
||||
def config_specs(*, language: str, facts: ObligationFacts | None = None) -> Mapping[str, ConfigSpec]:
|
||||
"""One ``ConfigSpec`` per configuration family, from the obligation facts' security-setting operations.
|
||||
|
||||
``permissive_values`` on a ``non_permissive_value`` discharger is the closed set of literals that leave a
|
||||
setting open — the one place in the facts where a value, rather than a call, is the evidence.
|
||||
"""
|
||||
scoped = (facts if facts is not None else load_obligation_facts()).for_language(language)
|
||||
settings = tuple(sorted({call for fact in scoped.operations if fact.kind == "security_setting" for call in fact.calls}))
|
||||
permissive = tuple(sorted({value for fact in scoped.dischargers for value in fact.permissive_values}))
|
||||
if not settings or not permissive:
|
||||
return {}
|
||||
return {"config_secrets": ConfigSpec(family="config_secrets", language=language, settings=settings, permissive=permissive)}
|
||||
|
||||
|
||||
def taint_specs(
|
||||
*,
|
||||
language: str,
|
||||
@@ -184,6 +213,8 @@ def dominance_specs(*, language: str, facts: ObligationFacts | None = None) -> M
|
||||
|
||||
__all__ = [
|
||||
"GUARD_KINDS",
|
||||
"ConfigSpec",
|
||||
"config_specs",
|
||||
"GUARD_PATTERNS",
|
||||
"DominanceSpec",
|
||||
"TaintSpec",
|
||||
|
||||
Reference in New Issue
Block a user