mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
docs(learning-harness): record implementation notes for group 1 and tasks 2.1-2.4
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
95ffd901e4
commit
6d98e47e42
@@ -173,3 +173,16 @@
|
||||
|
||||
- Sanity review round 1 (2026-09-06): NEEDS_FIXES, eleven findings, all repaired (K-run policy honors Req 3.5; per-model baselines added to the design; family validation moved out of the corpus loader; reasoning replay owned by the hunter loop; Req 5.3 split into re-measure and publish; scoring path split into pairs and scan tasks; oversized tasks split; fix-date prerequisite added; fixture-difficulty check depends on the re-harvest; memory-safety family measured on vfc; detector task depends on the classifier and round zero owns the shipped directories; observables re-anchored).
|
||||
- Sanity review round 2 (2026-09-06): NEEDS_FIXES with three local items, repaired without a third review under the bounded loop: task 1.5 delivers the four curated tools and the hunter-loop parameters that 2.5 relied on; the scan-path task now follows round zero and degrades when a family directory is missing; the write-root observable moved to the HarnessX proposer task with 7.6. Non-blocking notes applied: the split helper assigns as well as reports (4.4), 3.2 depends on 2.1 rather than 2.2.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Requirements approved 2026-09-06, design approved 2026-09-06, tasks approved 2026-09-06 (maintainer, in session).
|
||||
- Task 1.1 (2026-09-06, RED first: 9 failing tests in `tests/test_learning_families.py`, `ModuleNotFoundError: openultrasast.learning`; then green): `learning/families.py` and `ruleset/families.toml` with the ten decided families, each carrying its CWE set, mechanism set, verifier kind and description. Decisions taken while writing the data: (a) `unknown` carries the six mechanisms that span families (`source_reaches_sink`, `container_taint`, `validation_strength`, `redos`, `cross_artifact`, `other`) and the two weaknesses we deliberately do not target (CWE-20 generic validation, CWE-835 infinite loop), so a row we considered and declined is visible instead of unresolved; an earlier draft said `unknown` had none. (b) The maintainer's "server-side request forgery" family is named `untrusted_destination` and also covers open redirect (CWE-601), because both are untrusted input choosing a request destination and the corpus carries 34 open-redirect rows that would otherwise be unclassifiable. (c) The taxonomy is flat: `parent` is an optional field no shipped family uses, exercised by a fixture, because Req 2.3 and 3.1 need a parent-child arm and inventing a hierarchy would be worse than leaving it empty. (d) Abstention earns no partial credit: `related("unknown", X)` is `lateral`. All 33 labeled corpus weaknesses resolve, none is claimed twice, and every mechanism id belongs to exactly one family.
|
||||
- Task 1.2 (2026-09-06, RED first: 8 failing tests in `tests/test_learning_endpoint.py`; then green): `learning/endpoint.py` resolves override, then the scripted flag, then a configured endpoint, then DeepSeek, then OpenRouter; the embedding client is untouched. `provider/openrouter.py` gains `complete_chat_raw` (whole payload, for `usage`) and an `extra_body` passthrough; `ChatResponse` gains `reasoning` and `mean_logprob`. Design deviation: `ChatEndpoint` deliberately holds no `api_key` (the design listed one) so the record is safe to journal; the key stays on the client. `[models]` gains `judge`, `chat_base_url`, `chat_api_key_env`; `[learning]` is new. `ModelConfig.hunter` keeps its `None` default on purpose: defaulting it would silently enable LLM stages in existing scans, so the DeepSeek model defaults live in `resolve_models`.
|
||||
- Task 1.3 (2026-09-06, RED first: 7 failing tests in `tests/test_pair_hygiene.py`; then green): `ExpectedFinding.family` (opaque; the classifier validates it), `PairCase.unscorable`, `PairCase.split_declared`, `PairCase.fix_date` (the generator fills it in 4.3), `duplicate_groups`, `split_by_repository` with `SplitReport`, and a split filter on `build_pair_signals`. Measured and committed as `benchmarks/measurements/2026-09-06-corpus-hygiene.json`: **19 repositories straddle the train/holdout split** (a learning path can be taught by one file of a repository and evaluated on another) and **10 groups of pairs share a vulnerable excerpt byte for byte**, mostly the same fix harvested twice at different granularity. Only three vendored rows (the `local` slice) declare no split.
|
||||
- Task 1.4 (2026-09-06, RED first: 2 failing tests in `tests/test_typescript_grammar.py`; then green): the semantic extra gains `tree-sitter-typescript`; `grammar_for` maps a language to (module, attribute) because the wheel ships two grammars, and `parse_with_cst` picks the JSX-aware one for a `.tsx` path while the parsed file keeps the language it was given. Eighteen agent-vfc rows plus the TypeScript absence rows now parse on both sides.
|
||||
- Task 1.5 (2026-09-06, RED first: 5 of 6 tests in `tests/test_hunter_curated_tools.py` failing; then green): `hunter_tools` gains `read_definition`, `entry_points`, `flows`, `obligations`; `run_tool_hunter` gains `system_prompt`, `user_prompt`, `context_files`, `tools`, `max_chars` and `tags`, all defaulting to today's behaviour, and replays the provider's reasoning field on the next turn. Deviations: `read_definition` returns the definition text as well as its span, and includes preceding decorator lines, because a resolver that returns only line numbers forces another `read_file` and a handler's decorators are exactly what the obligations work showed to matter; the three list tools return identifiers and spans only. No caching: a stale view of a tree a round may have rewritten is worse than a reparse.
|
||||
- Task 2.1 (2026-09-06, RED first: 8 failing tests in `tests/test_learning_classify.py`; then green): `learning/classify.py` with three tiers, multi-label answers in taxonomy order, and `validate_family_labels` owning the taxonomy check the corpus loader deliberately does not do.
|
||||
- Task 2.2 (2026-09-06, RED first: 5 failing tests in `tests/test_learning_classifier_report.py`; then green): `measure_classifier` and `write_review_queue`. Bug caught by the RED test: the classifier read the declared label it was being scored against, making agreement trivially 1.0; `classify_pair(..., use_declared=False)` is what the measurement uses. Measured on the 277 vendored pairs: tier one places 275 without a model call, 2 land in `unknown`, and the 176 memory-safety rows resolve to one family. `labeled` is 0 because no row carries a maintainer family label yet, and the report says so instead of inventing a score.
|
||||
- Task 2.3 (2026-09-06, RED first: 8 failing tests in `tests/test_learning_scoring.py`; then green): `learning/scoring.py` `score_pair_family`. Detection is a family-tagged finding inside the labeled function; text is never read. Findings of other families or outside the function are counted separately and never called leaks; a fabricated family id is counted as noise. K runs, majority outcome, disagreeing runs recorded as flips.
|
||||
- Task 2.4 (2026-09-06, RED first: 7 failing tests in `tests/test_learning_aggregation.py`; then green): `aggregate` and `sign_test`. Recall at a fixed false-positive ceiling is forfeited entirely when the leak rate exceeds it; the directional-bias index separates "flags everything" from "silent on everything", which Youden cannot; change against a baseline is a two-sided exact sign test over per-pair flips, not a difference of means. `PairFamilyScore` gained a `slice` field (additive, filled from the case) so metrics can be reported per slice.
|
||||
|
||||
Reference in New Issue
Block a user