mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
spec(access-control-prover): control flow decides dominance; the trace carries each guard's relation, effect and the operation's control context
Requirement 10, the structural control-flow graph per function and CfgDominance behind the existing protocol, tasks 4.4 and 4.5. Textual order stays the checker's default; the prover selects control flow. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
12d09faeed
commit
52ecbb76ab
@@ -220,6 +220,20 @@ from its own request-derived values keeps `request_input`; anything the walk can
|
|||||||
inter-procedural dataflow beyond the call sites on the path. The trace never carries language, framework,
|
inter-procedural dataflow beyond the call sites on the path. The trace never carries language, framework,
|
||||||
repository, size or count fields.
|
repository, size or count fields.
|
||||||
|
|
||||||
|
### cfg and CfgDominance (owned here)
|
||||||
|
`src/openultrasast/semantic/cfg.py`: `FunctionCfg.from_cst(node, language)` builds basic blocks and edges from
|
||||||
|
the tree-sitter syntax tree of one function (blocks, if/else, switch arms, loops with back edges, try/catch/
|
||||||
|
finally, return, throw, break, continue, exit) and computes dominators (`dominates(a_line, b_line)`) and the
|
||||||
|
control context of a line (`context(line) -> ControlContext(conditions, in_loop, in_try, catches, on_exception_path)`).
|
||||||
|
`src/openultrasast/semantic/obligations/cfg_dominance.py::CfgDominance` implements the `Dominance` protocol:
|
||||||
|
a statement witness dominates when its block dominates the operation's block and its failure effect is
|
||||||
|
`throws`, `returns` or `exits` (the guard's condition leads to an exit on the failing arm; a bare boolean
|
||||||
|
whose value no branch or exit consumes before the operation is `value_only`); router and decorator scopes
|
||||||
|
dominate as before; along a path record, a witness on an earlier hop dominates as before. Without a graph for
|
||||||
|
a function, `CfgDominance` falls back to `OrderDominance` and records `cfg_unavailable`.
|
||||||
|
The trace's `Guard` gains `relation` (`all_paths`, `some_paths`, `none`, `after`) and `effect`
|
||||||
|
(`throws`, `returns`, `exits`, `value_only`); the trace gains `operation_context: ControlContext`.
|
||||||
|
|
||||||
### obligations.enforcement
|
### obligations.enforcement
|
||||||
```python
|
```python
|
||||||
def witnesses(*, root: Path, texts: Mapping[str, str], entries: Sequence[EntryPointRecord], language: str) -> list[Discharge]
|
def witnesses(*, root: Path, texts: Mapping[str, str], entries: Sequence[EntryPointRecord], language: str) -> list[Discharge]
|
||||||
|
|||||||
@@ -204,3 +204,27 @@ trace with its verdict, so that a decision model sees the security-relevant stru
|
|||||||
6. The hunt defect of an unguarded pair shall reference its trace, so the proof arm and the pre-push admission
|
6. The hunt defect of an unguarded pair shall reference its trace, so the proof arm and the pre-push admission
|
||||||
can show the path.
|
can show the path.
|
||||||
|
|
||||||
|
### Requirement 10: Control flow decides dominance, and the trace says so
|
||||||
|
**Objective:** As the maintainer, I want a guard to count only when control flow makes the operation unreachable
|
||||||
|
without it, so that a check in a branch the attacker can avoid, or one whose result is ignored, is not a discharge.
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
1. The prover shall build a structural control-flow graph per function on a path from the syntax tree it already
|
||||||
|
parses: statement blocks, if and else branches, switch arms, loops, try, catch and finally, return, throw,
|
||||||
|
break and continue, and the function's exit; with no grammar for a construct the function shall be marked
|
||||||
|
`cfg_unavailable` and judged by order with that degradation recorded.
|
||||||
|
2. A guard shall dominate an operation when every path from the function's entry to the operation passes the
|
||||||
|
guard's statement (dominators over the graph); the trace shall record each guard's relation to the operation
|
||||||
|
as `all_paths`, `some_paths`, `none` or `after`.
|
||||||
|
3. The trace shall record each guard's failure effect: `throws`, `returns`, `exits` (break, continue, exit call),
|
||||||
|
or `value_only` when the guard's result is not consumed by a branch or an exit before the operation; a
|
||||||
|
`value_only` guard shall not discharge.
|
||||||
|
4. The trace shall record the operation's control context: the branch conditions it sits under in order (the
|
||||||
|
guard's own condition when it is one), whether it is inside a loop, inside a try whose catch swallows the
|
||||||
|
guard's exception type, or on an exception path.
|
||||||
|
5. Router and decorator witnesses shall keep their scope semantics (they dominate the handler as a whole).
|
||||||
|
6. The parameter list of a Java entry shall carry parameter names only, never annotation names; the arguments
|
||||||
|
of a handler-named operation shall be the handler's own parameters with their provenance.
|
||||||
|
7. The existing order-based dominance shall remain the default of the checker; the prover shall select the
|
||||||
|
control-flow dominance, and the checker's tests shall stay unchanged.
|
||||||
|
|
||||||
|
|||||||
@@ -68,18 +68,34 @@
|
|||||||
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 6.2, 7.1, 8.1, 8.2_
|
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 6.2, 7.1, 8.1, 8.2_
|
||||||
- _Depends: 1.1, 2.1, 3.1, 3.2_
|
- _Depends: 1.1, 2.1, 3.1, 3.2_
|
||||||
|
|
||||||
- [ ] 4.2 `semantic/obligations/trace.py`: `Guard`, `Hop`, `Trace`, `text()` and `record()`; argument provenance through the IR's binds within each hop, forwarded along the path
|
- [x] 4.2 `semantic/obligations/trace.py`: `Guard`, `Hop`, `Trace`, `text()` and `record()`; argument provenance through the IR's binds within each hop, forwarded along the path
|
||||||
- Done when `tests/test_obligation_trace.py` serialises a three-hop fixture deterministically (text and JSON),
|
- Done when `tests/test_obligation_trace.py` serialises a three-hop fixture deterministically (text and JSON),
|
||||||
traces an argument from an entry parameter to `request_input`, from a literal to `constant`, from an identity
|
traces an argument from an entry parameter to `request_input`, from a literal to `constant`, from an identity
|
||||||
source to `authenticated_context`, and marks an unfollowable value `unknown`
|
source to `authenticated_context`, and marks an unfollowable value `unknown`
|
||||||
- _Requirements: 9.1, 9.3, 9.4, 9.5_
|
- _Requirements: 9.1, 9.3, 9.4, 9.5_
|
||||||
- _Boundary: obligations.trace_
|
- _Boundary: obligations.trace_
|
||||||
- [ ] 4.3 The prover emits a trace per examined pair (discharged and unguarded), the record and the defect carry them
|
- [x] 4.3 The prover emits a trace per examined pair (discharged and unguarded), the record and the defect carry them
|
||||||
- Done when `tests/test_hunt_prover.py` shows a discharged pair and an unguarded pair each produce one trace with
|
- Done when `tests/test_hunt_prover.py` shows a discharged pair and an unguarded pair each produce one trace with
|
||||||
the verdict, the unguarded finding references its trace, and `prover_arm` records carry `traces`
|
the verdict, the unguarded finding references its trace, and `prover_arm` records carry `traces`
|
||||||
- _Requirements: 9.1, 9.2, 9.5, 9.6_
|
- _Requirements: 9.1, 9.2, 9.5, 9.6_
|
||||||
- _Depends: 4.2_
|
- _Depends: 4.2_
|
||||||
|
|
||||||
|
- [ ] 4.4 `semantic/cfg.py`: structural control-flow graph per function from the syntax tree, dominators, control context (P)
|
||||||
|
- Java, PHP, Python, JavaScript and TypeScript constructs of requirement 10.1; `cfg_unavailable` otherwise
|
||||||
|
- Done when `tests/test_semantic_cfg.py` shows, per language, that a guard before an unconditional return
|
||||||
|
dominates, a guard inside an if-branch does not dominate a statement after the branch, a guard inside a
|
||||||
|
loop body does not dominate a statement after the loop, a try/catch is represented, and context reports
|
||||||
|
conditions and loops
|
||||||
|
- _Requirements: 10.1, 10.2, 10.4_
|
||||||
|
- _Boundary: semantic.cfg_
|
||||||
|
- [ ] 4.5 `obligations/cfg_dominance.py::CfgDominance`, failure effects, and the trace's control components; the prover selects it; Java parameter names and handler-operation arguments cleaned
|
||||||
|
- Done when `tests/test_obligation_cfg_dominance.py` shows a value-only predicate does not discharge while the
|
||||||
|
same predicate guarding a throw does; a branch-only guard yields `some_paths` and no discharge; the prover
|
||||||
|
smoke on the 23 fixes keeps or raises its recall; `tests/test_mapping_frameworks.py` shows Java parameter
|
||||||
|
names without annotations
|
||||||
|
- _Requirements: 10.2, 10.3, 10.4, 10.5, 10.6, 10.7_
|
||||||
|
- _Depends: 4.4_
|
||||||
|
|
||||||
- [ ] 5. The arm
|
- [ ] 5. The arm
|
||||||
- [x] 5.1 `benchmarks/valid/prover_arm.py`: run over a pool slice with checkpoints, summarise with the valid metrics
|
- [x] 5.1 `benchmarks/valid/prover_arm.py`: run over a pool slice with checkpoints, summarise with the valid metrics
|
||||||
- Record envelope copied from `hunter_arm`; instrument fields; resume; `summary.json` with recall (pushes and
|
- Record envelope copied from `hunter_arm`; instrument fields; resume; `summary.json` with recall (pushes and
|
||||||
@@ -152,3 +168,16 @@
|
|||||||
All 23 executions completed, zero errors; 16 changes checked operations and seven remained unchecked.
|
All 23 executions completed, zero errors; 16 changes checked operations and seven remained unchecked.
|
||||||
It materialised only changed source files in temporary directories, so it is not a full-repository
|
It materialised only changed source files in temporary directories, so it is not a full-repository
|
||||||
recall measurement. The maintainer-owned task 6.1 and release readiness remain unclaimed.
|
recall measurement. The maintainer-owned task 6.1 and release readiness remain unclaimed.
|
||||||
|
|
||||||
|
- Delivery C (2026-10-14) implements tasks 4.2 and 4.3: ordered traces for every examined
|
||||||
|
path/operation pair, argument origins through local bindings and forwarded parameters, traces in
|
||||||
|
prover results and arm records, and trace references on unguarded findings. Existing checker
|
||||||
|
decisions and coverage accounting are unchanged. Declared route access is retained in the trace.
|
||||||
|
- Delivery C review used the main context because the brief prohibits model calls. Verdict: APPROVED
|
||||||
|
for tasks 4.2 and 4.3. Regression cases cover unresolved collections, overwritten bindings,
|
||||||
|
source-name boundaries, missing IR arguments, method parameter forwarding and return-order guards.
|
||||||
|
- Fresh delivery C verification with UV_NO_SYNC=1 and UV_CACHE_DIR=/tmp/ousast-uv: the exact
|
||||||
|
requested static gate passed (format, Ruff, mypy over 179 source files); the exact pytest gate
|
||||||
|
passed with 102 passed, zero failed, exit 0. The initial trace test failed collection because
|
||||||
|
trace.py did not yet exist; additional regression tests failed before their fixes. No
|
||||||
|
test_search_* test, network request or model call was made. Changes remain uncommitted.
|
||||||
|
|||||||
Reference in New Issue
Block a user