mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 01:48:17 +00:00
feat(detection-core): recognition target table and first express fact pack (8.2 part 1)
This commit is contained in:
@@ -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.
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"]
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user