mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
fix(pre-push-safety-net): take the operation symbol from the call (M1b 12.1)
Admission extracted everything before the first parenthesis, so the operation for `const preTax = eval(req.body.preTax)` was `const preTax = eval`. No real declaration can enumerate every assignment a codebase might write, so admission would have rejected genuine findings on operation_semantics_mismatch. The M1b demonstration only passed because its declaration listed the assignment spellings. The symbol is now the trailing qualified callee. Receiver spelling stays part of it, so `$wpdb->prepare` and `prepare` remain different operations and a declaration cannot widen from one to the other. A spelling that is not a qualified name, such as a dynamic `handlers[name](x)`, yields the statement text, matches no declaration and stays ineligible exactly as before. Controls cover the assigned call, the receiver distinction, the dynamic spelling, and that a declaration for a different operation still does not cover it. The recorded-veto report reads the same helper instead of repeating the extraction. 1,403 tests passed, nine skipped. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5
parent
6653b6aca8
commit
4ee31142ab
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import asdict, dataclass, replace
|
||||
@@ -363,6 +364,30 @@ def _blocking_degradations(scan: ModelScanResult) -> tuple[str, ...]:
|
||||
return tuple(str(d.get("reason")) for d in scan.degradations if str(d.get("reason")) not in DECLARED_EXCLUSION_GAPS)
|
||||
|
||||
|
||||
# A qualified callee: an identifier, optionally reached through `.`, `->` or `::`. Receiver
|
||||
# spelling is part of the symbol, so `$wpdb->prepare` and `prepare` stay different operations.
|
||||
_CALLEE = re.compile(r"[A-Za-z_$\\][\w$\\]*(?:\s*(?:\.|->|::)\s*[A-Za-z_$\\][\w$\\]*)*\s*$")
|
||||
|
||||
|
||||
def operation_symbol(operation: str) -> str:
|
||||
"""The operation a declaration can name, taken from the call rather than the statement.
|
||||
|
||||
Everything before the first parenthesis is the statement, not the operation: for
|
||||
`const preTax = eval(req.body.preTax)` that is `const preTax = eval`, and a declaration
|
||||
naming `eval` would not match it. No real declaration can enumerate every assignment a
|
||||
codebase might write, so admission rejected genuine findings on `operation_semantics_mismatch`.
|
||||
|
||||
Only the trailing qualified callee is the symbol. A spelling that is not a qualified name,
|
||||
such as a dynamic `handlers[name](x)`, yields the statement text unchanged, which matches no
|
||||
declaration and so stays ineligible exactly as it did before.
|
||||
"""
|
||||
prefix = operation.partition("(")[0].strip()
|
||||
found = _CALLEE.search(prefix)
|
||||
if found is None:
|
||||
return prefix
|
||||
return re.sub(r"\s*(\.|->|::)\s*", r"\1", found.group(0).strip())
|
||||
|
||||
|
||||
def _owning_question(gap: str) -> str | None:
|
||||
"""The question id a gap is attributed to, or None when nothing attributed it."""
|
||||
tail = gap.rsplit(":", 1)[-1]
|
||||
@@ -902,7 +927,7 @@ def admit_candidates(candidates: Sequence[AdmissionCandidate], *, capabilities:
|
||||
if cap is not None:
|
||||
# These are existing graph operation spellings, not source scanning.
|
||||
# A declaration may narrow an entailed claim; it cannot create one.
|
||||
symbol = op.operation.partition("(")[0].strip()
|
||||
symbol = operation_symbol(op.operation)
|
||||
if symbol not in cap.operation_symbols:
|
||||
reasons.append("operation_semantics_mismatch")
|
||||
consequence = _grounded_template(cap.consequence_template, op, key.context)
|
||||
|
||||
@@ -37,6 +37,7 @@ from openultrasast.push.policy import (
|
||||
_grounded_template,
|
||||
admit_candidates,
|
||||
operation_provenance,
|
||||
operation_symbol,
|
||||
)
|
||||
|
||||
FLAG = "--experimental-record-vetoes"
|
||||
@@ -369,7 +370,7 @@ def _evaluation(delta: CandidateDelta, declarations: Sequence[CapabilityAdmissio
|
||||
return None
|
||||
key = CapabilityKey(op.question.language, "unspecified", "unspecified", delta.family, op.mechanism, "unreviewed", semantics)
|
||||
matching = [declaration for declaration in declarations if declaration.key == key]
|
||||
symbol = op.operation.partition("(")[0].strip()
|
||||
symbol = operation_symbol(op.operation)
|
||||
unavailable: list[str] = []
|
||||
consequence = repair = None
|
||||
declaration = matching[0] if len(matching) == 1 else None
|
||||
|
||||
Reference in New Issue
Block a user