feat(learn): excerpts and the example store (decision-engine 6.1)

learn/excerpt.py: the labelled function at its pin as numbered lines, bounded
(80 lines / 4,000 chars for candidates, 40 / 2,000 for memory examples),
centred on focus lines; advisory ids anywhere and comments with security
wording (labels.security_reason, multilingual) become <redacted>; owner,
owner/name, path and commit ids are replaced; a delta unit appends the
base->head diff (<= 40 lines). C/C++ and other brace languages without a
repo-facts declaration pattern use a brace matcher (346 c_cpp pair rows
had no excerpt without it).

learn/examples.py + `ousast learn memory build`: label + feature record +
excerpt -> `example` rows (no rationale), excerpts as content-addressed
blobs; refuses origin: user and sources outside sources.toml (exit 2);
conditional labels skipped; exit 2 when >10% of a source's recorded
candidates have no excerpt. A fixed side that was read but no longer
declares the labelled function is counted apart (absent_fixed_side): a
label defect, not an unread clone.

plane/memory.py: `example` in KINDS; put_blob/get_blob/blob_names on the
MemoryStore base, so FileStore and MinioStore share them (tested on both,
MinIO through the fake object client).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-10-01 09:45:17 +02:00
co-authored by Claude Opus 5.5
parent 0f9318f58b
commit 7b81c9ac4e
9 changed files with 1094 additions and 9 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ a model or embedding call.
- _Requirements: 1.1_ - _Requirements: 1.1_
- [ ] 6. Program, local memory and compilation (design sections 4-5) - [ ] 6. Program, local memory and compilation (design sections 4-5)
- [ ] 6.1 Excerpts and the example store (no model) - [x] 6.1 Excerpts and the example store (no model)
- `learn/excerpt.py` (function span at the label's pin, bounds 80/4,000 for candidates and 40/2,000 for - `learn/excerpt.py` (function span at the label's pin, bounds 80/4,000 for candidates and 40/2,000 for
examples, numbered lines, no path/repository/commit, advisory ids and security wording in comments examples, numbered lines, no path/repository/commit, advisory ids and security wording in comments
redacted, delta diff); `learn/examples.py` and `ousast learn memory build` (label + feature record + redacted, delta diff); `learn/examples.py` and `ousast learn memory build` (label + feature record +
@@ -1,7 +1,7 @@
{ {
"modules": 143, "modules": 146,
"counts": { "counts": {
"load_bearing": 136, "load_bearing": 139,
"standalone_capability": 7 "standalone_capability": 7
}, },
"orphaned": [], "orphaned": [],
@@ -352,6 +352,9 @@
"reason": "reachable from gate, map_gate, pair_gate or the CLI", "reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [ "importers": [
"cli", "cli",
"learn.cli",
"learn.examples",
"learn.excerpt",
"learn.features", "learn.features",
"learn.roles", "learn.roles",
"plane.tasks.features", "plane.tasks.features",
@@ -359,11 +362,36 @@
"plane.tasks.roles" "plane.tasks.roles"
] ]
}, },
{
"module": "learn.cli",
"classification": "load_bearing",
"reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [
"cli"
]
},
{
"module": "learn.examples",
"classification": "load_bearing",
"reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [
"learn.cli"
]
},
{
"module": "learn.excerpt",
"classification": "load_bearing",
"reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [
"learn.examples"
]
},
{ {
"module": "learn.features", "module": "learn.features",
"classification": "load_bearing", "classification": "load_bearing",
"reason": "imported by plane.tasks.features", "reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [ "importers": [
"learn.excerpt",
"plane.tasks.features" "plane.tasks.features"
] ]
}, },
@@ -372,13 +400,16 @@
"classification": "load_bearing", "classification": "load_bearing",
"reason": "reachable from gate, map_gate, pair_gate or the CLI", "reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [ "importers": [
"cli" "cli",
"learn.cli",
"learn.examples",
"learn.excerpt"
] ]
}, },
{ {
"module": "learn.roles", "module": "learn.roles",
"classification": "load_bearing", "classification": "load_bearing",
"reason": "imported by learn.features, plane.tasks.features, plane.tasks.roles", "reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [ "importers": [
"learn.features", "learn.features",
"plane.tasks.features", "plane.tasks.features",
@@ -706,6 +737,7 @@
"improve", "improve",
"improve.evolve", "improve.evolve",
"improve.memory", "improve.memory",
"learn.examples",
"learn.labels", "learn.labels",
"model.candidates", "model.candidates",
"pair_gate", "pair_gate",
@@ -719,6 +751,8 @@
"importers": [ "importers": [
"cli", "cli",
"improve.memory", "improve.memory",
"learn.cli",
"learn.examples",
"plane.egress", "plane.egress",
"plane.engine_alerts", "plane.engine_alerts",
"plane.generate", "plane.generate",
@@ -792,6 +826,8 @@
"reason": "harnessx-removal Req 6.1/6.4: the memory store (file, MinIO), ingest and fact reuse; `ousast plane remember`", "reason": "harnessx-removal Req 6.1/6.4: the memory store (file, MinIO), ingest and fact reuse; `ousast plane remember`",
"importers": [ "importers": [
"improve.memory", "improve.memory",
"learn.cli",
"learn.examples",
"plane.generate", "plane.generate",
"plane.tasks.loop", "plane.tasks.loop",
"plane.tasks.remember" "plane.tasks.remember"
@@ -828,6 +864,7 @@
"reason": "ai-service-plane Req 4.1: the task entrypoint package the runner imports by name; runner integration pending (task 3)", "reason": "ai-service-plane Req 4.1: the task entrypoint package the runner imports by name; runner integration pending (task 3)",
"importers": [ "importers": [
"cli", "cli",
"learn.excerpt",
"learn.features", "learn.features",
"learn.labels", "learn.labels",
"learn.roles", "learn.roles",
@@ -884,6 +921,7 @@
"classification": "load_bearing", "classification": "load_bearing",
"reason": "ai-service-plane Req 5.1/5.2: source-only functions and callers per file; runner integration pending", "reason": "ai-service-plane Req 5.1/5.2: source-only functions and callers per file; runner integration pending",
"importers": [ "importers": [
"learn.excerpt",
"learn.features", "learn.features",
"learn.labels", "learn.labels",
"learn.roles", "learn.roles",
+4 -1
View File
@@ -44,6 +44,8 @@ from .harness import HarnessRuntime, HarnessTraceWriter, write_harness_config
from .hunter import run_hunter_pool, write_hunter_trajectories from .hunter import run_hunter_pool, write_hunter_trajectories
from .improve import RoundOutcome, run_improvement from .improve import RoundOutcome, run_improvement
from .index import build_code_chunks from .index import build_code_chunks
from .learn.cli import add_commands as add_learn_commands
from .learn.cli import run as run_learn
from .mapping import analyze_entry_points, attach_reachability_hints, ingest_sarif, write_entry_points, write_static_hints from .mapping import analyze_entry_points, attach_reachability_hints, ingest_sarif, write_entry_points, write_static_hints
from .pair_gate import print_pair_metrics from .pair_gate import print_pair_metrics
from .pairs import ( from .pairs import (
@@ -269,6 +271,7 @@ def _main(argv: list[str] | None) -> int:
learn_labels.add_argument("--snapshot", type=Path, help="write the counts-only snapshot (the committable record) here") learn_labels.add_argument("--snapshot", type=Path, help="write the counts-only snapshot (the committable record) here")
learn_labels.add_argument("--include-title", action="store_true", help="the title-tier pairs as well (a sensitivity arm)") learn_labels.add_argument("--include-title", action="store_true", help="the title-tier pairs as well (a sensitivity arm)")
learn_labels.add_argument("--no-assumed-benign", action="store_true", help="skip mining the assumed-benign history source") learn_labels.add_argument("--no-assumed-benign", action="store_true", help="skip mining the assumed-benign history source")
add_learn_commands(learn_sub)
plane = subparsers.add_parser("plane", help="maintainer: the ax-backed service plane on this host's kind cluster") plane = subparsers.add_parser("plane", help="maintainer: the ax-backed service plane on this host's kind cluster")
plane_sub = plane.add_subparsers(dest="plane_command", required=True) plane_sub = plane.add_subparsers(dest="plane_command", required=True)
plane_run = plane_sub.add_parser("run", help="execute a Run manifest on ax; a rerun skips done tasks") plane_run = plane_sub.add_parser("run", help="execute a Run manifest on ax; a rerun skips done tasks")
@@ -440,7 +443,7 @@ def _main(argv: list[str] | None) -> int:
if args.command == "plane": if args.command == "plane":
return _plane(args) return _plane(args)
if args.command == "learn": if args.command == "learn":
return _learn_labels(args) return _learn_labels(args) if args.learn_command == "labels" else run_learn(args)
if args.command == "mcp": if args.command == "mcp":
from .mcp import serve # lazy: keeps the import cycle (mcp -> cli) one-directional from .mcp import serve # lazy: keeps the import cycle (mcp -> cli) one-directional
+69
View File
@@ -0,0 +1,69 @@
"""``ousast learn ...`` beyond ``labels``: the local memory and the compiled program (design sections 4-5).
Host only, maintainer surface. The parser is registered from :mod:`..cli`; every heavy import is lazy. No command
here spends money unless it is given a real client: ``compile``/``evaluate``/``curve`` and ``memory embed`` run under
an explicit ``--budget-usd`` ceiling through the metered clients.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
def add_commands(learn_sub: Any) -> None:
memory = learn_sub.add_parser("memory", help="the decision engine's local memory of labelled examples")
memory_sub = memory.add_subparsers(dest="memory_command", required=True)
build = memory_sub.add_parser("build", help="label + feature record + excerpt -> example rows (no model)")
build.add_argument("--labels", type=Path, required=True, help="labels-<sha>.jsonl written by `ousast learn labels --out`")
build.add_argument("--features", type=Path, action="append", default=[], help="feature records (JSONL with repo, pin); repeatable")
build.add_argument("--sources", type=Path, help="the source list (default: the packaged learn/sources.toml)")
build.add_argument("--root", type=Path, default=Path("."), help="the repository root the source paths are relative to")
build.add_argument("--cache", type=Path, help="clones and checkouts (default ~/.cache/openultrasast)")
build.add_argument("--memory", help="the store (default OUSAST_MEMORY, then results_root()/memory)")
build.add_argument("--profile", choices=("static", "plane"), default="static")
build.add_argument("--dry-run", action="store_true", help="count, write nothing")
def run(args: argparse.Namespace) -> int:
if args.learn_command == "memory" and args.memory_command == "build":
return _memory_build(args)
print(f"learn: unknown command {args.learn_command}", file=sys.stderr)
return 2
def _memory_build(args: argparse.Namespace) -> int:
"""Exit 2 on a refused label (``origin: user``, a source outside the list) or a source with >10% unread excerpts."""
from ..plane.memory import open_store
from .examples import ExampleBuildError, SideReader, build_examples, read_jsonl, records_by_key
from .labels import DEFAULT_CACHE, DEFAULT_SOURCES, load_sources
sources = load_sources(args.sources or DEFAULT_SOURCES)
labels = read_jsonl(args.labels)
store = None if args.dry_run else open_store(args.memory)
rows = [row for path in args.features for row in read_jsonl(path)]
if store is not None:
rows += [r.row for r in store.rows(kind="features")]
reader = SideReader(args.root, args.cache or DEFAULT_CACHE, sources, labels)
try:
build = build_examples(
labels, records_by_key(rows), reader, sources=sources, store=store, profile=args.profile, license_of=reader.license,
function_of=reader.function,
) # fmt: skip
except ExampleBuildError as exc:
print(f"learn memory build: {exc}", file=sys.stderr)
return 2
summary = build.summary()
print(json.dumps(summary, indent=2, sort_keys=True))
if summary["failing_sources"]:
print(
f"learn memory build: more than 10% of the recorded candidates have no excerpt in {summary['failing_sources']}", file=sys.stderr
)
return 2
return 0
__all__ = ["add_commands", "run"]
+315
View File
@@ -0,0 +1,315 @@
"""The example store: the decision engine's local memory (learned-decision-engine design section 4.2, Req 3.2, 3.6).
No model. ``ousast learn memory build`` joins each verified label (``ousast learn labels``) with the feature record of
the same candidate and an excerpt of its function at the label's pin, and writes one ``example`` row per candidate
into the memory store, with the excerpt as a content-addressed blob ``excerpts/<sha>.txt``::
envelope (id, kind=example, repo, pin, run, task, population=<source>, split, image)
+ {candidate, family, language, profile, label, source, group, frameworks, unit, direction, pin_role, weight,
license, x, instruments, roles, excerpt_sha, label_set_digest}
Memory examples carry **no rationale** (code, signals, roles, family and label only). Refusals are loud:
- a row with ``origin: user`` (the opt-in adaptation layer never enters the generalisation memory, Req 7.5) or a
source that ``sources.toml`` does not list stops the build (:class:`ExampleBuildError`);
- conditional rows (``benign_control``, ``assumed_benign``) are skipped until their condition is evaluated;
- a candidate whose excerpt is empty or whose function is not found is never stored; when more than 10% of a
source's recorded candidates have no excerpt the build fails that source (exit 2 in the CLI): an unread clone is
not a small memory. A fixed side whose file was read but no longer declares the labelled function (a pointer
pair whose fix lives in another function) is a label defect, counted apart as ``absent_fixed_side``.
Pair labels have no commit pin (the corpus holds excerpts); their envelope pin is :func:`label_pin`, a stable
40-hex digest of the pair and its side, so the vulnerable and the fixed side stay distinct rows.
"""
from __future__ import annotations
import hashlib
import json
import re
from collections.abc import Callable, Iterable, Mapping, Sequence
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any
from ..plane.memory import MemoryStore, repo_key, row_id
from .excerpt import EXAMPLE, Bounds, delta_diff, excerpt, function_bounds, identities_of
from .labels import Label, Sources
EXAMPLE_KIND = "example"
RUN = "learn-memory"
TASK = "build"
IMAGE = "host"
NO_EXCERPT_LIMIT = 0.10
DERIVED_SOURCES = {"benign_control": "population"} # sources the label builder derives from a listed source
_PIN = re.compile(r"[0-9a-f]{40}")
class ExampleBuildError(RuntimeError):
"""A label the memory must not hold (``origin: user``, a source outside ``sources.toml``) or a source whose
excerpts could not be read."""
def label_pin(label: Mapping[str, Any]) -> str:
"""The envelope pin of a label: its commit, or for a pair (no commit) a stable digest of pair and side."""
pin = str(label.get("pin") or "")
if _PIN.fullmatch(pin):
return pin
subject = f"{label.get('source')}:{label.get('source_ref')}:{label.get('pin_role', 'vulnerable')}"
return hashlib.sha1(subject.encode("utf-8")).hexdigest() # noqa: S324 -- a name, not a security digest
def record_key(repo: str, pin: str, candidate: str, family: str, unit: str) -> tuple[str, str, str, str, str]:
return repo_key(repo), pin, candidate, family, unit
def _as_dict(label: Label | Mapping[str, Any]) -> dict[str, Any]:
return asdict(label) if isinstance(label, Label) else dict(label)
def label_set_digest(labels: Iterable[Label | Mapping[str, Any]]) -> str:
lines = sorted(json.dumps(_as_dict(row), sort_keys=True, default=list) for row in labels)
return hashlib.sha256("\n".join(lines).encode("utf-8")).hexdigest()
@dataclass
class SourceCount:
labels: int = 0
no_record: int = 0
recorded: int = 0
no_excerpt: int = 0 # unread side, or the labelled function not found where it must be (counts to the 10%)
unread: int = 0
absent_fixed_side: int = 0 # file read, the function is gone at the fix: a label defect, reported apart
examples: int = 0
@dataclass
class MemoryBuild:
rows: list[dict[str, Any]] = field(default_factory=list)
sources: dict[str, SourceCount] = field(default_factory=dict)
skipped: dict[str, int] = field(default_factory=dict)
def skip(self, reason: str) -> None:
self.skipped[reason] = self.skipped.get(reason, 0) + 1
def failing(self, limit: float = NO_EXCERPT_LIMIT) -> list[str]:
"""Sources where more than ``limit`` of the recorded candidates have no excerpt."""
return sorted(s for s, c in self.sources.items() if c.recorded and c.no_excerpt / c.recorded > limit)
def summary(self) -> dict[str, Any]:
return {
"examples": len(self.rows),
"by_source": {s: asdict(c) for s, c in sorted(self.sources.items())},
"skipped": dict(sorted(self.skipped.items())),
"failing_sources": self.failing(),
}
LinesOf = Callable[[Mapping[str, Any], str], Sequence[str] | None] # (label, pin_role) -> file lines at that side
def _counter_role(label: Mapping[str, Any]) -> str:
return "vulnerable" if label.get("pin_role") == "fixed" else "fixed"
def build_examples(
labels: Iterable[Label | Mapping[str, Any]],
records: Mapping[tuple[str, str, str, str, str], Mapping[str, Any]],
lines_of: LinesOf,
*,
sources: Sources,
store: MemoryStore | None = None,
profile: str = "static",
roles_of: Callable[[Mapping[str, Any]], Sequence[Mapping[str, Any]]] | None = None,
license_of: Callable[[Mapping[str, Any]], str] | None = None,
function_of: Callable[[Mapping[str, Any], str], str | None] | None = None,
bounds: Bounds = EXAMPLE,
) -> MemoryBuild:
"""Example rows for the verified labels that have a feature record (``records`` by :func:`record_key`) and an
excerpt; with ``store`` the excerpt blobs and the rows are written. Delta units append the base->head diff."""
rows = [_as_dict(label) for label in labels]
allowed = {s.id for s in sources.sources} | {d for d, kind in DERIVED_SOURCES.items() if any(s.kind == kind for s in sources.sources)}
for row in rows:
if row.get("origin") == "user":
raise ExampleBuildError(f"label {row.get('candidate')!r} has origin: user -- the adaptation layer never enters the memory")
if row.get("source") not in allowed:
raise ExampleBuildError(f"label source {row.get('source')!r} is not in {sources.path}: it cannot enter the memory")
digest = label_set_digest(rows)
build = MemoryBuild()
for label in rows:
if label.get("conditional") is not None or label.get("source") == "assumed_benign":
build.skip("conditional label (condition not evaluated)")
continue
count = build.sources.setdefault(str(label["source"]), SourceCount())
count.labels += 1
pin = label_pin(label)
candidate, family, unit = str(label["candidate"]), str(label["family"]), str(label["unit"])
record = records.get(record_key(str(label["repo"]), pin, candidate, family, unit))
if record is None or record.get("profile") != profile:
count.no_record += 1
continue
count.recorded += 1
path, _, function = candidate.partition("::")
function = (function_of(label, str(label.get("pin_role") or "vulnerable")) if function_of else None) or function
language = str(record.get("language") or label.get("language") or "other")
identities = identities_of(str(label.get("repo") or ""), path, [str(label.get("pin") or "")])
lines = lines_of(label, str(label.get("pin_role") or "vulnerable"))
diff = None
if unit == "delta" and lines:
base = lines_of(label, _counter_role(label))
head_span, base_span = function_bounds(lines, language, function), function_bounds(base or (), language, function)
if base and head_span and base_span:
diff = delta_diff(base[slice(*base_span)], lines[slice(*head_span)], language, identities=identities)
shown = excerpt(lines, language, function, bounds=bounds, identities=identities, diff=diff)
if shown is None:
if not lines:
count.unread += 1
elif label.get("pin_role") == "fixed":
count.absent_fixed_side += 1
continue
count.no_excerpt += 1
continue
if store is not None:
store.put_blob("excerpts", shown.text.encode("utf-8"))
subject = f"{candidate}\x00{family}\x00{unit}\x00{label.get('pin_role')}\x00{label.get('source_ref')}"
build.rows.append(
{
"id": row_id(EXAMPLE_KIND, RUN, TASK, f"{label['repo']}\x00{pin}\x00{subject}"), "kind": EXAMPLE_KIND,
"repo": repo_key(str(label["repo"])), "pin": pin, "run": RUN, "task": TASK, "population": str(label["source"]),
"split": str(label.get("split") or label["source"]), "image": IMAGE,
"candidate": candidate, "family": family, "language": language, "profile": profile, "label": int(label["label"]),
"source": str(label["source"]), "group": str(label.get("group") or ""),
"frameworks": list(label["frameworks"]) if label.get("frameworks") is not None else None, "unit": unit,
"direction": label.get("direction"), "pin_role": label.get("pin_role", "vulnerable"),
"weight": float(label.get("weight", 1.0)), "license": (license_of(label) if license_of else "") or "",
"x": dict(record["x"]), "instruments": dict(record["instruments"]),
"roles": list(roles_of(label)) if roles_of else [], "excerpt_sha": shown.sha, "label_set_digest": digest,
}
) # fmt: skip
count.examples += 1
if store is not None and build.rows:
store.put_rows(build.rows)
return build
@dataclass(frozen=True)
class Example:
"""An example row as retrieval and the program use it (no envelope identity beyond ``id`` and ``group``)."""
id: str
group: str
source: str
frameworks: tuple[str, ...]
family: str
label: int
language: str
profile: str
unit: str
x: Mapping[str, Any]
instruments: Mapping[str, Mapping[str, Any]]
roles: tuple[Mapping[str, Any], ...]
excerpt_sha: str
license: str = ""
@classmethod
def from_row(cls, row: Mapping[str, Any]) -> Example:
return cls(
id=str(row["id"]), group=str(row.get("group") or ""), source=str(row["source"]), frameworks=tuple(row.get("frameworks") or ()),
family=str(row["family"]), label=int(row["label"]), language=str(row.get("language") or "other"),
profile=str(row.get("profile") or "static"), unit=str(row.get("unit") or "pin"), x=dict(row["x"]),
instruments={k: dict(v) for k, v in row["instruments"].items()}, roles=tuple(row.get("roles") or ()),
excerpt_sha=str(row["excerpt_sha"]), license=str(row.get("license") or ""),
) # fmt: skip
def load_examples(store: MemoryStore, profile: str | None = None) -> list[Example]:
"""Every example row in the store (optionally one profile), in key then id order."""
where = {"profile": profile} if profile else None
return [Example.from_row(r.row) for r in store.rows(kind=EXAMPLE_KIND, where=where)]
# --- reading the label sides --------------------------------------------------------------------------------------------
class SideReader:
"""File lines of a label's side, read-only: pairs from the corpus excerpts, populations and adjudications from
their clones (``git show pin:path``), development recipes from their checkouts. ``None`` when unread."""
def __init__(self, root: Path, cache: Path, sources: Sources, labels: Sequence[Mapping[str, Any]]) -> None:
from .labels import Clone
self.root, self.cache, self.sources = Path(root), Path(cache), sources
self._pairs: dict[str, Any] | None = None
self._clones: dict[str, Clone | None] = {}
self._case_of: dict[str, str] = {}
for label in labels:
source = next((s for s in sources.sources if s.id == label.get("source")), None)
if source is not None and source.kind == "population":
self._case_of.setdefault(str(label.get("repo")), f"{source.options.get('cache', 'independent')}/{label.get('source_ref')}")
def _pair_cases(self) -> dict[str, Any]:
if self._pairs is None:
from ..pairs import load_pair_catalog
source = next(s for s in self.sources.sources if s.kind == "pairs")
self._pairs = {case.name: case for case in load_pair_catalog(self.root / source.files[0])}
return self._pairs
def function(self, label: Mapping[str, Any], pin_role: str) -> str | None:
"""The function a pair's fixed side holds when it is not the labelled one (``fix_function``)."""
if label.get("source") != "pairs" or pin_role != "fixed":
return None
case = self._pair_cases().get(str(label.get("source_ref")))
return str(getattr(case, "fix_function", "") or "") or None
def license(self, label: Mapping[str, Any]) -> str:
if label.get("source") != "pairs":
return ""
case = self._pair_cases().get(str(label.get("source_ref")))
return str(getattr(case, "license", "") or "")
def __call__(self, label: Mapping[str, Any], pin_role: str) -> list[str] | None:
from .labels import Clone, LabelSourceError
source = str(label.get("source"))
path = str(label.get("candidate", "")).partition("::")[0]
if source == "pairs":
case = self._pair_cases().get(str(label.get("source_ref")))
if case is None:
return None
file = case.vuln_file if pin_role == "vulnerable" else case.fixed_file
return Path(file).read_text(encoding="utf-8", errors="replace").splitlines() if Path(file).is_file() else None
if source == "dev-php":
name = str(label.get("source_ref", "")).partition(":")[0]
file = self.cache / "repos" / name / str(label.get("pin"))[:12] / path
return file.read_text(encoding="utf-8", errors="replace").splitlines() if file.is_file() else None
case_dir = self._case_of.get(str(label.get("repo")))
if case_dir is None:
return None
if case_dir not in self._clones:
try:
self._clones[case_dir] = Clone(self.cache / case_dir)
except LabelSourceError:
self._clones[case_dir] = None
clone = self._clones[case_dir]
return clone.lines(str(label.get("pin")), path) if clone is not None else None
def read_jsonl(path: Path) -> list[dict[str, Any]]:
return [json.loads(line) for line in Path(path).read_text(encoding="utf-8").splitlines() if line.strip()]
def records_by_key(rows: Iterable[Mapping[str, Any]]) -> dict[tuple[str, str, str, str, str], dict[str, Any]]:
"""Feature records (``features`` rows of the store, or records with ``repo`` and ``pin`` beside them) by
:func:`record_key`."""
out: dict[tuple[str, str, str, str, str], dict[str, Any]] = {}
for row in rows:
key = record_key(str(row["repo"]), str(row["pin"]), str(row["candidate"]), str(row["family"]), str(row.get("unit", "pin")))
out[key] = dict(row)
return out
__all__ = [
"EXAMPLE_KIND", "NO_EXCERPT_LIMIT", "Example", "ExampleBuildError", "MemoryBuild", "SideReader", "build_examples",
"label_pin", "label_set_digest", "load_examples", "read_jsonl", "record_key", "records_by_key",
] # fmt: skip
+318
View File
@@ -0,0 +1,318 @@
"""Code excerpts for the decision engine's prompts and memory (learned-decision-engine design section 4.2).
No model. An excerpt is the body of the labelled function at its pin, as numbered lines, bounded:
- candidates: at most 80 lines and 4,000 characters, centred on the changed lines of a delta or the hit line;
- memory examples: at most 40 lines and 2,000 characters.
What an excerpt never holds: the path, the repository or the commit (none is rendered; a caller may name identity
strings -- ``owner/name``, the owner, the path, commit ids -- that are replaced wherever the code repeats them).
What it hides: advisory ids (CVE, GHSA) anywhere, and **comments with security wording** (:func:`.labels.
security_reason`, multilingual), which a fixed side often carries ("prevent XSS", "CVE-2021-1234") and which would
otherwise tell the model the label. Both become ``<redacted>``. A delta unit adds the base->head diff of the function
(at most 40 lines), the change under test -- never the later fix.
The text is normalised (trailing blanks dropped, ``\\n`` endings, one final newline) before it is hashed, so the sha
names the content an embedding and a response cache key are of.
"""
from __future__ import annotations
import difflib
import hashlib
import re
from collections.abc import Iterable, Sequence
from dataclasses import dataclass
from ..plane.tasks.repo_facts import DECLARATION, GLOBAL, _declared_name
from .features import function_span
from .labels import _ADVISORY, security_reason
REDACTED = "<redacted>"
LANGUAGE_ALIASES = {"c_cpp": "cpp", "c++": "cpp", "js": "javascript", "ts": "typescript", "py": "python"}
HASH_COMMENTS = frozenset({"python", "php", "ruby", "shell", "perl", "yaml", "r"})
SLASH_COMMENTS = frozenset(
{"c", "cpp", "java", "javascript", "typescript", "php", "go", "groovy", "csharp", "kotlin", "rust", "swift", "scala"}
) # fmt: skip
MAX_LINE_CHARS = 240
_HEX = re.compile(r"\b[0-9a-f]{7,40}\b", re.IGNORECASE)
@dataclass(frozen=True)
class Bounds:
lines: int
chars: int
CANDIDATE = Bounds(80, 4000)
EXAMPLE = Bounds(40, 2000)
DIFF_LINES = 40
@dataclass(frozen=True)
class Excerpt:
"""A rendered excerpt: ``text`` (numbered lines, then the diff for a delta), its ``sha``, the first and last
source line shown (1-based), and whether the bounds cut the function."""
text: str
sha: str
first: int
last: int
truncated: bool
@property
def line_numbers(self) -> range:
return range(self.first, self.last + 1)
def normalise(text: str) -> str:
lines = [line.rstrip() for line in text.replace("\r\n", "\n").replace("\r", "\n").split("\n")]
while lines and not lines[-1]:
lines.pop()
return "\n".join(lines) + "\n" if lines else ""
def excerpt_sha(text: str) -> str:
return hashlib.sha256(normalise(text).encode("utf-8")).hexdigest()
# --- comments ---------------------------------------------------------------------------------------------------------
def _comment_ranges(line: str, state: str | None, language: str) -> tuple[list[tuple[int, int]], str | None]:
"""The comment spans of one line and the block state carried to the next (``block`` inside ``/* */``, a triple
quote inside a Python docstring). Strings are skipped so a ``#`` in a literal is not a comment."""
hashes, slashes, python = language in HASH_COMMENTS, language in SLASH_COMMENTS, language == "python"
ranges: list[tuple[int, int]] = []
i, n, quote = 0, len(line), None
if state is not None:
close = "*/" if state == "block" else state
j = line.find(close)
if j < 0:
return [(0, n)], state
ranges.append((0, j + len(close)))
i, state = j + len(close), None
while i < n:
c = line[i]
if quote:
if c == "\\":
i += 2
continue
if c == quote:
quote = None
i += 1
continue
if python and line.startswith(('"""', "'''"), i) and not line[:i].strip():
delim = line[i : i + 3]
j = line.find(delim, i + 3)
if j < 0:
ranges.append((i, n))
return ranges, delim
ranges.append((i, j + 3))
i = j + 3
continue
if c in "\"'`":
quote = c
elif (slashes and line.startswith("//", i)) or (hashes and c == "#"):
ranges.append((i, n))
break
elif slashes and line.startswith("/*", i):
j = line.find("*/", i + 2)
if j < 0:
ranges.append((i, n))
return ranges, "block"
ranges.append((i, j + 2))
i = j + 2
continue
i += 1
return ranges, state
_OPEN = ("/**", "/*", "//", "#", '"""', "'''", "*")
_CLOSE = ("*/", '"""', "'''")
def _redact_comment(text: str) -> str:
if security_reason(text) is None:
return _ADVISORY.sub(REDACTED, text)
stripped = text.strip()
opener = next((o for o in _OPEN if stripped.startswith(o)), "")
closer = next((c for c in _CLOSE if stripped.endswith(c) and len(stripped) > len(opener)), "")
lead = text[: len(text) - len(text.lstrip())]
return lead + " ".join(p for p in (opener, REDACTED, closer) if p)
def redact(lines: Sequence[str], language: str, identities: Iterable[str] = ()) -> list[str]:
"""``lines`` with security wording in comments and advisory ids anywhere replaced by ``<redacted>``, and every
identity string (case-insensitive, at least 4 characters) replaced too."""
language = LANGUAGE_ALIASES.get(language, language)
names = sorted({i for i in identities if len(i) >= 4}, key=len, reverse=True)
identity = re.compile("|".join(re.escape(n) for n in names), re.IGNORECASE) if names else None
out: list[str] = []
state: str | None = None
for line in lines:
ranges, state = _comment_ranges(line, state, language)
text = line
for start, end in reversed(ranges):
text = text[:start] + _redact_comment(text[start:end]) + text[end:]
text = _ADVISORY.sub(REDACTED, text)
if identity is not None:
text = identity.sub(REDACTED, text)
out.append(text)
return out
def identities_of(repo: str = "", path: str = "", commits: Iterable[str] = ()) -> tuple[str, ...]:
"""The identity strings an excerpt must not repeat: ``owner/name``, the owner, the path, commit ids. The bare
repository name is kept: it is usually the package name the code imports, which is code, not an identity."""
from .labels import repo_name
found: list[str] = []
slug = repo_name(repo) if repo else ""
if "/" in slug:
found += [slug, slug.split("/", 1)[0]]
if path and "/" in path:
found.append(path)
found += [c for c in commits if c and _HEX.fullmatch(c)]
return tuple(found)
# --- spans and excerpts -----------------------------------------------------------------------------------------------
def _brace_bounds(lines: Sequence[str], language: str, function: str) -> tuple[int, int] | None:
"""A brace language's definition of ``function`` (``Class::name`` and ``a.b`` reduced to the last name): the first
line naming it before ``(`` whose ``{`` comes before any ``;``, to the matching ``}``. Comments are blanked
before braces are counted."""
name = re.split(r"::|\.|->", function)[-1]
if not re.fullmatch(r"[A-Za-z_]\w*", name):
return None
call = re.compile(r"(?<![\w.>$])" + re.escape(name) + r"\s*\(")
code: list[str] = []
state: str | None = None
for line in lines:
ranges, state = _comment_ranges(line, state, language if language in SLASH_COMMENTS else "c")
for start, end in reversed(ranges):
line = line[:start] + " " * (end - start) + line[end:]
code.append(line)
for i, line in enumerate(code):
found = call.search(line)
if found is None or re.match(r"\s*(return|if|while|for|switch|else)\b", line):
continue
tail = "\n".join([line[found.end() :], *code[i + 1 : i + 8]])
brace, semi = tail.find("{"), tail.find(";")
if brace < 0 or (0 <= semi < brace):
continue
depth, opened = 0, False
for j in range(i, len(code)):
for c in line[found.end() :] if j == i else code[j]:
if c == "{":
depth, opened = depth + 1, True
elif c == "}" and opened:
depth -= 1
if depth == 0:
return i, j + 1
return i, len(code)
return None
def function_bounds(lines: Sequence[str], language: str, function: str) -> tuple[int, int] | None:
"""0-based ``[start, end)`` of the first declaration of ``function`` (the whole file for ``<global>``), or
``None`` when the file does not declare it. Languages without a repo-facts declaration pattern (C, C++, ...) and
brace languages whose pattern misses the name use the brace matcher."""
language = LANGUAGE_ALIASES.get(language, language)
if function == GLOBAL:
return (0, len(lines)) if lines else None
pattern = DECLARATION.get(language)
start = next((i for i, line in enumerate(lines) if _declared_name(pattern, line) == function), None) if pattern else None
if start is None:
return _brace_bounds(lines, language, function) if language in SLASH_COMMENTS or pattern is None else None
span = function_span(lines, language, function)
if not span:
return None
return start, min(start + span, len(lines))
def _window(start: int, end: int, focus: Sequence[int], size: int) -> tuple[int, int]:
if end - start <= size:
return start, end
inside = sorted(f - 1 for f in focus if start <= f - 1 < end)
if not inside:
return start, start + size
centre = inside[len(inside) // 2]
lo = max(start, min(centre - size // 2, end - size))
return lo, lo + size
def _numbered(lines: Sequence[str], first: int) -> list[str]:
out = []
for offset, line in enumerate(lines):
text = line if len(line) <= MAX_LINE_CHARS else line[: MAX_LINE_CHARS - 3] + "..."
out.append(f"{first + offset:>5} {text}")
return out
def excerpt(
lines: Sequence[str] | None,
language: str,
function: str,
*,
bounds: Bounds = CANDIDATE,
focus: Sequence[int] = (),
identities: Iterable[str] = (),
diff: str | None = None,
) -> Excerpt | None:
"""The bounded, redacted, numbered excerpt of ``function``, or ``None`` when the file is unread or the function is
not declared there (never an empty excerpt). ``focus`` are 1-based lines to centre on; ``diff`` (a delta's
:func:`delta_diff`) is appended under a ``diff:`` line."""
if not lines:
return None
span = function_bounds(lines, language, function)
if span is None:
return None
start, end = span
lo, hi = _window(start, end, focus, bounds.lines)
shown = redact(lines[lo:hi], language, identities)
rendered = _numbered(shown, lo + 1)
centre = (len(rendered) - 1) // 2 if focus else 0
truncated = (lo, hi) != (start, end)
while len(rendered) > 1 and len("\n".join(rendered)) > bounds.chars:
# drop the line farthest from the centre (the end when there is no focus), keeping the window contiguous
if focus and centre > len(rendered) - 1 - centre:
rendered.pop(0)
lo += 1
centre -= 1
else:
rendered.pop()
hi = lo + len(rendered)
truncated = True
body = "\n".join(rendered)
if diff:
body += "\ndiff:\n" + diff
text = normalise(body)
if not text.strip():
return None
return Excerpt(text, excerpt_sha(text), lo + 1, hi, truncated)
def delta_diff(
base: Sequence[str], head: Sequence[str], language: str, *, max_lines: int = DIFF_LINES, identities: Iterable[str] = ()
) -> str:
"""The base->head unified diff of a function (one line of context, no file headers), redacted, at most
``max_lines`` lines."""
diff = [line for line in difflib.unified_diff(list(base), list(head), lineterm="", n=1)][2:]
out: list[str] = []
for line in diff:
if line.startswith("@@"):
out.append(line)
else:
out.append(line[:1] + redact([line[1:]], language, identities)[0])
if len(out) > max_lines:
out = out[: max_lines - 1] + [f"... ({len(out) - max_lines + 1} more diff lines)"]
return "\n".join(out)
__all__ = [
"CANDIDATE", "DIFF_LINES", "EXAMPLE", "REDACTED", "Bounds", "Excerpt", "delta_diff", "excerpt", "excerpt_sha",
"function_bounds", "identities_of", "normalise", "redact",
] # fmt: skip
+49 -2
View File
@@ -8,6 +8,8 @@ engine's own. Layout, identical for both backends::
index.jsonl one row per ingested (run, task): sha256 of its rows, row count index.jsonl one row per ingested (run, task): sha256 of its rows, row count
facts/<sha256>.json facts.json by content (identical facts stored once) facts/<sha256>.json facts.json by content (identical facts stored once)
repos/<host>__<owner>__<name>/<pin>.jsonl the rows of one repository + 40-hex pin, sorted by id repos/<host>__<owner>__<name>/<pin>.jsonl the rows of one repository + 40-hex pin, sorted by id
<prefix>/<sha256>.<ext> blobs (``put_blob``): excerpts/*.txt by content, embeddings/<model>/*.json
by excerpt sha, responses/*.json by request sha, programs/*.json
``OUSAST_MEMORY`` selects the backend: ``file:///path`` (:class:`FileStore`, default ``results_root()/memory``) or ``OUSAST_MEMORY`` selects the backend: ``file:///path`` (:class:`FileStore`, default ``results_root()/memory``) or
``minio://<bucket>[/<prefix>]`` (:class:`MinioStore`, the ``minio`` extra). MinIO's endpoint and credentials come ``minio://<bucket>[/<prefix>]`` (:class:`MinioStore`, the ``minio`` extra). MinIO's endpoint and credentials come
@@ -51,11 +53,16 @@ POPULATION_ANNOTATION = "openultrasast.io/population"
SPLIT_ANNOTATION = "openultrasast.io/split" SPLIT_ANNOTATION = "openultrasast.io/split"
SNAPSHOT_ANNOTATION = "openultrasast.io/memory-snapshot" # the loop's guard: {"catalog", "manifest", "populations"} SNAPSHOT_ANNOTATION = "openultrasast.io/memory-snapshot" # the loop's guard: {"catalog", "manifest", "populations"}
# The decision engine's kinds (learned-decision-engine, Data Models): `features` from the `features` task through # The decision engine's kinds (learned-decision-engine, Data Models): `features` from the `features` task through
# `remember`; `label`, `decision`, `experiment`, `arm_outcome` and `experiment_result` for the later tasks. # `remember`; `label`, `decision`, `experiment`, `arm_outcome` and `experiment_result` for the later tasks; `example`
# (a labelled case of the local memory: label + feature record + excerpt sha) from `ousast learn memory build`.
KINDS = ( KINDS = (
"facts", "verdict", "unit_cost", "alert", "coverage", "proposal_outcome", "facts", "verdict", "unit_cost", "alert", "coverage", "proposal_outcome",
"features", "label", "decision", "experiment", "arm_outcome", "experiment_result", "features", "label", "decision", "experiment", "arm_outcome", "experiment_result", "example",
) # fmt: skip ) # fmt: skip
# Blobs (`put_blob`): a top-level prefix, optionally one sub-directory (`embeddings/<model-slug>`), and a 64-hex name.
_BLOB_PREFIX = re.compile(r"[a-z]+(?:/[A-Za-z0-9._-]+)?")
_BLOB_NAME = re.compile(r"[0-9a-f]{64}")
BLOB_SUFFIX = {"excerpts": ".txt", "labels": ".jsonl"} # every other prefix holds JSON
ROW_FIELDS = ("id", "kind", "repo", "pin", "run", "task", "population", "split", "image") ROW_FIELDS = ("id", "kind", "repo", "pin", "run", "task", "population", "split", "image")
TAG_FIELDS = ("repo", "pin", "kind", "family", "run", "population", "split") TAG_FIELDS = ("repo", "pin", "kind", "family", "run", "population", "split")
MIN_FREE_BYTES = 1 << 30 MIN_FREE_BYTES = 1 << 30
@@ -112,6 +119,15 @@ def parse_memory_key(text: str) -> dict[str, str]:
return {k: str(v) for k, v in key.items()} return {k: str(v) for k, v in key.items()}
def blob_key(prefix: str, name: str) -> str:
"""The key of a blob: ``<prefix>/<name><suffix>``; a prefix or name outside the layout is refused."""
if not _BLOB_PREFIX.fullmatch(prefix) or ".." in prefix or prefix.split("/", 1)[0] in ("repos", "facts", "index.jsonl"):
raise MemoryStoreError(f"blob prefix {prefix!r} is not a blob prefix of the layout")
if not _BLOB_NAME.fullmatch(name):
raise MemoryStoreError(f"blob name {name!r} is not a 64-hex sha256")
return f"{prefix}/{name}{BLOB_SUFFIX.get(prefix.split('/', 1)[0], '.json')}"
def validate_row(row: object, where: str = "row") -> dict[str, Any]: def validate_row(row: object, where: str = "row") -> dict[str, Any]:
if not isinstance(row, dict): if not isinstance(row, dict):
raise MemoryStoreError(f"{where}: a row must be a JSON object") raise MemoryStoreError(f"{where}: a row must be a JSON object")
@@ -235,6 +251,20 @@ class MemoryStore(ABC):
return None return None
return found[0] return found[0]
def get_blob(self, prefix: str, name: str, *, verify: bool = True) -> bytes | None:
"""The blob ``<prefix>/<name>``. With ``verify`` (a content-addressed blob) one whose content no longer hashes
to its name is dropped with a warning and reported absent; keyed blobs (an embedding by its excerpt's sha, a
response by its request's sha) pass ``verify=False``."""
key = blob_key(prefix, name)
found = self._get(key)
if found is None:
return None
if verify and hashlib.sha256(found[0]).hexdigest() != name:
log.warning("memory %s: %s no longer matches its sha256; dropped", self.describe(), key)
self._delete(key)
return None
return found[0]
def rows( def rows(
self, repo: str | None = None, pin: str | None = None, kind: str | None = None, where: Mapping[str, Any] | None = None self, repo: str | None = None, pin: str | None = None, kind: str | None = None, where: Mapping[str, Any] | None = None
) -> list[Record]: ) -> list[Record]:
@@ -260,6 +290,23 @@ class MemoryStore(ABC):
self._put(key, data) self._put(key, data)
return sha return sha
def put_blob(self, prefix: str, data: bytes, *, name: str | None = None) -> str:
"""Store ``data`` under ``<prefix>/<sha256(data)>`` (content-addressed; identical data stored once) or under
``name`` when the blob is keyed by something else (the excerpt an embedding is of). A blob already present is
not rewritten: a key names one content. Returns the name."""
name = name or hashlib.sha256(data).hexdigest()
key = blob_key(prefix, name)
if self._get(key) is None:
self._check_space()
self._put(key, data)
return name
def blob_names(self, prefix: str) -> list[str]:
"""The names of the blobs under ``prefix``, sorted."""
blob_key(prefix, "0" * 64)
suffix = BLOB_SUFFIX.get(prefix.split("/", 1)[0], ".json")
return sorted(k.rsplit("/", 1)[1].removesuffix(suffix) for k in self._keys(prefix + "/") if k.count("/") == prefix.count("/") + 1)
def put_row(self, row: Mapping[str, Any]) -> None: def put_row(self, row: Mapping[str, Any]) -> None:
self.put_rows([row]) self.put_rows([row])
+157
View File
@@ -0,0 +1,157 @@
"""The example store (learned-decision-engine design 4.2, task 6.1): label + feature record + excerpt -> ``example``
rows, the refusals, the 10% unread-excerpt failure, and content-addressed blobs on both backends."""
from __future__ import annotations
import hashlib
import json
from collections.abc import Iterator, Mapping, Sequence
from pathlib import Path
from typing import Any
import pytest
from test_plane_memory import FakeObjects
from openultrasast.cli import main
from openultrasast.learn.examples import (
ExampleBuildError,
build_examples,
label_pin,
load_examples,
record_key,
records_by_key,
)
from openultrasast.learn.features import Part, quick_part, record
from openultrasast.learn.labels import Source, Sources
from openultrasast.plane.memory import FileStore, MemoryStore, MemoryStoreError, MinioStore
PIN = "0123456789abcdef0123456789abcdef01234567"
SOURCES = Sources(
(Source("pairs", "pairs", (), (), "2026-09-30", "development"), Source("population-v1", "population", (), (), "2026-09-25", "spent")),
frozenset(),
{},
Path("sources.toml"),
)
CODE = {
"vulnerable": ["def handler(request):", " # CVE-2021-1234 fixed later", " return run(request.args['q'])", ""],
"fixed": ["def handler(request):", " return run(quote(request.args['q']))", ""],
}
@pytest.fixture(params=["file", "fake-minio"])
def store(request: pytest.FixtureRequest, tmp_path: Path) -> Iterator[MemoryStore]:
yield FileStore(tmp_path / "memory") if request.param == "file" else MinioStore(FakeObjects("works"), "bucket", "p")
def label(**kw: Any) -> dict[str, Any]:
base = {
"candidate": "app.py::handler", "family": "injection", "label": 1, "source": "population-v1", "source_ref": "case-1",
"unit": "pin", "weight": 1.0, "split": "population-v1", "group": "acme/webapp", "repo": "https://github.com/acme/webapp",
"language": "python", "created": "2026-09-30", "pin": PIN, "pin_role": "vulnerable", "frameworks": ["flask"],
"conditional": None, "direction": None,
} # fmt: skip
return {**base, **kw}
def feature(lab: Mapping[str, Any], unit: str = "pin") -> dict[str, Any]:
parts = {"quick": quick_part([("r1", "enabled")], {})}
if unit == "delta":
parts["delta"] = Part("ran", {"delta.novelty": "new", "delta.changed_lines": 1, "delta.sanitizer_removed": True})
out = record(str(lab["candidate"]), str(lab["family"]), "python", "static", parts, unit=unit)
return {**out, "repo": lab["repo"], "pin": label_pin(lab)}
def lines_of(lab: Mapping[str, Any], role: str) -> Sequence[str] | None:
return CODE[role]
def test_example_rows_join_label_record_and_excerpt(store: MemoryStore) -> None:
pos, neg = label(), label(label=0, pin_role="fixed", pin="f" * 40)
records = records_by_key([feature(pos), feature(neg)])
build = build_examples([pos, neg], records, lines_of, sources=SOURCES, store=store)
assert build.summary()["examples"] == 2 and not build.failing()
stored = load_examples(store)
assert sorted(e.label for e in stored) == [0, 1] and {e.group for e in stored} == {"acme/webapp"}
row = next(r.row for r in store.rows(kind="example") if r.row["label"] == 1)
assert row["kind"] == "example" and row["frameworks"] == ["flask"] and "rationale" not in row
text = store.get_blob("excerpts", row["excerpt_sha"])
assert text is not None and hashlib.sha256(text).hexdigest() == row["excerpt_sha"]
assert b"CVE-2021" not in text
assert "acme" not in text.decode() and "app.py" not in text.decode()
def test_delta_units_carry_the_diff(store: MemoryStore) -> None:
lab = label(unit="delta", direction="introduce")
build = build_examples([lab], records_by_key([feature(lab, "delta")]), lines_of, sources=SOURCES, store=store)
row = build.rows[0]
text = store.get_blob("excerpts", row["excerpt_sha"])
assert text is not None and b"\ndiff:\n@@" in text and b"+ return run(request.args['q'])" in text
def test_user_origin_and_unlisted_sources_are_refused() -> None:
with pytest.raises(ExampleBuildError, match="origin: user"):
build_examples([label(origin="user")], {}, lines_of, sources=SOURCES)
with pytest.raises(ExampleBuildError, match="not in"):
build_examples([label(source="population-v3")], {}, lines_of, sources=SOURCES)
def test_conditional_and_unrecorded_labels_never_become_examples() -> None:
rows = [label(conditional="signal_at_tip_not_base", label=0), label(source="pairs", pin="", source_ref="p1")]
build = build_examples(rows, {}, lines_of, sources=SOURCES)
assert build.rows == [] and build.skipped == {"conditional label (condition not evaluated)": 1}
assert build.sources["pairs"].no_record == 1
def test_more_than_ten_percent_without_excerpt_fails_the_source() -> None:
labs = [label(candidate=f"app.py::f{i}", source_ref=f"c{i}") for i in range(10)] + [label()]
records = records_by_key([feature(lab) for lab in labs])
build = build_examples(labs, records, lines_of, sources=SOURCES) # f0..f9 are not declared in CODE: no excerpt
assert build.sources["population-v1"].no_excerpt == 10 and build.failing() == ["population-v1"]
ok = build_examples([label()], records, lines_of, sources=SOURCES)
assert ok.failing() == []
def test_pair_pins_are_stable_and_distinct_per_side() -> None:
vuln, fixed = label(source="pairs", pin="", source_ref="p1"), label(source="pairs", pin="", source_ref="p1", pin_role="fixed")
assert label_pin(vuln) != label_pin(fixed) and len(label_pin(vuln)) == 40 and label_pin(vuln) == label_pin(dict(vuln))
assert label_pin(label()) == PIN
key = record_key(vuln["repo"], label_pin(vuln), "app.py::handler", "injection", "pin")
assert key[0] == "github.com/acme/webapp"
def test_blobs_are_content_addressed_on_both_backends(store: MemoryStore) -> None:
sha = store.put_blob("excerpts", b"x = 1\n")
assert sha == hashlib.sha256(b"x = 1\n").hexdigest() and store.get_blob("excerpts", sha) == b"x = 1\n"
assert store.put_blob("excerpts", b"x = 1\n") == sha and store.blob_names("excerpts") == [sha]
keyed = store.put_blob("embeddings/openai__text-embedding-3-small", b'{"v": 1}', name=sha)
assert keyed == sha and store.get_blob("embeddings/openai__text-embedding-3-small", sha, verify=False) == b'{"v": 1}'
assert store.get_blob("excerpts", "0" * 64) is None
for bad in ("repos", "../x", "facts"):
with pytest.raises(MemoryStoreError):
store.put_blob(bad, b"x")
with pytest.raises(MemoryStoreError):
store.get_blob("excerpts", "not-a-sha")
def test_a_changed_blob_is_dropped_not_served(tmp_path: Path) -> None:
store = FileStore(tmp_path / "m")
sha = store.put_blob("excerpts", b"a\n")
(tmp_path / "m" / "excerpts" / f"{sha}.txt").write_bytes(b"tampered\n")
assert store.get_blob("excerpts", sha) is None and store.blob_names("excerpts") == []
def test_cli_build_writes_examples_and_exits_2_on_refusal(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
labels = tmp_path / "labels.jsonl"
labels.write_text(json.dumps(label(origin="user")) + "\n")
assert main(["learn", "memory", "build", "--labels", str(labels), "--dry-run"]) == 2
assert "origin: user" in capsys.readouterr().err
def test_a_fixed_side_without_the_function_is_a_label_defect_not_an_unread_side() -> None:
fixed = label(label=0, pin_role="fixed", pin="f" * 40)
moved = {"vulnerable": CODE["vulnerable"], "fixed": ["def guard(request):", " return True"]}
build = build_examples([fixed], records_by_key([feature(fixed)]), lambda lab, role: moved[role], sources=SOURCES)
count = build.sources["population-v1"]
assert count.absent_fixed_side == 1 and count.no_excerpt == 0 and build.failing() == []
unread = build_examples([fixed], records_by_key([feature(fixed)]), lambda lab, role: None, sources=SOURCES)
assert unread.sources["population-v1"].unread == 1 and unread.failing() == ["population-v1"]
+138
View File
@@ -0,0 +1,138 @@
"""Excerpts for the decision engine (learned-decision-engine design 4.2): bounds, centring, no identities, advisory ids
and security wording in comments redacted, the delta diff."""
from __future__ import annotations
from openultrasast.learn.excerpt import (
CANDIDATE,
EXAMPLE,
REDACTED,
Bounds,
delta_diff,
excerpt,
excerpt_sha,
identities_of,
normalise,
redact,
)
PY = [
"import os",
"",
"def handler(request):",
" # CVE-2021-1234: prevent SQL injection by quoting",
" name = request.args['name']",
' """Fetch the user. Fixes GHSA-abcd-efgh-ijkm."""',
" query = 'SELECT * FROM users WHERE name = ' + name # build the query",
" return db.execute(query)",
"",
"def other():",
" return 1",
]
def _long_function(n: int, width: int = 10) -> list[str]:
return ["def big(x):"] + [f" v{i} = x + {i}" + " " * width for i in range(n)] + [" return v0", "", "def after():", " pass"]
def test_function_only_numbered_from_its_first_line() -> None:
shown = excerpt(PY, "python", "handler")
assert shown is not None
lines = shown.text.splitlines()
assert lines[0].split()[0] == "3" and "def handler" in lines[0]
assert "def other" not in shown.text and shown.first == 3 and shown.last == 9
assert shown.sha == excerpt_sha(shown.text) and not shown.truncated
def test_unread_file_or_unknown_function_is_none_never_empty() -> None:
assert excerpt(None, "python", "handler") is None
assert excerpt([], "python", "handler") is None
assert excerpt(PY, "python", "missing") is None
assert excerpt(PY, "cobol", "handler") is None
def test_security_comments_and_advisory_ids_are_redacted() -> None:
shown = excerpt(PY, "python", "handler")
assert shown is not None
assert "CVE-2021" not in shown.text and "GHSA" not in shown.text and "injection" not in shown.text
assert "prevent" not in shown.text and REDACTED in shown.text
assert "# build the query" in shown.text # an ordinary comment stays
assert "query = 'SELECT * FROM users WHERE name = ' + name" in shown.text # code is never redacted
def test_redaction_covers_block_comments_and_other_languages() -> None:
php = ["function f($id) {", " /* sanitize to avoid XSS", " (see report) */", " echo $id; // 修复越权", "}"]
out = redact(php, "php")
assert "XSS" not in "\n".join(out) and "修复越权" not in out[3] and out[3].startswith(" echo $id;")
assert "#" not in redact(["x = '#not a comment'"], "python")[0].replace("'#not a comment'", "")
assert redact(["s = 'CVE-2020-0001'"], "python") == [f"s = '{REDACTED}'"]
def test_no_path_repository_or_commit_in_the_excerpt() -> None:
lines = [
"def handler(request):",
" # see https://github.com/acme-corp/webapp/blob/0123456789abcdef0123456789abcdef01234567/app/views.py",
" log('app/views.py')",
" return request",
]
ids = identities_of("https://github.com/acme-corp/webapp", "app/views.py", ["0123456789abcdef0123456789abcdef01234567"])
shown = excerpt(lines, "python", "handler", identities=ids)
assert shown is not None
for identity in ("acme-corp", "0123456789abcdef", "app/views.py"):
assert identity not in shown.text
assert "webapp" in shown.text or REDACTED in shown.text # the bare name is code-level, the owner and slug are not
def test_bounds_lines_and_characters() -> None:
lines = _long_function(200)
shown = excerpt(lines, "python", "big", bounds=CANDIDATE)
assert shown is not None and shown.truncated
assert len(shown.text.splitlines()) <= CANDIDATE.lines and len(shown.text) <= CANDIDATE.chars
small = excerpt(lines, "python", "big", bounds=EXAMPLE)
assert small is not None and len(small.text.splitlines()) <= EXAMPLE.lines and len(small.text) <= EXAMPLE.chars
wide = _long_function(60, width=120)
tight = excerpt(wide, "python", "big", bounds=Bounds(80, 2000))
assert tight is not None and len(tight.text) <= 2000 and tight.truncated
def test_window_centres_on_the_focus_lines() -> None:
lines = _long_function(300)
shown = excerpt(lines, "python", "big", bounds=Bounds(40, 100_000), focus=[200])
assert shown is not None and shown.first <= 200 <= shown.last
assert abs((shown.first + shown.last) / 2 - 200) <= 1
chars = excerpt(lines, "python", "big", bounds=Bounds(80, 1200), focus=[200])
assert chars is not None and chars.first <= 200 <= chars.last
def test_delta_diff_is_redacted_and_bounded() -> None:
base = ["def f(x):", " return escape(x)"]
head = ["def f(x):", " # CVE-2022-2222 sanitize later", " return x"]
diff = delta_diff(base, head, "python")
assert diff.startswith("@@") and "+ return x" in diff and "- return escape(x)" in diff
assert "CVE-2022" not in diff and "sanitize" not in diff and "---" not in diff
long = delta_diff([f"a{i}" for i in range(100)], [f"b{i}" for i in range(100)], "python", max_lines=40)
assert len(long.splitlines()) == 40 and long.splitlines()[-1].startswith("...")
shown = excerpt(head, "python", "f", diff=diff)
assert shown is not None and "\ndiff:\n@@" in shown.text
def test_normalised_text_names_the_content() -> None:
assert normalise("a \r\nb\n\n\n") == "a\nb\n"
assert excerpt_sha("a \nb") == excerpt_sha("a\nb\n")
def test_brace_languages_without_a_declaration_pattern() -> None:
c = [
"/* CVE-2016-0001: heap overflow fixed */",
"static int",
"parse_header(const char *buf, size_t len)",
"{",
" if (len > 4) { memcpy(out, buf, len); }",
" return parse_header_inner(buf); /* not a definition */",
"}",
"int other(void) { return parse_header(0, 0); }",
]
shown = excerpt(c, "c_cpp", "parse_header")
assert shown is not None and shown.first == 3 and shown.last == 7 and "other(void)" not in shown.text
assert excerpt(c, "cpp", "Parser::parse_header") is not None
assert excerpt(["int f(void);", "int g(void) { return f(); }"], "c", "f") is None # a prototype is not a body