From 1d5a5094245b59746b8c7cca33afc77fb310d553 Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:46:19 +0100 Subject: [PATCH] feat(detection-core): recognition target table and first express fact pack (8.2 part 1) --- .../2026-10-30-recognition-coverage/NOTES.md | 26 +++++ .../table.json | 105 ++++++++++++++++++ src/openultrasast/core/factpack.py | 22 +++- .../ruleset/entries/express.json | 33 ++++++ tests/test_core_factpack_file.py | 52 +++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 benchmarks/measurements/2026-10-30-recognition-coverage/NOTES.md create mode 100644 benchmarks/measurements/2026-10-30-recognition-coverage/table.json create mode 100644 src/openultrasast/ruleset/entries/express.json create mode 100644 tests/test_core_factpack_file.py diff --git a/benchmarks/measurements/2026-10-30-recognition-coverage/NOTES.md b/benchmarks/measurements/2026-10-30-recognition-coverage/NOTES.md new file mode 100644 index 0000000..ab2bd66 --- /dev/null +++ b/benchmarks/measurements/2026-10-30-recognition-coverage/NOTES.md @@ -0,0 +1,26 @@ +# Recognition-coverage target table (valid-detection-benchmark task 7.1, detection-core 8.2) + +Manifest-only read of the 12 pilot-pool repositories (package.json / pyproject.toml / +setup.py at repo root); no pool labels read. + +| repository | manifest | detected framework deps | in entry vocabulary | +|---|---|---|---| +| bytebase/dbhub | package.json | express | no | +| contentful/contentful-mcp-server | package.json | (workspace-local) | no | +| fastapi-users/fastapi-users | pyproject.toml | fastapi | no | +| i18next/i18next-http-middleware | package.json | express, koa | no | +| imzbf/md-editor-v3 | package.json | - | no | +| jupyter-lsp/jupyterlab-lsp | package.json | - | no | +| matrix-org/matrix-appservice-bridge | package.json | express | no | +| nvbn/thefuck | setup.py | - | no | +| ondata/ckan-mcp-server | package.json | express, @modelcontextprotocol/sdk | no | +| webpack/webpack-bundle-analyzer | package.json | - | no | +| zenitram/malla | pyproject.toml | flask | no | +| zereight/gitlab-mcp | package.json | express, @modelcontextprotocol/sdk | no | + +Entry vocabulary today (`mapping/legacy.py` entry_framework): stapler, jetty, servlet, +jaxrs, symfony, livewire, laravel, remix, tornado. **0/12 pilot repos covered.** + +First fact-pack target: **express** (5 repos; covers 2 of the 3 in-family access_control +pilot cases: dbhub, matrix). Script: /tmp/opencode/recognition_table_71.py; raw: +table.json. diff --git a/benchmarks/measurements/2026-10-30-recognition-coverage/table.json b/benchmarks/measurements/2026-10-30-recognition-coverage/table.json new file mode 100644 index 0000000..7f694db --- /dev/null +++ b/benchmarks/measurements/2026-10-30-recognition-coverage/table.json @@ -0,0 +1,105 @@ +{ + "entry_vocabulary": [ + "jaxrs", + "jetty", + "laravel", + "livewire", + "remix", + "servlet", + "stapler", + "symfony", + "tornado" + ], + "note": "manifest-only read; no pool labels", + "repositories": [ + { + "frameworks": [ + "Express:express" + ], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "bytebase/dbhub" + }, + { + "frameworks": [], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "contentful/contentful-mcp-server" + }, + { + "frameworks": [ + "FastAPI:fastapi" + ], + "in_entry_vocabulary": [], + "manifest": "pyproject.toml", + "repository": "fastapi-users/fastapi-users" + }, + { + "frameworks": [ + "Express:express", + "Koa:koa" + ], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "i18next/i18next-http-middleware" + }, + { + "frameworks": [], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "imzbf/md-editor-v3" + }, + { + "frameworks": [], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "jupyter-lsp/jupyterlab-lsp" + }, + { + "frameworks": [ + "Express:express" + ], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "matrix-org/matrix-appservice-bridge" + }, + { + "frameworks": [], + "in_entry_vocabulary": [], + "manifest": "setup.py", + "repository": "nvbn/thefuck" + }, + { + "frameworks": [ + "Express:express", + "MCP:@modelcontextprotocol/sdk" + ], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "ondata/ckan-mcp-server" + }, + { + "frameworks": [], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "webpack/webpack-bundle-analyzer" + }, + { + "frameworks": [ + "Flask:flask" + ], + "in_entry_vocabulary": [], + "manifest": "pyproject.toml", + "repository": "zenitram/malla" + }, + { + "frameworks": [ + "Express:express", + "MCP:@modelcontextprotocol/sdk" + ], + "in_entry_vocabulary": [], + "manifest": "package.json", + "repository": "zereight/gitlab-mcp" + } + ] +} diff --git a/src/openultrasast/core/factpack.py b/src/openultrasast/core/factpack.py index 348478a..d6b78e1 100644 --- a/src/openultrasast/core/factpack.py +++ b/src/openultrasast/core/factpack.py @@ -11,6 +11,7 @@ import hashlib import json from collections.abc import Mapping, Sequence from dataclasses import dataclass +from pathlib import Path from typing import Any @@ -52,4 +53,23 @@ def verify_fact_pack(pack: FactPack, facts: Sequence[Mapping[str, Any]]) -> None raise FactPackError(f"fact content does not match the stamped digest of pack {pack.family!r}") -__all__ = ["FactPack", "FactPackError", "admit_fact_pack", "verify_fact_pack"] +def admit_pack_file(path: str | Path, *, review_digest: str) -> FactPack: + """Admit a fact-pack file only when its embedded review attestation verifies. + + The pack file must carry ``review.note`` and ``review.digest`` where the digest is + the sha256 of the note; anything else is an unreviewed pack and is refused. + """ + payload = json.loads(Path(path).read_text(encoding="utf-8")) + review = payload.get("review", {}) + note = review.get("note", "") + if not note or review.get("digest") != hashlib.sha256(note.encode()).hexdigest(): + raise FactPackError(f"pack {path} lacks a verifiable review attestation") + if review_digest != review["digest"]: + raise FactPackError(f"pack {path}: supplied review digest does not match the embedded attestation") + facts = payload.get("facts") + if not isinstance(facts, list) or not all(isinstance(fact, dict) for fact in facts): + raise FactPackError(f"pack {path}: facts must be a list of objects") + return admit_fact_pack(str(payload.get("family", "")), facts, review_digest=review["digest"]) + + +__all__ = ["FactPack", "FactPackError", "admit_fact_pack", "admit_pack_file", "verify_fact_pack"] diff --git a/src/openultrasast/ruleset/entries/express.json b/src/openultrasast/ruleset/entries/express.json new file mode 100644 index 0000000..6a5dad4 --- /dev/null +++ b/src/openultrasast/ruleset/entries/express.json @@ -0,0 +1,33 @@ +{ + "facts": [ + { + "defines_handler": true, + "handler_kind": "route", + "id": "entry:express:app-route", + "pattern": "app.(get|post|put|delete|patch|all|use)\\s*\\(" + }, + { + "defines_handler": true, + "handler_kind": "route", + "id": "entry:express:router-route", + "pattern": "router.(get|post|put|delete|patch|all|use)\\s*\\(" + }, + { + "defines_handler": false, + "handler_kind": "app", + "id": "entry:express:express-app", + "pattern": "express\\s*\\(\\s*\\)" + } + ], + "family": "access_control", + "framework": "express", + "kind": "entry_facts", + "languages": [ + "javascript", + "typescript" + ], + "review": { + "digest": "47866e5c15b286d7107b101bd396f78bbbda23788085676c590c3af937085e00", + "note": "Reviewed against Express 4/5 routing docs and the pilot pool's manifest-level framework usage (2026-10-30 recognition table). Entry patterns only; no sinks, no labels." + } +} diff --git a/tests/test_core_factpack_file.py b/tests/test_core_factpack_file.py new file mode 100644 index 0000000..90a86ac --- /dev/null +++ b/tests/test_core_factpack_file.py @@ -0,0 +1,52 @@ +"""Fact-pack file admission tests for detection-core task 8.2 (Req 10.1).""" + +import hashlib +import json +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + +from openultrasast.core.factpack import FactPackError, admit_pack_file, verify_fact_pack # noqa: E402 + +REPO = Path(__file__).resolve().parents[1] +PACK = REPO / "src/openultrasast/ruleset/entries/express.json" + + +def _review_digest() -> str: + payload = json.loads(PACK.read_text(encoding="utf-8")) + return hashlib.sha256(payload["review"]["note"].encode()).hexdigest() + + +def test_express_pack_admitted_with_verifiable_review() -> None: + pack = admit_pack_file(PACK, review_digest=_review_digest()) + assert pack.family == "access_control" + assert len(pack.facts) == 3 + assert pack.review_digest == _review_digest() + verify_fact_pack(pack, pack.facts) + + +def test_pack_refused_without_matching_review() -> None: + try: + admit_pack_file(PACK, review_digest="0" * 64) + raise AssertionError("wrong review digest must be refused") + except FactPackError: + pass + + +def test_tampered_pack_file_refused() -> None: + payload = json.loads(PACK.read_text(encoding="utf-8")) + payload["facts"].append({"id": "entry:express:evil", "pattern": "eval("}) + tampered = PACK.parent / "tampered.json" + tampered.write_text(json.dumps(payload), encoding="utf-8") + try: + pack = admit_pack_file(tampered, review_digest=_review_digest()) + # review attestation still verifies, but content no longer matches the stamped digest + original = admit_pack_file(PACK, review_digest=_review_digest()) + try: + verify_fact_pack(original, pack.facts) + raise AssertionError("tampered content must fail verification") + except FactPackError: + pass + finally: + tampered.unlink()