mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(prover): delivery D: control flow decides dominance; the trace carries each guard's relation, effect and the operation's control context
Opus agent delivery under spec access-control-prover, requirement 10. `semantic/cfg.py` builds a control-flow graph per function from the syntax tree (if/else, switch arms, loops, try/catch/finally, return, throw, break, continue, exit, assert) with dominators and a per-line control context, and leaves a function without a graph rather than a wrong one. `obligations/cfg_dominance.py::CfgDominance` discharges a statement guard only when it lies on every path to the operation and the operation is unreachable from its failing arm; a predicate whose result nothing consumes is `value_only` and never discharges; router, decorator and earlier-hop witnesses behave as before; without a graph the order rule applies and `cfg_unavailable` is recorded. The trace's guards carry `relation` and `effect`, the trace carries `operation_context`; Java parameter lists carry names only; a handler-named operation's arguments are the handler's parameters. The checker's default stays order-based. Smoke on the 23 fix changes: 15 caught, 55 of 93 locations, precision 15 of 15, no failure, no change over 120 s (unchanged from delivery B). Gate: 3575 tests pass; the reserved-repository guard passes on the export. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
6c3d4a7d72
commit
03b114cbeb
@@ -295,3 +295,72 @@ def test_remix_missing_kinds_and_public_policy(tmp_path, public, expected_missin
|
||||
change["files"] = [{"path": "app/routes/items.ts", "head_lines": [[1, 1]]}]
|
||||
result = prove_change(root, change)
|
||||
assert [finding["missing"] for finding in result.findings] == expected_missing
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"guard,findings,relation,effect",
|
||||
[
|
||||
(
|
||||
[" if (shared) {", " Jenkins.get().checkPermission(Jenkins.ADMINISTER);", " }"],
|
||||
1,
|
||||
"some_paths",
|
||||
"throws",
|
||||
),
|
||||
(
|
||||
[" if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {", ' throw new AccessDeniedException("denied");', " }"],
|
||||
0,
|
||||
"all_paths",
|
||||
"throws",
|
||||
),
|
||||
([" boolean allowed = Jenkins.get().hasPermission(Jenkins.ADMINISTER);"], 1, "all_paths", "value_only"),
|
||||
],
|
||||
)
|
||||
def test_control_flow_decides_the_verdict_and_the_trace(tmp_path, guard, findings, relation, effect):
|
||||
body = "\n ".join(guard)
|
||||
root, change = repository(
|
||||
tmp_path,
|
||||
f"""class X extends Descriptor<Thing> {{
|
||||
public Object doTestConnection(@QueryParameter boolean shared) {{
|
||||
{body}
|
||||
return CredentialsProvider.lookupCredentials(Thing.class);
|
||||
}}
|
||||
}}""",
|
||||
)
|
||||
result = prove_change(root, change)
|
||||
assert len(result.findings) == findings
|
||||
assert result.coverage.operations_discharged == 1 - findings
|
||||
trace = result.traces[0]
|
||||
assert trace["parameters"] == ["shared"]
|
||||
assert [(g["relation"], g["effect"]) for g in trace["hops"][0]["guards_before_call"]] == [(relation, effect)]
|
||||
assert not [item for item in result.degradations if item.get("reason") == "cfg_unavailable"]
|
||||
if relation == "some_paths":
|
||||
assert trace["missing"] == "path_guard" and trace["operation_context"]["conditions"] == []
|
||||
|
||||
|
||||
def test_handler_operation_trace_carries_handler_parameters(tmp_path):
|
||||
root, change = repository(
|
||||
tmp_path,
|
||||
"""class X extends Descriptor<Thing> {
|
||||
public Object doCheckValue(@QueryParameter String value, @AncestorInPath Item item) {
|
||||
return 1;
|
||||
}
|
||||
}""",
|
||||
)
|
||||
result = prove_change(root, change)
|
||||
assert result.traces[0]["arguments"] == [["value", "request_input"], ["item", "request_input"]]
|
||||
|
||||
|
||||
def test_unmodelled_function_is_judged_by_order_and_recorded(tmp_path):
|
||||
root, change = repository(
|
||||
tmp_path,
|
||||
"""class X extends Descriptor<Thing> {
|
||||
public Object doTestConnection() {
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
outer: for (;;) { break outer; }
|
||||
return CredentialsProvider.lookupCredentials(Thing.class);
|
||||
}
|
||||
}""",
|
||||
)
|
||||
result = prove_change(root, change)
|
||||
assert not result.findings and result.coverage.operations_discharged == 1
|
||||
assert any(item.get("reason") == "cfg_unavailable" and item.get("function") == "doTestConnection" for item in result.degradations)
|
||||
|
||||
Reference in New Issue
Block a user