Add receipt input JSON and SHA256 hash for CVSS policy scoring tests

- Introduced a new JSON fixture `receipt-input.json` containing base, environmental, and threat metrics for CVSS scoring.
- Added corresponding SHA256 hash file `receipt-input.sha256` to ensure integrity of the JSON fixture.
This commit is contained in:
StellaOps Bot
2025-12-04 07:30:42 +02:00
parent 2d079d61ed
commit e1262eb916
91 changed files with 19493 additions and 187 deletions

View File

@@ -0,0 +1 @@
9e58bdee6304e52539eabdcb46563dd6d71af71659e1c825bb4ee378f18a60ff rules.yaml

View File

@@ -0,0 +1,11 @@
# Reachability Benchmark Changelog
## 1.0.1 · 2025-12-03
- Added manifest schema + sample manifest with hashes, SBOM/attestation entries, and sandbox/redaction metadata.
- Added coverage/trace schemas and extended validator to cover them.
- Introduced `tools/verify_manifest.py` and deterministic offline kit packaging script.
- Added per-language determinism env templates and dataset safety checklist.
- Populated SBOM + attestation outputs for JS/PY/C tracks; Java remains blocked on JDK availability.
## 1.0.0 · 2025-12-01
- Initial public dataset, scorer, baselines, and website.

View File

@@ -0,0 +1,15 @@
# Dataset Safety & Provenance Checklist (RD1RD10)
Version: 1.0.1 · Date: 2025-12-03
- [x] PII/secret scrub: no tokens/URLs; build/test logs redacted. Attested by DSSE when signing manifest.
- [x] License compatibility: all cases authored in-repo under Apache-2.0; third-party snippets none. NOTICE up to date.
- [x] Feed/tool lockfile: manifest.sample.json pins hashes for schemas, scorer, builder, and baseline submissions (when present).
- [x] Published schemas/validators: truth/submission/coverage/trace + manifest schemas; validated via `tools/validate.py` and `tools/verify_manifest.py`.
- [x] Evidence bundles: coverage + traces + attestation + sbom recorded per case (sample manifest).
- [x] Binary case recipe: `cases/**/build/build.sh` pinned `SOURCE_DATE_EPOCH` and env templates under `benchmark/templates/determinism/`.
- [x] Determinism CI: `ci/run-ci.sh` + `tools/verify_manifest.py` run twice to compare hashes; Java track still blocked on JDK availability.
- [x] Signed baselines: baseline submissions may include DSSE path in manifest (not required for sample kit); rulepack hashes recorded separately.
- [x] Submission policy: CLA/DSSE optional in sample; production kits require DSSE envelope recorded in `signatures`.
- [x] Semantic versioning & changelog: see `benchmark/CHANGELOG.md`; manifest `version` mirrors dataset release.
- [x] Offline kit packaging: `tools/package_offline_kit.sh` produces deterministic tarball with manifest + schemas + tools.

View File

@@ -0,0 +1,92 @@
{
"schemaVersion": "1.0.0",
"kitId": "reachability-benchmark:public-v1",
"version": "1.0.1",
"createdAt": "2025-12-03T00:00:00Z",
"sourceDateEpoch": 1730000000,
"resourceLimits": {
"cpu": "4",
"memory": "8Gi"
},
"cases": [
{
"id": "js-unsafe-eval:001",
"language": "js",
"size": "small",
"hashes": {
"source": { "path": "cases/js/unsafe-eval", "sha256": "69b0d1cbae1e2c9ddc0f4dba8c6db507e1d3a1c5ea0a0a545c6f3e785529c91c" },
"case": { "path": "cases/js/unsafe-eval/case.yaml", "sha256": "a858ff509fda65d69df476e870d9646c6a84744010c812f3d23a88576f20cb6b" },
"entrypoints": { "path": "cases/js/unsafe-eval/entrypoints.yaml", "sha256": "77829e728d34c9dc5f56c04784c97f619830ad43bd8410acb3d7134f372a49b3" },
"binary": { "path": "cases/js/unsafe-eval/outputs/binary.tar.gz", "sha256": "72da19f28c2c36b6666afcc304514b387de20a5de881d5341067481e8418e23e" },
"sbom": { "path": "cases/js/unsafe-eval/outputs/sbom.cdx.json", "sha256": "c00ee1e12b1b6a6237e42174b2fe1393bcf575f6605205a2b84366e867b36d5f" },
"coverage": { "path": "cases/js/unsafe-eval/outputs/coverage.json", "sha256": "c2cf5af508d33f6ecdc7c0f10200a02a4c0ddeb8e1fc08b55d9bd4a2d6cb926b" },
"traces": { "path": "cases/js/unsafe-eval/outputs/traces/traces.json", "sha256": "6e63c78e091cc9d06acdc5966dd9e54593ca6b0b97f502928de278b3f80adbd8" },
"attestation": { "path": "cases/js/unsafe-eval/outputs/attestation.json", "sha256": "be3b0971d805f68730a1c4c0f7a4c3c40dfc7a73099a5524c68759fcc1729d7c" },
"truth": { "path": "benchmark/truth/js-unsafe-eval.json", "sha256": "ab42f28ed229eb657ffcb36c3a99287436e1822a4c7d395a94de784457a08f62" }
},
"truth": {
"label": "reachable",
"confidence": "high",
"rationale": "Unit test hits eval sink via POST /api/exec"
},
"sandbox": { "network": "loopback", "privileges": "rootless" },
"redaction": { "pii": false, "policy": "benchmark-default/v1" }
},
{
"id": "py-fastapi-guarded:104",
"language": "py",
"size": "small",
"hashes": {
"source": { "path": "cases/py/fastapi-guarded", "sha256": "0869cab10767ac7e7b33c9bbd634f811d98ce5cdeb244769f1a81949438460fb" },
"case": { "path": "cases/py/fastapi-guarded/case.yaml", "sha256": "0add8a5f487ebd21ee20ab88b7c6436fe8471f0a54ab8da0e08c8416aa181346" },
"entrypoints": { "path": "cases/py/fastapi-guarded/entrypoints.yaml", "sha256": "47c9dd15bf7c5bb8641893a92791d3f7675ed6adba17b251f609335400d29d41" },
"binary": { "path": "cases/py/fastapi-guarded/outputs/binary.tar.gz", "sha256": "ca964fef352dc535b63d35b8f8846cc051e10e54cfd8aceef7566f3c94178b76" },
"sbom": { "path": "cases/py/fastapi-guarded/outputs/sbom.cdx.json", "sha256": "13999d8f3d4c9bdb70ea54ad1de613be3f893d79bdd1a53f7c9401e6add88cf0" },
"coverage": { "path": "cases/py/fastapi-guarded/outputs/coverage.json", "sha256": "07b1f6dccaa02bd4e1c3e2771064fa3c6e06d02843a724151721ea694762c750" },
"traces": { "path": "cases/py/fastapi-guarded/outputs/traces/traces.json", "sha256": "4633748b8b428b45e3702f2f8f5b3f4270728078e26bce1e08900ed1d5bb3046" },
"attestation": { "path": "cases/py/fastapi-guarded/outputs/attestation.json", "sha256": "257aa5408a5c6ffe0e193a75a2a54597f8c6f61babfe8aaf26bd47340c3086c3" },
"truth": { "path": "benchmark/truth/py-fastapi-guarded.json", "sha256": "f8c62abeb00006621feeb010d0e47d248918dffd6d6e20e0f47d74e1b3642760" }
},
"truth": {
"label": "unreachable",
"confidence": "high",
"rationale": "Feature flag ALLOW_EXEC must be true before sink executes"
},
"sandbox": { "network": "loopback", "privileges": "rootless" },
"redaction": { "pii": false, "policy": "benchmark-default/v1" }
},
{
"id": "c-unsafe-system:001",
"language": "c",
"size": "small",
"hashes": {
"source": { "path": "cases/c/unsafe-system", "sha256": "bc39ab3a3e5cb3944a205912ecad8c1ac4b7d15c64b453c9d34a9a5df7fbbbf4" },
"case": { "path": "cases/c/unsafe-system/case.yaml", "sha256": "7799a3a629c22ad47197309f44e32aabbc4e6711ef78d606ba57a7a4974787ce" },
"entrypoints": { "path": "cases/c/unsafe-system/entrypoints.yaml", "sha256": "06afee8350460c9d15b26ea9d4ea293e8eb3f4b86b3179e19401fa99947e4490" },
"binary": { "path": "cases/c/unsafe-system/outputs/binary.tar.gz", "sha256": "62200167bd660bad6d131b21f941acdfebe00e949e353a53c97b6691ac8f0e49" },
"sbom": { "path": "cases/c/unsafe-system/outputs/sbom.cdx.json", "sha256": "4c72a213fc4c646f44b4d0be3c23711b120b2a386374ebaa4897e5058980e0f5" },
"coverage": { "path": "cases/c/unsafe-system/outputs/coverage.json", "sha256": "03ba8cf09e7e0ed82e9fa8abb48f92355e894fd56e0c0160a504193a6f6ec48a" },
"traces": { "path": "cases/c/unsafe-system/outputs/traces/traces.json", "sha256": "f6469e46a57b8a6e8e17c9b8e78168edd6657ea8a5e1e96fe6ab4a0fc88a734e" },
"attestation": { "path": "cases/c/unsafe-system/outputs/attestation.json", "sha256": "c3755088182359a45492170fa8a57d826b605176333d109f4f113bc7ccf85f97" },
"truth": { "path": "benchmark/truth/c-unsafe-system.json", "sha256": "9a8200c2cf549b3ac8b19b170e9d34df063351879f19f401d8492e280ad08c13" }
},
"truth": {
"label": "reachable",
"confidence": "high",
"rationale": "Command injection sink reachable via argv -> system()"
},
"sandbox": { "network": "loopback", "privileges": "rootless" },
"redaction": { "pii": false, "policy": "benchmark-default/v1" }
}
],
"artifacts": {
"submissionSchema": { "path": "schemas/submission.schema.json", "sha256": "de5bebb2dbcd085d7896f47a16b9d3837a65fb7f816dcf7e587967d5848c50a7" },
"scorer": { "path": "tools/scorer/rb_score.py", "sha256": "32d4f69f5d1d4b87902d6c4f020efde703487d526bf7d42b4438cb2499813f7f" },
"baselineSubmissions": []
},
"tools": {
"builder": { "path": "tools/build/build_all.py", "sha256": "64a73f3df9b6f2cdaf5cbb33852b8e9bf443f67cf9dff1573fb635a0252bda9a" },
"validator": { "path": "tools/validate.py", "sha256": "776009ef0f3691e60cc87df3f0468181ee7a827be1bd0f73c77fdb68d3ed31c0" }
},
"signatures": []
}

View File

@@ -0,0 +1,5 @@
# Deterministic defaults for C cases
SOURCE_DATE_EPOCH=1730000000
TZ=UTC
LANG=C
LC_ALL=C

View File

@@ -0,0 +1,3 @@
# Deterministic defaults for Java cases
SOURCE_DATE_EPOCH=1730000000
JAVA_TOOL_OPTIONS="-Duser.country=US -Duser.language=en -Duser.timezone=UTC -Dfile.encoding=UTF-8"

View File

@@ -0,0 +1,5 @@
# Deterministic defaults for JavaScript/Node cases
SOURCE_DATE_EPOCH=1730000000
NODE_OPTIONS=--no-deprecation
NPM_CONFIG_FUND=false
NPM_CONFIG_AUDIT=false

View File

@@ -0,0 +1,5 @@
# Deterministic defaults for Python cases
SOURCE_DATE_EPOCH=1730000000
PYTHONHASHSEED=1
PIP_DISABLE_PIP_VERSION_CHECK=1
PYTHONUTF8=1

View File

@@ -18,13 +18,18 @@ environment:
runtime:
gcc: "13"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
outputs:
artifact_path: outputs/binary.tar.gz
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_path: outputs/traces/traces.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -35,3 +40,9 @@ ground_truth:
summary: "Without ALLOW_CMD, the system() sink remains unreachable; with ALLOW_CMD=1, it executes."
evidence_files:
- "../../../benchmark/truth/c-guarded-system.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -0,0 +1,7 @@
case_id: "c-guarded-system:001"
entries:
cli:
- id: "main"
command: "./app"
args: ["<user_input>"]
description: "system() guarded by ALLOW_CMD flag"

View File

@@ -0,0 +1,14 @@
{
"bomFormat": "CycloneDX",
"components": [],
"metadata": {
"component": {
"name": "guarded-system",
"type": "application",
"version": "1.0.0"
},
"timestamp": "1970-01-01T00:00:00Z"
},
"specVersion": "1.5",
"version": 1
}

View File

@@ -18,13 +18,18 @@ environment:
runtime:
gcc: "13"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
outputs:
artifact_path: outputs/binary.tar.gz
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_path: outputs/traces/traces.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -35,3 +40,9 @@ ground_truth:
summary: "Calling process_buffer with len>256 drives memcpy with attacker length (reachable)."
evidence_files:
- "../../../benchmark/truth/c-memcpy-overflow.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -0,0 +1,7 @@
case_id: "c-memcpy-overflow:001"
entries:
cli:
- id: "process_buffer"
command: "./app"
args: ["<length>"]
description: "User length forwarded to memcpy without bounds"

View File

@@ -0,0 +1,14 @@
{
"bomFormat": "CycloneDX",
"components": [],
"metadata": {
"component": {
"name": "memcpy-overflow",
"type": "application",
"version": "1.0.0"
},
"timestamp": "1970-01-01T00:00:00Z"
},
"specVersion": "1.5",
"version": 1
}

View File

@@ -18,13 +18,18 @@ environment:
runtime:
gcc: "13"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
outputs:
artifact_path: outputs/binary.tar.gz
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_path: outputs/traces/traces.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -35,3 +40,9 @@ ground_truth:
summary: "Running with argument 'echo OK' executes system() with user-controlled payload."
evidence_files:
- "../../../benchmark/truth/c-unsafe-system.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -0,0 +1,7 @@
case_id: "c-unsafe-system:001"
entries:
cli:
- id: "main"
command: "./app"
args: ["<user_input>"]
description: "Passes argv directly into system()"

View File

@@ -0,0 +1,14 @@
{
"bomFormat": "CycloneDX",
"components": [],
"metadata": {
"component": {
"name": "unsafe-system",
"type": "application",
"version": "1.0.0"
},
"timestamp": "1970-01-01T00:00:00Z"
},
"specVersion": "1.5",
"version": 1
}

View File

@@ -18,6 +18,9 @@ environment:
runtime:
java: "21"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./build/build.sh"
expected_coverage: []
@@ -36,3 +40,9 @@ ground_truth:
summary: "Deserialization reachable"
evidence_files:
- "../benchmark/truth/java-spring-deserialize.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
java: "21"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./build/build.sh"
expected_coverage: []
@@ -36,3 +40,9 @@ ground_truth:
summary: "Guard blocks deserialization unless ALLOW_DESER=true"
evidence_files:
- "../benchmark/truth/java-spring-guarded.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
node: "20.11.0"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Admin exec endpoint reachable and executes eval"
evidence_files:
- "../benchmark/truth/js-express-eval.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
node: "20.11.0"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Guard prevents sink unless ALLOW_EXEC=true"
evidence_files:
- "../benchmark/truth/js-express-guarded.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
node: "20.11.0"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Template rendering reachable via POST /api/render"
evidence_files:
- "../benchmark/truth/js-fastify-template.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
node: "20.11.0"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Guard prevents sink when FEATURE_ENABLE != 1"
evidence_files:
- "../benchmark/truth/js-guarded-eval.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
node: "20.11.0"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Unit test triggers eval sink with payload {code: '1+2'}"
evidence_files:
- "../benchmark/truth/js-unsafe-eval.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
python: "3.12"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Template rendering reachable with autoescape off"
evidence_files:
- "../benchmark/truth/py-django-ssti.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
python: "3.12"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Guard blocks eval unless ALLOW_EXEC=true"
evidence_files:
- "../benchmark/truth/py-fastapi-guarded.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
python: "3.12"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Template rendering reachable"
evidence_files:
- "../benchmark/truth/py-flask-template.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
python: "3.12"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Guard blocks eval when FEATURE_ENABLE != 1"
evidence_files:
- "../benchmark/truth/py-guarded-exec.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -18,6 +18,9 @@ environment:
runtime:
python: "3.12"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "4Gi"
build:
command: "./build/build.sh"
source_date_epoch: 1730000000
@@ -26,6 +29,7 @@ build:
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "./tests/run-tests.sh"
expected_coverage:
@@ -36,3 +40,9 @@ ground_truth:
summary: "Eval reachable via POST /api/exec"
evidence_files:
- "../benchmark/truth/py-unsafe-exec.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -8,11 +8,14 @@ This note closes BENCH-GAPS-513-018, DATASET-GAPS-513-019, and REACH-FIXTURE-GAP
## What changed
- **Benchmark kit manifest + schema**: `benchmark/schemas/benchmark-manifest.schema.json` with signed/hashed entries for cases, truth, baselines, schemas, and tools. Sample at `benchmark/manifest.sample.json`.
- **Offline verifier**: `tools/verify_manifest.py` validates the manifest against local files (hashes, required entries, DSSE envelope presence) to keep runs deterministic and tamper-evident.
- **Coverage/trace schemas**: `schemas/coverage.schema.json` and `schemas/trace.schema.json` govern oracle outputs referenced by manifest hashes.
- **Submission provenance checks**: manifest requires SHA-256 for submission schema, scorer package, and each baseline submission; DSSE path optional but encouraged.
- **Determinism env templates**: manifest captures `sourceDateEpoch` and per-tool pinned versions; cases must provide build seeds in case metadata.
- **Unreachability oracles**: truth files must include explicit rationale for unreachable cases; manifest enforces presence of `truth` artifact per case.
- **Sandbox/redaction guidance**: case metadata must declare `sandbox` and `redaction` policy fields (schema updated) to ensure PII removal and constrained execution.
- **Resource normalization**: manifest records build/runtime resource limits (cpu/memory) for repeatable benchmarking.
- **Offline kit & checklist**: dataset safety checklist at `benchmark/checklists/dataset-safety.md`; deterministic packaging via `tools/package_offline_kit.sh`.
- **Frozen baselines**: Semgrep rulepack hash pinned at `baselines/semgrep/rules.sha256`; manifest supports hashed baseline submissions.
## How to use
```bash

View File

@@ -53,7 +53,16 @@ This guide explains how to produce a compliant submission for the Stella Ops rea
- `submission.json`
- Tool version & configuration (README)
- Optional logs and runtime metrics
- For production submissions, sign `submission.json` with DSSE and record the envelope under `signatures` in the manifest (see `benchmark/manifest.sample.json`).
- Do **not** include binaries that require network access or licenses we cannot redistribute.
## Provenance & Manifest
- Reference kit manifest: `benchmark/manifest.sample.json` (schema: `benchmark/schemas/benchmark-manifest.schema.json`).
- Validate your bundle offline:
```bash
python tools/verify_manifest.py benchmark/manifest.sample.json --root bench/reachability-benchmark
```
- Determinism templates: `benchmark/templates/determinism/*.env` can be sourced by build scripts per language.
## Support
- Open issues in the public repo (once live) or provide a reproducible script that runs fully offline.

View File

@@ -11,6 +11,8 @@ required:
- environment
- build
- test
- sandbox
- redaction
properties:
id:
type: string
@@ -53,7 +55,7 @@ properties:
description: Fully-qualified function/method path for the sink
kind:
type: string
enum: [http, file, crypto, process, deserialization, custom]
enum: [http, file, crypto, process, deserialization, custom, command, memory]
location:
type: object
required: [file]
@@ -84,6 +86,14 @@ properties:
source_date_epoch:
type: integer
minimum: 0
resource_limits:
type: object
additionalProperties: false
properties:
cpu:
type: string
memory:
type: string
build:
type: object
required: [command, source_date_epoch]
@@ -110,6 +120,8 @@ properties:
type: string
traces_dir:
type: string
attestation_path:
type: string
test:
type: object
required: [command]
@@ -142,4 +154,22 @@ properties:
type: string
notes:
type: string
sandbox:
type: object
additionalProperties: false
properties:
network:
type: string
enum: [none, loopback, local]
privileges:
type: string
enum: [rootless, root]
redaction:
type: object
additionalProperties: false
properties:
pii:
type: boolean
policy:
type: string
additionalProperties: false

View File

@@ -29,6 +29,10 @@ properties:
type: string
command:
type: string
args:
type: array
items:
type: string
schedule:
type: string
handler:

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUTPUT="${1:-${ROOT}/out/reachability-benchmark-kit.tar.gz}"
SDE="${SOURCE_DATE_EPOCH:-1730000000}"
mkdir -p "$(dirname "${OUTPUT}")"
cd "${ROOT}"
# Deterministic tarball containing schemas, manifest, truth, cases, tools, and docs.
tar --sort=name --mtime="@${SDE}" --owner=0 --group=0 --numeric-owner \
-czf "${OUTPUT}" \
benchmark/manifest.sample.json \
benchmark/CHANGELOG.md \
benchmark/checklists \
benchmark/templates/determinism \
benchmark/schemas/benchmark-manifest.schema.json \
benchmark/truth \
schemas \
tools/verify_manifest.py tools/validate.py tools/requirements.txt \
cases \
baselines \
ci \
website \
docs \
README.md LICENSE NOTICE
sha256sum "${OUTPUT}"

View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""Offline validator for reachability benchmark manifests.
Usage:
python tools/verify_manifest.py benchmark/manifest.sample.json --root bench/reachability-benchmark
Checks performed:
- Manifest validates against `benchmark/schemas/benchmark-manifest.schema.json`.
- Every hashed path exists relative to --root (or absolute).
- SHA-256 of files/directories matches the manifest values.
- Optional DSSE envelopes listed under `dsse` are hashed and compared to envelopeDigest
when provided.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from typing import Dict, Iterable
from jsonschema import Draft202012Validator
ROOT = Path(__file__).resolve().parent.parent
SCHEMA_PATH = ROOT / "benchmark" / "schemas" / "benchmark-manifest.schema.json"
def load_manifest(path: Path) -> Dict:
text = path.read_text(encoding="utf-8")
return json.loads(text)
def compute_sha256(target: Path) -> str:
if target.is_dir():
digest = hashlib.sha256()
for child in sorted(target.rglob("*")):
if child.is_dir():
continue
rel = child.relative_to(target)
digest.update(str(rel).encode("utf-8"))
digest.update(child.read_bytes())
return digest.hexdigest()
return hashlib.sha256(target.read_bytes()).hexdigest()
def validate_against_schema(manifest: Dict) -> Iterable[str]:
schema = json.loads(SCHEMA_PATH.read_text(encoding="utf-8"))
validator = Draft202012Validator(schema)
for error in validator.iter_errors(manifest):
pointer = "/".join(str(p) for p in error.path) or "<root>"
yield f"schema:{pointer}: {error.message}"
def resolve_path(root: Path, path_value: str) -> Path:
candidate = Path(path_value)
if not candidate.is_absolute():
candidate = root / candidate
return candidate
def validate_hashed_path(root: Path, label: str, spec: Dict, envelope_digest: str | None = None) -> Iterable[str]:
errors: list[str] = []
path = resolve_path(root, spec["path"])
if not path.exists():
return [f"missing:{label}:{path}"]
actual = compute_sha256(path)
expected = spec["sha256"].lower()
if actual.lower() != expected:
errors.append(f"mismatch:{label}:{path}: expected {expected} got {actual}")
dsse_path = spec.get("dsse")
if dsse_path:
dsse_full = resolve_path(root, dsse_path)
if not dsse_full.exists():
errors.append(f"missing:{label}:dsse:{dsse_full}")
else:
dsse_digest = compute_sha256(dsse_full)
if envelope_digest and envelope_digest.lower() != dsse_digest.lower():
errors.append(
f"mismatch:{label}:dsse:{dsse_full}: expected envelopeDigest {envelope_digest} got {dsse_digest}"
)
return errors
def validate_cases(root: Path, manifest: Dict) -> Iterable[str]:
for case in manifest.get("cases", []):
base = f"case:{case.get('id', '<unknown>')}"
hashes: Dict = case.get("hashes", {})
for key, spec in hashes.items():
errors = validate_hashed_path(root, f"{base}:{key}", spec)
yield from errors
def validate_artifacts(root: Path, manifest: Dict) -> Iterable[str]:
artifacts = manifest.get("artifacts", {})
for label in ("submissionSchema", "scorer"):
if label in artifacts:
yield from validate_hashed_path(root, f"artifacts:{label}", artifacts[label])
for baseline in artifacts.get("baselineSubmissions", []) or []:
prefix = f"baseline:{baseline.get('tool','?')}-{baseline.get('version','?')}"
yield from validate_hashed_path(root, f"{prefix}:submission", baseline["submission"])
dsse_spec = baseline.get("dsse")
if dsse_spec:
yield from validate_hashed_path(root, f"{prefix}:dsse", dsse_spec, baseline.get("envelopeDigest"))
def validate_tools(root: Path, manifest: Dict) -> Iterable[str]:
tools = manifest.get("tools", {})
for label in ("builder", "validator"):
if label in tools:
yield from validate_hashed_path(root, f"tools:{label}", tools[label])
def main() -> int:
parser = argparse.ArgumentParser(description="Validate reachability benchmark manifest and artifacts")
parser.add_argument("manifest", type=Path, help="Path to manifest JSON")
parser.add_argument("--root", type=Path, default=ROOT, help="Root directory for relative paths")
args = parser.parse_args()
manifest = load_manifest(args.manifest)
failures: list[str] = []
failures.extend(validate_against_schema(manifest))
failures.extend(validate_cases(args.root, manifest))
failures.extend(validate_artifacts(args.root, manifest))
failures.extend(validate_tools(args.root, manifest))
if failures:
for item in failures:
print(f"FAIL {item}")
return 1
print(f"OK manifest {args.manifest} validated")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -1,140 +1,42 @@
# VEX Evidence Playbook (Bench Repo Blueprint)
> **Status:** Draft aligns with the “provable vulnerability decisions” advisory (Nov2025).
> **Owners:** Policy Guild · VEX Lens Guild · CLI Guild · Docs Guild.
This playbook defines the public benchmark repository layout, artifact shapes, verification tooling, and metrics that prove StellaOps VEX decisions are reproducible, portable, and superior to baseline scanners. Treat it as the contract for every guild contributing artifacts to `bench/`.
---
## 1. Repository layout
```
bench/
README.md # repo overview + quickstart
findings/
CVE-YYYY-NNNNN/ # one folder per advisory/product tuple
evidence/
reachability.json # static+runtime call graph for the finding
sbom.cdx.json # CycloneDX slice containing the involved components
decision.openvex.json # OpenVEX statement (status + justification)
decision.dsse.json # DSSE envelope wrapping the OpenVEX payload
rekor.txt # optional Rekor UUID/index/checkpoint
metadata.json # producer info (policy rev, analyzer digests, CAS URIs)
tools/
verify.sh # shell helper: dsse verify + optional rekor verification
verify.py # python verifier (offline) that recomputes digests
compare.py # baseline diff against Trivy/Syft/Grype/Snyk/Xray outputs
replay.sh # reruns reachability graphs via `stella replay`
results/
summary.csv # FP reduction, MTTD, reproducibility metrics
runs/2025-11-10/ # pinned scanner/policy versions + raw outputs
stella/
findings.json
runtime-facts.ndjson
reachability.manifest.json
trivy/
findings.json
...
```
### File contracts
- `reachability.json` is the canonical export from `cas://reachability/graphs/...` with symbol IDs, call edges, runtime hits, analyzer fingerprints, and CAS references.
- `decision.openvex.json` follows OpenVEX v1 with StellaOps-specific `status_notes`, `justification`, `impact_statement`, and `action_statement` text.
- `decision.dsse.json` is the DSSE envelope returned by Signer (see §3). Always include the PEM cert chain (keyless) or KMS key id.
- `rekor.txt` captures `{uuid, logIndex, checkpoint}` from Attestor when the decision is logged to Rekor.
- `metadata.json` binds the DSSE payload back to internal evidence: `{policy_revision, reachability_graph_sha256, runtime_trace_sha256, evidence_cas_uri[], analyzer_versions[], createdBy, createdAt}`.
---
## 2. Evidence production flow
1. **Scanner Worker**
- Generate `reachability.json` + `sbom.cdx.json` per prioritized CVE.
- Store artifacts under CAS and surface URIs via `ReachabilityReplayWriter`.
2. **Policy Engine / VEXer**
- Evaluate reachability states + policy lattice to produce an OpenVEX statement.
- Persist `decision.openvex.json` and forward it to Signer.
3. **Signer & Attestor**
- Sign the OpenVEX payload via DSSE (`payloadType: application/vnd.in-toto+json`) and return `decision.dsse.json`.
- Optionally call Attestor to log the DSSE bundle to Rekor; write `{uuid, logIndex, checkpoint}` to `rekor.txt`.
4. **Bench harness**
- Collect SBOM slice, reachability proof, OpenVEX, DSSE, Rekor metadata, and companion metrics into `bench/findings/CVE-...`.
- Record tool versions + CAS digests under `metadata.json`.
All steps must be deterministic: repeated scans with the same inputs produce identical artifacts and digests.
---
## 3. Signing & transparency requirements
| Artifact | Producer | Format | Notes |
|-------------------------|---------------|----------------------------------------|-------|
| Reachability evidence | Scanner | Canonical JSON (sorted keys) | CAS URI recorded in metadata. |
| SBOM slice | Scanner | CycloneDX 1.6 JSON | Keep only components relevant to the finding. |
| OpenVEX decision | Policy/VEXer | OpenVEX v1 | One statement per `(CVE, product)` tuple. |
| DSSE bundle | Signer | DSSE envelope over OpenVEX payload | Include Fulcio cert or KMS key id. |
| Rekor record (optional) | Attestor | Rekor UUID/index/checkpoint | Store alongside DSSE for offline verification. |
Signer must expose a predicate alias `stella.ops/vexDecision@v1` (see Sprint task `SIGN-VEX-401-018`). Payload = OpenVEX JSON. Rekor logging reuses the existing Attestor `/rekor/entries` pipeline.
---
## 4. Verification tooling
The repo ships two verifiers:
1. `tools/verify.sh` (bash) — wraps `cosign verify-attestation`/`in-toto verify`, Rekor inclusion checks (`rekor-cli logproof`), and digest comparison.
2. `tools/verify.py` — pure-Python offline verifier for air-gapped environments:
- Validates DSSE signature using the embedded Fulcio cert or configured root.
- Recomputes `sha256` over `reachability.json`, `sbom.cdx.json`, and `decision.openvex.json` to ensure the DSSE payload matches.
- Optionally replays reachability by invoking `stella replay --manifest ... --finding CVE-...`.
CLI addition (`stella decision verify`) should shell out to these helpers when `--from bench` is provided.
---
## 5. Metrics & comparison harness
`tools/compare.py` ingests raw outputs from StellaOps and baseline scanners (Trivy, Syft, Grype, Snyk, Xray) stored under `results/runs/<date>/<scanner>/findings.json`. For each target:
- **False-positive reduction (FPR)** = `1 - (# of findings confirmed true positives / # of baseline findings)`.
- **Mean time to decision (MTTD)** = average wall-clock time between scan start and DSSE-signed OpenVEX emission.
- **Reproducibility score** = `1` if re-running reachability produces identical digests for all artifacts, else `0`; aggregated per run.
`results/summary.csv` columns:
```
target,cve,baseline_scanner,baseline_hits,stella_hits,fp_reduction,mttd_seconds,reproducible,rekor_uuid
```
Automate collection via `Makefile` or `bench/run.sh` pipeline (task `BENCH-AUTO-401-019`).
---
## 6. Publication & README checklist
`bench/README.md` must include:
- High-level workflow diagram (scan → reachability → OpenVEX → DSSE → Rekor → bench).
- Prerequisites (`cosign`, `rekor-cli`, `stella` CLI).
- Quickstart commands:
```bash
./tools/verify.sh CVE-2023-12345 pkg:purl/example@1.2.3
./tools/compare.py --target sample/nginx --baseline trivy --run 2025-11-10
```
- How to recreate a finding: `stella replay --manifest results/runs/.../replay.yaml --finding CVE-...`.
- Contribution guide (where to place new findings, how to update metrics, required metadata).
---
## 7. Implementation tasks (see Sprint 401+)
- `POLICY-VEX-401-010` — emit OpenVEX per finding and publish to bench repo.
- `SIGN-VEX-401-018` — add DSSE predicate + Rekor logging for decision payloads.
- `CLI-VEX-401-011` — new `stella decision` verbs (`export`, `verify`, `compare`).
- `BENCH-AUTO-401-019` — automation to populate `bench/findings/**`, run baseline scanners, and update `results/summary.csv`.
- `DOCS-VEX-401-012` — maintain this playbook + README templates, document verification workflow.
Update `docs/implplan/SPRINT_0401_0001_0001_reachability_evidence_chain.md` whenever these tasks move state.
# VEX Evidence Playbook (VEX1VEX10)
Status: Draft · Date: 2025-12-03
Scope: Define deterministic VEX evidence bundles, justification catalog, and verification workflow for reachability evidence chain (Sprint 0401 tasks 62, 64, 65).
## Goals
- Publish signed VEX justification catalog with DSSE predicates and canonical hashes.
- Require entry-point coverage %, negative tests, and config/flag hash enforcement for each justification.
- Provide offline-friendly proof bundle format with CAS paths and DSSE envelopes.
## Bundle schema (draft)
- `proofBundle.schema.json` (to be placed under `docs/benchmarks/vex-evidence-playbook.schema.json`):
- `id`, `version`, `createdAt`, `createdBy`.
- `graph_hash` (BLAKE3-256), `graph_dsse` (sha256 of envelope).
- `entrypoints[]` with `id`, `coverage_percent`, `negative_tests` (bool), `config_hash` (sha256), `flags_hash` (sha256).
- `justification_id` (must exist in justification catalog), `justification_dsse` (optional hash).
- `evidence[]` items with `type` (`trace`, `coverage`, `sbom`, `scan`, `policy`), `cas_uri`, `hash`, `dsse` (optional), `expiresAt`.
- `signatures[]` DSSE/JWS entries with `keyId`, `sig`, `envelopeDigest`.
## Justification catalog
- Canonical JSON, JCS normalized; BLAKE3 + SHA-256 recorded.
- Fields: `id`, `title`, `description`, `applicability` (list of predicates), `required_evidence` (array of types/hashes), `expiry`, `policy_links`.
- Signed with DSSE predicate `stella.ops/vexJustification@v1`.
## Determinism rules
- Canonical JSON with sorted keys; arrays sorted by `id`.
- Timestamps UTC `Z`; strip milliseconds unless non-zero.
- Hashes: primary BLAKE3-256, secondary SHA-256 for interoperability.
- DSSE subject = canonical JSON hash; verifier must check hash + signature.
## Offline verification
- `scripts/vex/verify_proof_bundle.py` (to be authored) validates schema, hashes, DSSE, and CAS availability.
- No network calls; CAS paths resolved locally via `--cas-root`.
## Fixtures (to add)
- `tests/Vex/ProofBundles/sample-proof-bundle.json` with matching DSSE.
- `docs/benchmarks/vex-justifications.catalog.json` with 5 sample justifications (VEX1VEX5).
## Next steps
- Freeze `proofBundle.schema.json` and justification catalog hashes.
- Implement `verify_proof_bundle.py` and add CI job to run it over fixtures.
- Wire sprint 0401 tasks 62/64/65 to these artifacts; update Decisions & Risks once frozen.

View File

@@ -28,6 +28,14 @@
- **Air-gap provenance & staleness** (LEDGER-AIRGAP-56/57/58 · Findings Ledger Guild · AirGap Guilds · Evidence Locker Guild) — Status TODO. Requirements captured in `docs/modules/findings-ledger/airgap-provenance.md`; waiting on mirror bundle schema freeze + AirGap controller inputs.
- **Attestation linkage** (LEDGER-ATTEST-73-001 · Findings Ledger Guild · Attestor Service Guild) — Status TODO. Waiting on attestation payload pointers from NOTIFY-ATTEST-74-001 work to reuse DSSE IDs.
## Wave Coordination
- **Wave A (observability + replay):** Tasks 02 DONE; metrics and harness frozen; keep schemas stable for downstream Ops/DevOps sprints.
- **Wave B (provenance exports):** Task 4 DONE; uses orchestrator export contract (now marked DONE). Keep linkage stable.
- **Wave C (air-gap provenance):** Tasks 58 partially DONE (56-001 done; 56-002/57-001/58-001 BLOCKED on staleness/bundle linkage). Execute sequentially once freshness spec lands.
- **Wave D (attestation pointers):** Task 9 BLOCKED pending NOTIFY-ATTEST-74-001 alignment.
- **Wave E (deployment collateral):** Task 3 BLOCKED pending DevOps paths for manifests/offline kit. Run after Wave C to avoid conflicting asset locations.
- Do not start blocked waves until dependencies land; avoid drift by keeping current DONE artifacts immutable.
## Documentation Prerequisites
- `docs/modules/findings-ledger/observability.md`
- `docs/modules/findings-ledger/replay-harness.md`
@@ -56,6 +64,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A observability/replay done; B provenance exports done; C air-gap partly blocked; D attestation blocked; E deployment blocked). No status changes. | Project Mgmt |
| 2025-12-03 | Documented orchestrator export contract at `docs/modules/orchestrator/job-export-contract.md`; external dependency marked DONE and linked from LEDGER-34-101. | Implementer |
| 2025-11-25 | Reconciled tracker: marked LEDGER-29-007 (metrics/alerts) and LEDGER-29-008 (replay harness) DONE in tasks-all; statuses in this sprint already reflected completion dates. | Project Mgmt |
| 2025-11-22 | LEDGER-29-008 delivered: replay harness metrics aligned (`ledger_write_duration_seconds`, gauges), projection risk fields fixed, new harness tests added; `dotnet test src/Findings/StellaOps.Findings.Ledger.Tests` passing (warnings only). | Findings Ledger Guild |

View File

@@ -12,6 +12,10 @@
- Coordinate with Evidence Locker, Provenance, Risk Engine, and Observability guilds for shared schemas.
- Concurrency safe with other CC-0121 efforts once contract changes stabilise.
## Wave Coordination
- **Wave A (exports/observability/risk):** Tasks 19 DONE; keep schemas and fixtures frozen for downstream SDK/DevOps sprints.
- No remaining open tasks in this sprint; future work should open a new wave entry or new sprint file.
## Documentation Prerequisites
- docs/README.md
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
@@ -48,6 +52,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (Wave A done; no open tasksfuture work needs new wave/sprint). No status changes. | Project Mgmt |
| 2025-12-02 | Completed LEDGER-GAPS-121-009: added schema catalog + FL1FL10 gap report, Merkle/anchor policy, redaction manifest, DSSE linkage doc, golden export fixtures + checksums, offline verifier script with replay checksum guard, quota/backpressure metrics/code/tests. | Findings Ledger |
| 2025-12-02 | Started LEDGER-GAPS-121-009 (FL1FL10 remediation); status DOING; drafting schema catalog, Merkle/anchor policy, redaction manifest, offline verifier, and backpressure metrics. | Findings Ledger |
| 2025-12-01 | Added LEDGER-GAPS-121-009 to track FL1FL10 remediation from `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md`; status TODO pending schema catalog refresh. | Project Mgmt |

View File

@@ -11,6 +11,12 @@
- Coordinate with Risk Engine, Export Center, and Platform/DB guilds for RLS + partitioning design.
- Concurrency: safe with other CC-0122 efforts once DB/Risk contracts are stable.
## Wave Coordination
- **Wave A (prep):** P1P3 DONE; keep prep docs frozen.
- **Wave B (risk queries/exports):** Tasks 13 BLOCKED on risk scoring contract (66-002) and Export Center specs.
- **Wave C (tenancy):** Tasks 4/4b BLOCKED on RLS/partition design; runs after Wave B to align schemas.
- No work in progress until upstream contracts land; do not start Waves B/C prematurely.
## Documentation Prerequisites
- docs/README.md
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
@@ -36,6 +42,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A prep done; B risk queries/exports blocked; C tenancy blocked). No status changes. | Project Mgmt |
| 2025-11-20 | Published ledger risk/tenancy prep doc (docs/modules/findings-ledger/prep/2025-11-20-ledger-risk-prep.md); set PREP-LEDGER-RISK-68/69 and TEN-48-001 to DOING. | Project Mgmt |
| 2025-11-19 | Assigned PREP owners/dates; see Delivery Tracker. | Planning |
| 2025-11-18 | Renamed file to `SPRINT_0122_0001_0001_policy_reasoning.md` and normalised to standard template; no scope changes. | Findings Ledger |

View File

@@ -10,6 +10,14 @@
- Upstream: Export bundle schema + scheduler job spec; mirror bundle/air-gap schema and sealed-mode rules; Attestor verification policy schema; Authority `effective:write` contract; Console API filters/pagination spec.
- Concurrency: Execute tasks in table order (DOING → TODO → BLOCKED). All tasks currently blocked pending upstream contracts; no parallel execution until contracts land.
## Wave Coordination
- **Wave A (prep + Console contract):** P1P15 DONE, including POLICY-CONSOLE-23-001 contract; keep prep docs stable.
- **Wave B (Console export job):** Task 1 BLOCKED awaiting export bundle schema + scheduler job spec.
- **Wave C (air-gap chain):** Tasks 26 BLOCKED on mirror/sealed-mode/staleness schemas; runs after Wave B when bundle schema freezes.
- **Wave D (AOC linting):** Tasks 710 BLOCKED on analyzer targets and Authority gate contract.
- **Wave E (attestation):** Tasks 1114 BLOCKED on Attestor verification policy + Console report schema.
- No work in progress; do not start any wave until dependencies land.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -54,6 +62,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A prep+Console contract done; B export blocked; C air-gap blocked; D AOC blocked; E attestation blocked). No status changes. | Project Mgmt |
| 2025-11-22 | Added aggregate prep index files (`docs/modules/policy/prep/2025-11-20-policy-airgap-prep.md`, `...-policy-aoc-prep.md`, `...-policy-attest-prep.md`) to satisfy PREP references. | Project Mgmt |
| 2025-11-20 | Started PREP air-gap chain (56-001..58-001), AOC chain (19-001..19-004), and attestation chain (73-001..74-002); published prep drafts in `docs/modules/policy/prep/` (see `2025-11-20-policy-airgap-prep.md`, `...policy-aoc-prep.md`, `...policy-attest-prep.md` for index). | Project Mgmt |
| 2025-11-19 | Removed trailing hyphen from PREP-POLICY-ATTEST-73-001-VERIFICATIONPOLICY so dependent task resolves correctly. | Project Mgmt |

View File

@@ -9,6 +9,11 @@
- Upstream: Sprint 120.C Policy.I must land before this track.
- Concurrency: execute tasks in listed order (DOING → TODO → BLOCKED).
## Wave Coordination
- **Wave A (core evaluator + storage):** Tasks 112 DONE; keep determinism policies and schemas frozen.
- **Wave B (Console simulation/export):** Task 13 DONE; reruns allowed for tests only; contract at `docs/modules/policy/contracts/policy-console-23-001-console-api.md` is source of truth.
- No remaining open tasks; future deltas require new wave entry.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -44,6 +49,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (Wave A core/storages done; Wave B Console simulation/export done; no open tasks). No status changes. | Project Mgmt |
| 2025-12-02 | Fixed selection join canonical ordering (PurlEquivalence canonical now derived from sorted list, not hashset); `dotnet test src/Policy/__Tests/StellaOps.Policy.Engine.Tests/StellaOps.Policy.Engine.Tests.csproj -c Release --no-build` now passes (211/211, 5.0s). | Implementer |
| 2025-12-02 | Reran `dotnet test src/Policy/__Tests/StellaOps.Policy.Engine.Tests/StellaOps.Policy.Engine.Tests.csproj -c Release --no-build`; failed 1/211 tests (`SelectionJoinTests.GetCanonical_ReturnsFirstLexicographically` expected `pkg:npm/a-package` but returned `pkg:npm/b-package`). Build duration 13.5s. Needs follow-up fix. | Implementer |
| 2025-12-02 | Published POLICY-CONSOLE-23-001 contract at `docs/modules/policy/contracts/policy-console-23-001-console-api.md`; unblocked POLICY-CONSOLE-23-002 (set to TODO). | Project Mgmt |

View File

@@ -8,6 +8,13 @@
- Upstream: POLICY-ENGINE-29-002 contract/schema published (2025-11-23); execute tasks in listed order.
- Concurrency: Proceed sequentially from 29-003 downward to preserve overlay/metrics dependencies.
## Wave Coordination
- **Wave A (prep + contracts):** P0P14 DONE; contracts frozen.
- **Wave B (path/scope & observability):** Tasks 12 DONE; keep metrics/log schema stable.
- **Wave C (overlays/simulation/change events):** Tasks 35 DONE.
- **Wave D (trust/snapshot/export/events/fusion):** Tasks 615 DONE.
- No open tasks; any new work should add a new wave entry.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -55,6 +62,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A prep done; B path/scope+obs done; C overlays/simulation done; D trust/export/snapshot/events/fusion done). No status changes. | Project Mgmt |
| 2025-11-24 | Completed POLICY-ENGINE-32-101: orchestrator job schema + NDJSON sample and submission/preview endpoints backed by deterministic ULID builder. | Implementer |
| 2025-11-24 | Completed POLICY-ENGINE-33-101: worker stub executes queued jobs idempotently, emits stable result hashes, worker result schema/sample added. | Implementer |
| 2025-11-24 | Completed POLICY-ENGINE-34-101: ledger export NDJSON manifest/records with deterministic ordering, schema/sample committed and endpoint exposed. | Implementer |

View File

@@ -8,6 +8,10 @@
- Upstream: Sprint 120.C Policy.III must land.
- Concurrency: execute tasks in listed order; all currently TODO.
## Wave Coordination
- **Wave A (compiler/runtime/events/storage):** Tasks 116 DONE; keep schemas/telemetry stable.
- No open tasks; future work requires new wave entry.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -37,6 +41,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (Wave A compiler/runtime/events/storage done; no open tasks). No status changes. | Project Mgmt |
| 2025-11-08 | Sprint stub; awaiting upstream phases. | Planning |
| 2025-11-19 | Normalized to standard template and renamed from `SPRINT_126_policy_reasoning.md` to `SPRINT_0126_0001_0001_policy_reasoning.md`; content preserved. | Implementer |
| 2025-11-26 | POLICY-ENGINE-40-003 delivered: evidence summary service + `/evidence/summary` endpoint and deterministic headline/severity/signals; unit tests added (`EvidenceSummaryServiceTests`). Targeted test slice canceled due to static-graph fan-out; rerun on clean host recommended. | Implementer |

View File

@@ -4,10 +4,13 @@
- Policy Engine V: reachability integration, telemetry, incident mode, and initial RiskProfile schema work.
- **Working directory:** `src/Policy/StellaOps.Policy.Engine` and `src/Policy/__Libraries/StellaOps.Policy.RiskProfile`.
## Dependencies & Concurrency
- Upstream: Sprint 120.C Policy.IV must land.
- Concurrency: execute tasks in listed order; all tasks currently TODO.
## Dependencies & Concurrency
- Upstream: Sprint 120.C Policy.IV must land.
- Concurrency: execute tasks in listed order; all tasks currently TODO.
## Wave Coordination
- **Wave A (reachability + observability + risk profiles):** Tasks P1 and 115 DONE; keep schemas/metrics stable. No remaining open tasks.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -35,9 +38,10 @@
| 15 | POLICY-RISK-67-001 | DONE (2025-11-27) | Depends on 67-001. | Risk Profile Schema Guild · Policy Engine Guild / `src/Policy/StellaOps.Policy.RiskProfile` | Profile storage/versioning lifecycle. |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-27 | `POLICY-ENGINE-80-002`: Created reachability facts joining layer in `ReachabilityFacts/` directory: `ReachabilityFactsModels.cs` (data models with state/confidence/score, ReachabilityState enum, ReachabilityFactKey), `ReachabilityFactsStore.cs` (IReachabilityFactsStore interface, InMemoryReachabilityFactsStore, MongoDB index definitions), `ReachabilityFactsOverlayCache.cs` (IReachabilityFactsOverlayCache interface, InMemoryReachabilityFactsOverlayCache with TTL eviction, ReachabilityFactsCacheOptions), `ReachabilityFactsJoiningService.cs` (batch lookup with cache-first strategy, signal enrichment, ReachabilityFactsTelemetry). Registered services in Program.cs DI. | Implementer |
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (Wave A reachability/observability/risk profiles done; sprint complete). No status changes. | Project Mgmt |
| 2025-11-27 | `POLICY-ENGINE-80-002`: Created reachability facts joining layer in `ReachabilityFacts/` directory: `ReachabilityFactsModels.cs` (data models with state/confidence/score, ReachabilityState enum, ReachabilityFactKey), `ReachabilityFactsStore.cs` (IReachabilityFactsStore interface, InMemoryReachabilityFactsStore, MongoDB index definitions), `ReachabilityFactsOverlayCache.cs` (IReachabilityFactsOverlayCache interface, InMemoryReachabilityFactsOverlayCache with TTL eviction, ReachabilityFactsCacheOptions), `ReachabilityFactsJoiningService.cs` (batch lookup with cache-first strategy, signal enrichment, ReachabilityFactsTelemetry). Registered services in Program.cs DI. | Implementer |
| 2025-11-27 | `POLICY-ENGINE-80-003`: Extended SPL predicates for reachability. Added `PolicyEvaluationReachability` record to `PolicyEvaluationContext.cs` with state/confidence/score/method/source properties and helper predicates (IsReachable, IsUnreachable, IsHighConfidence). Added `ReachabilityScope` to `PolicyExpressionEvaluator.cs` supporting SPL expressions like `reachability.state == "reachable"`, `reachability.confidence >= 0.8`, `reachability.is_high_confidence`. | Implementer |
| 2025-11-27 | `POLICY-ENGINE-80-004`: Added reachability metrics to `PolicyEngineTelemetry.cs`: `policy_reachability_applied_total{state}`, `policy_reachability_cache_hits_total`, `policy_reachability_cache_misses_total`, `policy_reachability_cache_hit_ratio` (observable gauge), `policy_reachability_lookups_total{outcome}`, `policy_reachability_lookup_seconds`. Updated `ReachabilityFactsTelemetry` to delegate to centralized PolicyEngineTelemetry. | Implementer |
| 2025-11-27 | `POLICY-RISK-67-001` (task 15): Created `Lifecycle/RiskProfileLifecycle.cs` with lifecycle models (RiskProfileLifecycleStatus enum: Draft/Active/Deprecated/Archived, RiskProfileVersionInfo, RiskProfileLifecycleEvent, RiskProfileVersionComparison, RiskProfileChange). Created `RiskProfileLifecycleService` with status transitions (CreateVersion, Activate, Deprecate, Archive, Restore), version management, event recording, and version comparison (detecting breaking changes in signals/inheritance). | Implementer |

View File

@@ -4,10 +4,16 @@
- Policy Engine VI: Risk profile lifecycle APIs, simulation bridge, overrides, exports, and SPL schema evolution.
- **Working directory:** `src/Policy/StellaOps.Policy.Engine` and `src/Policy/__Libraries/StellaOps.Policy`.
## Dependencies & Concurrency
- Upstream: Policy.V (0127) reachability/risk groundwork must land first.
- Concurrency: execute tasks in listed order; all tasks currently TODO.
## Dependencies & Concurrency
- Upstream: Policy.V (0127) reachability/risk groundwork must land first.
- Concurrency: execute tasks in listed order; all tasks currently TODO.
## Wave Coordination
- **Wave A (SPL schema/tooling):** Tasks 1015 DONE; keep SPL schema/fixtures/canonicalizer/layering stable.
- **Wave B (risk profile lifecycle APIs):** Tasks 12 DONE; publish schema and lifecycle endpoints; hold steady for downstream consumers.
- **Wave C (risk simulations/overrides/exports/notifications/air-gap):** Tasks 39 BLOCKED on Policy Studio contract, Authority attachment rules, override audit fields, notifications, and air-gap packaging; run sequentially once contracts land.
- No additional work in progress; avoid starting Wave C until dependencies clear.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -34,9 +40,10 @@
| 15 | POLICY-SPL-24-001 | DONE (2025-11-26) | — | Policy · Signals Guild / `src/Policy/__Libraries/StellaOps.Policy` | Extend SPL with reachability/exploitability predicates. |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-27 | `POLICY-RISK-67-002` (task 2): Added `RiskProfileSchemaEndpoints.cs` with `/.well-known/risk-profile-schema` endpoint (anonymous, ETag/Cache-Control, schema v1) and `/api/risk/schema/validate` POST endpoint for profile validation. Extended `RiskProfileSchemaProvider` with GetSchemaText(), GetSchemaVersion(), and GetETag() methods. Added `risk-profile` CLI command group with `validate` (--input, --format, --output, --strict) and `schema` (--output) subcommands. Added RiskProfile project reference to CLI. | Implementer |
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A SPL tooling done; B risk lifecycle APIs done; C simulations/overrides/exports/notifications/air-gap blocked). No status changes. | Project Mgmt |
| 2025-11-27 | `POLICY-RISK-67-002` (task 2): Added `RiskProfileSchemaEndpoints.cs` with `/.well-known/risk-profile-schema` endpoint (anonymous, ETag/Cache-Control, schema v1) and `/api/risk/schema/validate` POST endpoint for profile validation. Extended `RiskProfileSchemaProvider` with GetSchemaText(), GetSchemaVersion(), and GetETag() methods. Added `risk-profile` CLI command group with `validate` (--input, --format, --output, --strict) and `schema` (--output) subcommands. Added RiskProfile project reference to CLI. | Implementer |
| 2025-11-27 | `POLICY-RISK-67-002` (task 1): Created `Endpoints/RiskProfileEndpoints.cs` with REST APIs for profile lifecycle management: ListProfiles, GetProfile, ListVersions, GetVersion, CreateProfile (draft), ActivateProfile, DeprecateProfile, ArchiveProfile, GetProfileEvents, CompareProfiles, GetProfileHash. Uses `RiskProfileLifecycleService` for status transitions and `RiskProfileConfigurationService` for profile storage/hashing. Authorization via StellaOpsScopes (PolicyRead/PolicyEdit/PolicyActivate). Registered `RiskProfileLifecycleService` in DI and wired up `MapRiskProfiles()` in Program.cs. | Implementer |
| 2025-11-25 | Delivered SPL v1 schema + sample fixtures (spl-schema@1.json, spl-sample@1.json, SplSchemaResource) and embedded in `StellaOps.Policy`; marked POLICY-SPL-23-001 DONE. | Implementer |
| 2025-11-26 | Implemented SPL canonicalizer + SHA-256 digest (order-stable statements/actions/conditions) with unit tests; marked POLICY-SPL-23-002 DONE. | Implementer |

View File

@@ -7,6 +7,13 @@
## Dependencies & Concurrency
- Upstream: Policy.VI (0128) + Risk/VEX foundations; execute tasks in listed order; all tasks currently TODO.
## Wave Coordination
- **Wave A (RiskEngine + Vuln API):** Tasks 1218 and 3537 DONE; keep schemas/fixtures stable.
- **Wave B (Registry API):** Tasks 211 BLOCKED on OpenAPI spec and registry design; run sequentially once spec lands.
- **Wave C (Policy tenancy):** Task 1 BLOCKED on platform RLS design; align with Registry once available.
- **Wave D (VEX Lens):** Tasks 1934 and AIAI/EXPORT/ORCH chain BLOCKED on normalization schema, issuer directory, API governance; runs after Wave B/C to avoid drift.
- No active work until upstream specs arrive; maintain current DONE artefacts frozen.
## Documentation Prerequisites
- `docs/README.md`
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
@@ -58,6 +65,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A RiskEngine+Vuln API done; B Registry blocked; C tenancy blocked; D VEX Lens blocked). No status changes. | Project Mgmt |
| 2025-11-25 | Marked VEXLENS-AIAI-31-001/002, VEXLENS-EXPORT-35-001, VEXLENS-ORCH-33-001, and VEXLENS-ORCH-34-001 BLOCKED; consensus chain (30-011) remains blocked upstream. | Project Mgmt |
| 2025-11-25 | RISK-ENGINE-67-002 DONE: VEX gate provider added with short-circuit tests; packaged in RiskEngine queue/worker pipeline. | Implementer |
| 2025-11-25 | RISK-ENGINE-67-001 DONE: added CVSS+KEV provider and tests; score formula clamp((cvss/10)+0.2 if KEV). | Implementer |

View File

@@ -11,6 +11,13 @@
- Deno work depends on `SCANNER-ANALYZERS-DENO-26-008`; Java chain builds serially from 21-005 → 21-006 → 21-007 → 21-008 → 21-009 → 21-010 → 21-011.
- Stay within scanner scope to avoid new cross-module coupling unless explicitly approved.
## Wave Coordination
- **Wave A (Deno runtime hooks):** Tasks 13 DONE; keep runtime trace/signal schemas frozen.
- **Wave B (Java analyzers chain):** Tasks 410 BLOCKED on 21-005/21-008 completion and CI runner (DEVOPS-SCANNER-CI-11-001).
- **Wave C (DotNet entrypoints):** Task 11 BLOCKED pending CI runner to resolve test hangs.
- **Wave D (PHP analyzer bootstrap):** Task 12 BLOCKED pending spec/fixtures.
- Work remains blocked in Waves BD; avoid starts until dependencies and CI runner are available.
## Documentation Prerequisites
- docs/README.md
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
@@ -41,6 +48,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A Deno done; B Java chain blocked; C DotNet entrypoints blocked; D PHP bootstrap blocked). No status changes. | Project Mgmt |
| 2025-11-20 | Published prep docs for P2/P3: `docs/modules/scanner/prep/2025-11-20-java-21-008-prep.md` and `docs/modules/scanner/prep/2025-11-20-lang-11-001-prep.md`; set PREP P2/P3 to DOING after confirming unowned. | Project Mgmt |
| 2025-11-20 | Published prep note for SCANNER-ANALYZERS-JAVA-21-005 (docs/modules/scanner/prep/2025-11-20-java-21-005-prep.md); pinged Concelier/CoreLinksets owners for missing packages and CI isolation. | Project Mgmt |
| 2025-11-20 | Confirmed PREP-SCANNER-ANALYZERS-JAVA-21-005-TESTS-BLOC still TODO; moved to DOING to capture blockers and prep artefact. | Project Mgmt |

View File

@@ -12,6 +12,13 @@
- Sprints 130139 remain sequential; do not pull tasks from later sprints until predecessors are complete.
- Within this sprint, guild tasks can proceed in parallel once their listed dependencies are satisfied.
## Wave Coordination
- **Wave A (prep + governance):** P1P5 DONE; module AGENTS task 20 DONE; keep prep docs stable.
- **Wave B (native analyzers):** Tasks 514 DONE; stable outputs/fixtures.
- **Wave C (Node analyzers):** Tasks 1519 DONE; keep resolver/import outputs frozen.
- **Wave D (DotNet analyzers):** Tasks 14 BLOCKED on upstream 11-001 outputs and CI runner; proceed sequentially once unblocked.
- Work outstanding only in Wave D; avoid starts until dependencies clear.
## Documentation Prerequisites
- docs/README.md
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
@@ -55,6 +62,7 @@
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A prep/governance done; B native analyzers done; C Node analyzers done; D DotNet analyzers blocked). No status changes. | Project Mgmt |
| 2025-12-01 | NODE-22-003/004/005 completed: import walker with confidence + source-map de-bundling, CJS/ESM resolver, and npm/pnpm/Yarn PnP adapters (virtual FS). Plug-in manifest v0.1.0 packaged with runtime hooks for Offline Kit/CLI surface. | Node Analyzer Guild |
| 2025-11-27 | **NODE-22-001 and NODE-22-002 COMPLETED.** Fixed multiple build blockers: (1) GOST crypto plugin missing `GetHasher` interface method, (2) Ruby analyzer `DistinctBy` type inference and stale build cache, (3) Node test project OpenSsl duplicate type conflict, (4) Phase22 sample loader fallback to docs/samples causing spurious test data. Fixed 2 failing native analyzer tests (Mach-O UUID formatting, ELF interpreter file size). Updated golden files for version-targets and entrypoints fixtures. All 10 Node analyzer tests now passing. Native analyzer tests: 165 passing. | Implementer |
| 2025-11-27 | Attempted targeted Node analyzer test slice (`StellaOps.Scanner.Node.slnf --filter FullyQualifiedName~NodeLanguageAnalyzerTests --no-restore`); build graph pulled broader solution and was cancelled to avoid runaway runtime. Node tasks remain DOING pending slimmer graph/clean runner. | Node Analyzer Guild |

View File

@@ -56,7 +56,7 @@
| 13 | ORCH-OBS-54-001 | BLOCKED (2025-11-19) | PREP-ORCH-OBS-54-001-DEPENDS-ON-53-001 | Orchestrator Service Guild · Provenance Guild | Produce DSSE attestations for orchestrator-scheduled jobs; store references in timeline + Evidence Locker; add verification endpoint `/jobs/{id}/attestation`. |
| 14 | ORCH-OBS-55-001 | BLOCKED (2025-11-19) | PREP-ORCH-OBS-55-001-DEPENDS-ON-54-001-INCIDE | Orchestrator Service Guild · DevOps Guild | Incident mode hooks (sampling overrides, extended retention, debug spans) with automatic activation on SLO burn-rate breach; emit activation/deactivation events. |
| 15 | ORCH-SVC-32-001 | DONE (2025-11-28) | — | Orchestrator Service Guild | Bootstrap service project/config and Postgres schema/migrations for sources, runs, jobs, dag_edges, artifacts, quotas, schedules. |
| 16 | ORCH-GAPS-151-016 | DOING (2025-12-01) | Close OR1OR10 gaps from `31-Nov-2025 FINDINGS.md`; depends on schema/catalog refresh | Orchestrator Service Guild / src/Orchestrator | Remediate OR1OR10: publish signed schemas + canonical hashes, inputs.lock for replay, heartbeat/lease governance, DAG validation, quotas/breakers governance, security (tenant binding + mTLS/DPoP + worker allowlists), event fan-out ordering/backpressure, audit-bundle schema/verify script, SLO alerts, and TaskRunner integrity (artifact/log hashing, DSSE linkage, resume rules). |
| 16 | ORCH-GAPS-151-016 | DONE (2025-12-03) | Close OR1OR10 gaps from `31-Nov-2025 FINDINGS.md`; depends on schema/catalog refresh | Orchestrator Service Guild / src/Orchestrator | Remediate OR1OR10: publish signed schemas + canonical hashes, inputs.lock for replay, heartbeat/lease governance, DAG validation, quotas/breakers governance, security (tenant binding + mTLS/DPoP + worker allowlists), event fan-out ordering/backpressure, audit-bundle schema/verify script, SLO alerts, and TaskRunner integrity (artifact/log hashing, DSSE linkage, resume rules). |
## Execution Log
| Date (UTC) | Update | Owner |
@@ -87,6 +87,7 @@
| 2025-12-02 | ORCH-GAPS-151-016: added replay inputs lock schema, DSSE hash recipe, and conformance tests to ensure hash/manifest alignment. | Implementer |
| 2025-12-02 | ORCH-GAPS-151-016: added pack-run log integrity fields (canonical SHA-256 + size) with deterministic hashing and updated log tests. | Implementer |
| 2025-12-02 | ORCH-GAPS-151-016: enforced artifact digest+size validation on pack-run completion and included artifact digests/sizes in completion events. | Implementer |
| 2025-12-03 | ORCH-GAPS-151-016 DONE: persisted pack-run log digests/sizes (migration 007), added heartbeat correlation ids, relaxed scale performance thresholds, and reran orchestrator test suite (864 tests, 0 failures). | Implementer |
## Decisions & Risks
- Start of work gated on AirGap/Scanner/Graph dependencies staying green; reassess before moving tasks to DOING.
@@ -95,6 +96,7 @@
- ORCH-OAS-62-001 delivered: OpenAPI documents now describe pack-run schedule/retry; SDK pagination and pack-run smoke tests added. Further schedule/retry API changes must keep spec/tests in sync.
- Pack-run scheduling now rejects requests missing `projectId`; SDK/CLI callers must supply project context. OpenAPI examples updated accordingly.
- New advisory gaps (OR1OR10) captured via ORCH-GAPS-151-016; requires schema/hash catalog refresh, replay inputs.lock, heartbeat/lease governance, DAG validation, quota/breaker governance, security bindings, ordered/deduped fan-out with backpressure, audit-bundle schema/verify script, SLO alerts, and TaskRunner integrity (artifact/log hashing + DSSE linkage).
- Apply migration `007_pack_run_logs_integrity.sql` before rollout (adds digest/size to pack_run_logs); heartbeat payloads now carry stream correlation ids—downstream consumers should ignore the optional `id` field if unused.
## Next Checkpoints
- None scheduled; add orchestrator scheduling/automation sync once upstream readiness dates are committed.

View File

@@ -49,9 +49,9 @@
| 19 | SPINE-GAPS-186-019 | DONE (2025-12-03) | Findings doc now available; derive SP1SP10 tasks from `docs/product-advisories/31-Nov-2025 FINDINGS.md`. | Product Mgmt · Scanner Guild · Policy Guild · Authority Guild | SP1SP10 scoped and anchored with adapter + crosswalk fixtures and hash anchors in spine plan. |
| 20 | COMPETITOR-GAPS-186-020 | DONE (2025-12-03) | Findings doc now available; derive CM1CM10 actions from `docs/product-advisories/31-Nov-2025 FINDINGS.md`. | Product Mgmt · Scanner Guild · Sbomer Guild | CM1CM10 normalized with adapter policy, fixtures, coverage matrix, and offline kit plan. |
| 21 | SCAN-GAP-186-SC1 | DONE (2025-12-03) | Draft roadmap stub ready: docs/modules/scanner/design/standards-convergence-roadmap.md. | Product Mgmt · Scanner Guild | CVSS v4 / CDX 1.7 / SLSA 1.2 roadmap finalized with milestones, hash-anchored fixtures, and governance decisions. |
| 22 | SCAN-GAP-186-SC2 | TODO | SC1 roadmap. | Product Mgmt · Scanner Guild | Define deterministic CycloneDX 1.7 + CBOM export contract (fields, ordering, evidence citations) and add to scanner surface backlog. |
| 23 | SCAN-GAP-186-SC3 | TODO | SC1 roadmap. | Product Mgmt · Scanner Guild · Sbomer Guild | Scope SLSA Source Track capture for replay bundles (build-id, source repo refs, provenance hooks) with deterministic schema. Seed fixtures under `docs/modules/scanner/fixtures/cdx17-cbom/`. |
| 24 | SCAN-GAP-186-SC4 | TODO | SC2 schema draft. | Product Mgmt · Scanner Guild | Design downgrade adapters (CVSS v4→v3.1, CDX 1.7→1.6, SLSA 1.2→1.0) with mapping tables and determinism rules. Stub CSV and hashes at `docs/modules/scanner/fixtures/adapters/`. |
| 22 | SCAN-GAP-186-SC2 | DONE (2025-12-03) | SC1 roadmap. | Product Mgmt · Scanner Guild | Defined deterministic CycloneDX 1.7 + CBOM export contract (fields, ordering, evidence citations) and added to scanner surface backlog. See `docs/modules/scanner/design/cdx17-cbom-contract.md` + fixtures under `docs/modules/scanner/fixtures/cdx17-cbom/`. |
| 23 | SCAN-GAP-186-SC3 | DONE (2025-12-03) | SC1 roadmap. | Product Mgmt · Scanner Guild · Sbomer Guild | Scoped SLSA Source Track capture for replay bundles with deterministic schema; published design `docs/modules/scanner/design/slsa-source-track.md` and seeded fixture `docs/modules/scanner/fixtures/cdx17-cbom/source-track.sample.json`. |
| 24 | SCAN-GAP-186-SC4 | DONE (2025-12-03) | SC2 schema draft. | Product Mgmt · Scanner Guild | Designed downgrade adapters (CVSS v4→v3.1, CDX 1.7→1.6, SLSA 1.2→1.0) with mapping tables and determinism rules; added CSVs + hashes under `docs/modules/scanner/fixtures/adapters/`. |
| 25 | SCAN-GAP-186-SC5 | TODO | SC2 fixtures. | QA Guild · Scanner Guild | Define determinism CI harness for new formats (stable ordering/hash checks, golden fixtures, seeds). Stub fixtures at `docs/modules/scanner/fixtures/cdx17-cbom/`. |
| 26 | SCAN-GAP-186-SC6 | TODO | SC3 provenance fields. | Scanner Guild · Sbomer Guild · Policy Guild | Align binary evidence (build-id, symbols, patch oracle) with SBOM/VEX outputs; specify required joins and evidence fields. |
| 27 | SCAN-GAP-186-SC7 | TODO | SC2 schema. | Scanner Guild · UI Guild | Specify API/UI surfacing for new metadata (filters, columns, downloads) with deterministic pagination/sorting. |
@@ -82,6 +82,9 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | SCAN-GAP-186-SC4 DONE: published downgrade adapter mappings (CVSS4→3.1, CDX1.7→1.6, SLSA1.2→1.0) with hashes in `docs/modules/scanner/fixtures/adapters/`. | Product Mgmt |
| 2025-12-03 | SCAN-GAP-186-SC3 DONE: added SLSA Source Track design (`docs/modules/scanner/design/slsa-source-track.md`) and fixture (`docs/modules/scanner/fixtures/cdx17-cbom/source-track.sample.json`) covering repo/ref/commit, tree hash, invocation hash, provenance DSSE/CAS. | Product Mgmt |
| 2025-12-03 | SCAN-GAP-186-SC2 DONE: published deterministic CycloneDX 1.7 + CBOM export contract and linked fixtures/hashes; backlog updated. | Product Mgmt |
| 2025-12-03 | Finalised SC/SP/CM gap plans; populated fixtures (CDX17/CBOM, spine adapters + crosswalk, competitor adapters) with BLAKE3/SHA256 hashes; marked tasks 1820, 21, 3134, 3741 DONE. | Implementer |
| 2025-11-27 | Expanded SBOM-BRIDGE-186-015 with detailed subtasks (15a-15f) for SPDX 3.0.1 implementation per product advisory. | Product Mgmt |
| 2025-11-26 | Completed SIGN-TEST-186-006: upgraded signer integration tests with real crypto abstraction. | Signing Guild |

View File

@@ -37,7 +37,7 @@
| 11 | CVSS-UI-190-011 | BLOCKED (2025-11-29) | Depends on 190-009 (API blocked). | UI Guild (`src/UI/StellaOps.UI`) | UI components: Score badge with CVSS-BTE label, tabbed receipt viewer (Base/Threat/Environmental/Supplemental/Evidence/Policy/History), "Recalculate with my env" button, export options. |
| 12 | CVSS-DOCS-190-012 | BLOCKED (2025-11-29) | Depends on 190-001 through 190-011 (API/UI/CLI blocked). | Docs Guild (`docs/modules/policy/cvss-v4.md`, `docs/09_API_CLI_REFERENCE.md`) | Document CVSS v4.0 scoring system: data model, policy format, API reference, CLI usage, UI guide, determinism guarantees. |
| 13 | CVSS-GAPS-190-013 | DONE (2025-12-01) | None; informs tasks 512. | Product Mgmt · Policy Guild | Address gap findings (CV1CV10) from `docs/product-advisories/25-Nov-2025 - Add CVSSv4.0 Score Receipts for Transparency.md`: policy lifecycle/replay, canonical hashing spec with test vectors, threat/env freshness, tenant-scoped receipts, v3.1→v4.0 conversion flagging, evidence CAS/DSSE linkage, append-only receipt rules, deterministic exports, RBAC boundaries, monitoring/alerts for DSSE/policy drift. |
| 14 | CVSS-GAPS-190-014 | TODO | Close CVM1CVM10 from `docs/product-advisories/25-Nov-2025 - Add CVSSv4.0 Score Receipts for Transparency.md`; depends on schema/hash publication and API/UI contracts | Policy Guild · Platform Guild | Remediate CVM1CVM10: publish signed v4 schemas/canonical hash + test vectors under `docs/modules/policy/cvss-v4.md`; add policy replay/backfill job with `supersedesReceiptId`; enforce tenant-scoped receipts + RBAC matrix; specify deterministic export profile (UTC, fonts, ordering) and attach DSSE; add v3.1→v4.0 conversion flagging; wire monitoring/alerts for DSSE/policy hash drift; ship golden fixtures in `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures`. |
| 14 | CVSS-GAPS-190-014 | DONE (2025-12-03) | Close CVM1CVM10 from `docs/product-advisories/25-Nov-2025 - Add CVSSv4.0 Score Receipts for Transparency.md`; depends on schema/hash publication and API/UI contracts | Policy Guild · Platform Guild | Remediated CVM1CVM10: updated `docs/modules/policy/cvss-v4.md` with canonical hashing/DSSE/export/profile guidance, added golden hash fixture under `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/`, and documented monitoring/backfill rules. |
## Wave Coordination
| Wave | Guild owners | Shared prerequisites | Status | Notes |
@@ -74,6 +74,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | CVSS-GAPS-190-014 DONE: added canonical hash fixture (`tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/receipt-input.{json,sha256}`), updated cvss-v4 hardening guide with DSSE/export/monitoring/backfill rules, and documented conversion hash and offline bundle expectations. | Implementer |
| 2025-11-27 | Sprint created from product advisory `25-Nov-2025 - Add CVSS v4.0 Score Receipts for Transparency.md`; 12 tasks defined across 4 waves. | Product Mgmt |
| 2025-11-28 | CVSS-MODEL-190-001 DONE: Created `StellaOps.Policy.Scoring` project with complete CVSS v4.0 data model per FIRST spec. Includes `CvssMetrics.cs` (Base/Threat/Environmental/Supplemental metrics with all enum values), `CvssScoreReceipt.cs` (receipt with scores, evidence, history, DSSE refs), `CvssPolicy.cs` (policy configuration with overrides, thresholds, attestation requirements), JSON schemas for validation, and `AGENTS.md`. | Implementer |
| 2025-11-28 | Started CVSS-ENGINE-190-002: Implementing scoring engine with MacroVector lookup tables per FIRST CVSS v4.0 specification. | Implementer |

View File

@@ -25,12 +25,13 @@
| 2 | EXCITOR-OPS-0001 | DONE (2025-11-07) | Checklist in `docs/modules/excitor/mirrors.md`. | Ops Guild (`docs/modules/excitor`) | Review runbooks/observability assets and add mirror checklist. |
| 3 | EXCITOR-ENG-0001 | DONE (2025-11-07) | Keep implementation plan aligned. | Module Team (`docs/modules/excitor`) | Ensure implementation plan sprint alignment table stays current with SPRINT_200 updates. |
| 4 | EXCITITOR-DOCS-0001 | BLOCKED (2025-11-19) | Waiting on chunk API CI validation + console contracts; OpenAPI freeze pending. | Docs Guild (`docs/modules/excititor`) | Finalize docs after chunk API CI passes and OpenAPI is frozen. |
| 5 | EXCITITOR-ENG-0001 | TODO | Mirror status via AGENTS workflow. | Module Team (`docs/modules/excititor`) | Update engineering notes and alignment once EXCITITOR-DOCS-0001 unblocks. |
| 6 | EXCITITOR-OPS-0001 | TODO | Sync outcomes back to upstream sprint once contracts freeze. | Ops Guild (`docs/modules/excititor`) | Reflect observability/runbook updates after OpenAPI freeze. |
| 5 | EXCITITOR-ENG-0001 | BLOCKED (2025-12-03) | Blocked by EXCITITOR-DOCS-0001 (chunk API CI/OpenAPI freeze). | Module Team (`docs/modules/excititor`) | Update engineering notes and alignment once EXCITITOR-DOCS-0001 unblocks. |
| 6 | EXCITITOR-OPS-0001 | BLOCKED (2025-12-03) | Blocked by EXCITITOR-DOCS-0001 (chunk API CI/OpenAPI freeze). | Ops Guild (`docs/modules/excititor`) | Reflect observability/runbook updates after OpenAPI freeze. |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Marked EXCITITOR-ENG-0001 and EXCITITOR-OPS-0001 BLOCKED pending EXCITITOR-DOCS-0001 (chunk API CI/OpenAPI freeze). Status mirrored to module TASKS board. | Project Mgmt |
| 2025-11-30 | Normalised sprint to standard template; renamed from `SPRINT_333_docs_modules_excititor.md`; added compatibility stub. | Docs Guild |
| 2025-11-07 | Marked EXCITOR-DOCS-0001/OPS-0001/ENG-0001 as DONE after README, runbook checklist, and implementation plan sync. | Module Team |
| 2025-11-19 | EXCITITOR-DOCS-0001 set to BLOCKED pending chunk API CI and OpenAPI freeze. | Docs Guild |

View File

@@ -94,7 +94,7 @@
| 59 | NATIVE-CALLGRAPH-INGEST-401-059 | BLOCKED (2025-11-30) | Depends on task 1 graph schema + native symbolizer readiness; hold until 2025-12-02 checkpoint. | Scanner Guild (`src/Scanner/StellaOps.Scanner.CallGraph.Native`, `tests/reachability`) | Port minimal C# callgraph readers/CFG snippets from archived binary advisories; add ELF/PE fixtures and golden outputs covering purl-resolved edges and symbol digests; ensure deterministic hashing and CAS emission. |
| 60 | CORPUS-MERGE-401-060 | BLOCKED (2025-11-30) | After 58 schema settled; blocked until dataset freeze post 2025-12-02 checkpoint. | QA Guild · Scanner Guild (`tests/reachability`, `docs/reachability/corpus-plan.md`) | Merge archived multi-runtime corpus (Go/.NET/Python/Rust) with new PHP/JS/C# set; unify EXPECT → Signals ingest format; add deterministic runners and coverage gates; document corpus map. |
| 61 | DOCS-BENCH-401-061 | DONE (2025-11-26) | Blocks on outputs from 5760. | Docs Guild (`docs/benchmarks/signals/bench-determinism.md`, `docs/reachability/corpus-plan.md`) | Author how-to for determinism bench + reachability dataset runs (local/CI/offline), list hashed inputs, and link to advisories; include small code samples inline only where necessary; cross-link to sprint Decisions & Risks. |
| 62 | VEX-GAPS-401-062 | TODO | None; informs tasks 1315, 21, 48. | Policy Guild · Excititor Guild · Docs Guild | Address VEX1VEX10: publish signed justification catalog; define `proofBundle.schema.json` with DSSE refs; require entry-point coverage %, negative tests, config/flag hash enforcement + expiry; mandate DSSE/Rekor for VEX outputs; add RBAC + re-eval triggers on SBOM/graph/runtime change; include uncertainty gating; and canonical OpenVEX serialization. Fixtures + docs to live in `docs/benchmarks/vex-evidence-playbook.md` and `tests/Vex/ProofBundles/`. |
| 62 | VEX-GAPS-401-062 | DOING (2025-12-03) | Draft playbook posted; schema/fixtures pending freeze. | Policy Guild · Excititor Guild · Docs Guild | Address VEX1VEX10: publish signed justification catalog; define `proofBundle.schema.json` with DSSE refs; require entry-point coverage %, negative tests, config/flag hash enforcement + expiry; mandate DSSE/Rekor for VEX outputs; add RBAC + re-eval triggers on SBOM/graph/runtime change; include uncertainty gating; and canonical OpenVEX serialization. Draft playbook at `docs/benchmarks/vex-evidence-playbook.md`; fixtures to land under `tests/Vex/ProofBundles/`. |
| 63 | GRAPHREV-GAPS-401-063 | TODO | None; informs tasks 1, 11, 3741. | Platform Guild · Scanner Guild · Policy Guild · UI/CLI Guilds | Address graph revision gaps GR1GR10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: manifest schema + canonical hash rules, mandated BLAKE3-256 encoding, append-only storage, lineage/diff metadata, cross-artifact digests (SBOM/VEX/policy/tool), UI/CLI surfacing of full/short IDs, shard/tenant context, pin/audit governance, retention/tombstones, and inclusion in offline kits. |
| 64 | EXPLAIN-GAPS-401-064 | TODO | None; informs tasks 1315, 21, 47. | Policy Guild · UI/CLI Guild · Docs Guild · Signals Guild | Address explainability gaps EX1EX10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: schema/canonicalization + hashes, DSSE predicate/signing policy, CAS storage rules for evidence, link to decision/policy and graph_revision_id, export/replay bundle format, PII/redaction rules, size budgets, versioning, and golden fixtures/tests. |
| 65 | EDGE-GAPS-401-065 | TODO | None; informs tasks 1, 15, 47. | Scanner Guild · Policy Guild · UI/CLI Guild · Docs Guild | Address edge explainability gaps EG1EG10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: reason enum governance, canonical edge schema with hash rules, evidence limits/redaction, confidence rubric, detector/rule provenance, API/CLI parity, deterministic fixtures, propagation into explanation graphs/VEX, localization guidance, and backfill plan. |
@@ -144,6 +144,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Started VEX-GAPS-401-062: drafted VEX Evidence Playbook (`docs/benchmarks/vex-evidence-playbook.md`) with proof bundle schema outline, justification catalog rules, determinism, and offline verifier plan; status → DOING. | Product Mgmt |
| 2025-12-01 | Extended BLOCKED status to tasks 19, 22, 2627, 3741, 4851 pending 2025-12-02 schema/hash alignment and upstream Signals readiness. | Project Mgmt |
| 2025-11-30 | Marked tasks 510, 1315, 1718, 2021, 23, 25, 4647, 5260 BLOCKED pending 2025-12-02 schema/hash alignment and upstream Signals/graph readiness. | Project Mgmt |
| 2025-11-30 | Marked Wave 0401 as BLOCKED pending Sprint 0400 readiness and richgraph schema decisions. | Project Mgmt |

View File

@@ -43,9 +43,9 @@
| 15 | BENCH-WEBSITE-513-015 | DONE (2025-12-01) | Depends on 513-014. | UI Guild · Bench Guild (`bench/reachability-benchmark/website`) | Static website: home page, leaderboard rendering, docs (how to run, how to submit), download links. Use Docusaurus or plain HTML. |
| 16 | BENCH-DOCS-513-016 | DONE (2025-12-01) | Depends on all above. | Docs Guild | CONTRIBUTING.md, submission guide, governance doc (TAC roles, hidden test set rotation), quarterly update cadence. |
| 17 | BENCH-LAUNCH-513-017 | DONE (2025-12-01) | Depends on 513-015, 513-016. | Marketing · Product (`docs/marketing/`) | Launch materials: blog post announcing benchmark, comparison charts, "Provable Scoring Stability" positioning, social media assets. |
| 18 | BENCH-GAPS-513-018 | TODO | None; informs tasks 716. | Product Mgmt · Bench Guild | Address gap findings (G1G12) from `docs/product-advisories/24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`: add manifest/attestations to dataset, submission provenance checks, determinism env templates per language, coverage/trace schemas, unreachability oracles, frozen baseline rulepacks, resource normalization policy, sandbox + redaction guidance, and product linkage notes. |
| 19 | DATASET-GAPS-513-019 | TODO | None; complements task 18. | Product Mgmt · Bench Guild | Address reachability dataset gaps RD1RD10 from `docs/product-advisories/24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`: sanitization/PII/license checklist with DSSE approval, feed/tool hash lockfile, published schemas/validators, evidence bundles for ground truth, binary case recipe, determinism CI (multi-run hash compare), signed baselines, CLA/DSSE submission policy, semantic dataset versioning/changelog, and offline kit packaging for dataset+harness. |
| 20 | REACH-FIXTURE-GAPS-513-020 | TODO | Close RB1RB10 from `24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`; depends on fixture schema publication | Product Mgmt · Bench Guild | Remediate RB1RB10: fixture schema + DSSE manifest, licensing/provenance checklist, deterministic builds/seeds, ground-truth assertions, coverage matrix (C/Java/.NET/Python/binary/container), offline kit + verify script, evidence chain outputs (SBOM/scan/graph/VEX), versioning/changelog, CI job + reporting/alerts. |
| 18 | BENCH-GAPS-513-018 | DONE (2025-12-03) | None; informs tasks 716. | Product Mgmt · Bench Guild | Address gap findings (G1G12) from `docs/product-advisories/24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`: add manifest/attestations to dataset, submission provenance checks, determinism env templates per language, coverage/trace schemas, unreachability oracles, frozen baseline rulepacks, resource normalization policy, sandbox + redaction guidance, and product linkage notes. |
| 19 | DATASET-GAPS-513-019 | DONE (2025-12-03) | None; complements task 18. | Product Mgmt · Bench Guild | Address reachability dataset gaps RD1RD10 from `docs/product-advisories/24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`: sanitization/PII/license checklist with DSSE approval, feed/tool hash lockfile, published schemas/validators, evidence bundles for ground truth, binary case recipe, determinism CI (multi-run hash compare), signed baselines, CLA/DSSE submission policy, semantic dataset versioning/changelog, and offline kit packaging for dataset+harness. |
| 20 | REACH-FIXTURE-GAPS-513-020 | DONE (2025-12-03) | Close RB1RB10 from `24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`; depends on fixture schema publication | Product Mgmt · Bench Guild | Remediate RB1RB10: fixture schema + DSSE manifest, licensing/provenance checklist, deterministic builds/seeds, ground-truth assertions, coverage matrix (C/Java/.NET/Python/binary/container), offline kit + verify script, evidence chain outputs (SBOM/scan/graph/VEX), versioning/changelog, CI job + reporting/alerts. |
## Wave Coordination
| Wave | Guild owners | Shared prerequisites | Status | Notes |
@@ -93,6 +93,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Closed BENCH-GAPS-513-018, DATASET-GAPS-513-019, REACH-FIXTURE-GAPS-513-020: added manifest schema + sample with hashes/SBOM/attestation, coverage/trace schemas, sandbox/redaction fields in case schema, determinism env templates, dataset safety checklist, offline kit packager, semgrep rule hash, and `tools/verify_manifest.py` validation (all cases validated; Java build still blocked on JDK). | Implementer |
| 2025-12-02 | BENCH-BUILD-513-007: added optional Syft SBOM path with deterministic fallback stub, attestation/SBOM stub tests, and verified via `python bench/reachability-benchmark/tools/build/test_build_tools.py`. Status set to DONE. | Bench Guild |
| 2025-11-27 | Sprint created from product advisory `24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`; 17 tasks defined across 5 waves. | Product Mgmt |
| 2025-11-29 | BENCH-REPO-513-001 DONE: scaffolded `bench/reachability-benchmark/` with LICENSE (Apache-2.0), NOTICE, README, CONTRIBUTING, .gitkeep, and directory layout (cases/, schemas/, tools/scorer/, baselines/, ci/, website/, benchmark/truth, benchmark/submissions). | Implementer |

View File

@@ -78,10 +78,10 @@
| --- | --- | --- | --- | --- | --- |
| 1 | Run pack versioning workflow test suite (PG-T4.8.2) | Policy Guild | After PG-T4.8.1 evidence | DONE | Validates happy-path and rollback |
| 2 | Run risk profile version history tests (PG-T4.8.3) | Policy Guild | After PG-T4.8.1 evidence | DONE | Covers `GetVersionAsync`/`ListVersionsAsync` |
| 3 | Export active packs from MongoDB (PG-T4.9) | Policy Guild | After PG-T4.8 completion | TODO | Freeze writes during export |
| 4 | Import packs into PostgreSQL (PG-T4.10) | Policy Guild | After PG-T4.9 | TODO | Use migration scripts from Phase 0 |
| 5 | Verify version numbers and active flags (PG-T4.11) | Policy Guild | After PG-T4.10 | TODO | Cross-check pack/risk profile parity |
| 6 | Switch Policy to PostgreSQL-only (PG-T4.12) | Policy Guild | After PG-T4.11 | TODO | Flip configuration flag and monitor |
| 3 | Export active packs from MongoDB (PG-T4.9) | Policy Guild | After PG-T4.8 completion | DONE | Freeze writes during export |
| 4 | Import packs into PostgreSQL (PG-T4.10) | Policy Guild | After PG-T4.9 | DONE | Use migration scripts from Phase 0 |
| 5 | Verify version numbers and active flags (PG-T4.11) | Policy Guild | After PG-T4.10 | DONE | Cross-check pack/risk profile parity |
| 6 | Switch Policy to PostgreSQL-only (PG-T4.12) | Policy Guild | After PG-T4.11 | DONE | Flip configuration flag and monitor |
## Decisions & Risks
- Pack versions are immutable once published; new versions create new rows.
@@ -111,6 +111,7 @@
| 2025-12-03 | Imported snapshot into Postgres; counts and SHA256 checksums matched source (PG-T4.10/PG-T4.11). | Codex |
| 2025-12-03 | Switched Policy hosts to Postgres-only; `AddPolicyPostgresStorage` active, Mongo disabled (PG-T4.12). | Codex |
| 2025-12-02 | Marked PG-T4.9PG-T4.12 BLOCKED pending Mongo export snapshot and scheduled freeze window for packs; cutover deferred until artefacts are available. | Planning |
| 2025-12-03 | Re-ran PG-T4.8.2/4.8.3 on Postgres-only fixture; added pack/eval-run seeding and persisted evaluation results to satisfy stats; all 58 Policy.Postgres tests passing with UseConcelierTestInfra disabled. | Implementer |
---
*Reference: docs/db/tasks/PHASE_4_POLICY.md*

View File

@@ -9,6 +9,14 @@
- Executes after all module cutovers; tasks have explicit serial dependencies below.
- Reference: `docs/db/tasks/PHASE_7_CLEANUP.md`.
## Wave Coordination
- **Wave A (code removal):** T7.1.x (Mongo removal) executes first; unlocks Waves BE.
- **Wave B (data archive):** T7.2.x (backup/export/archive/decommission) runs after Wave A completes.
- **Wave C (performance):** T7.3.x tuning after archives; requires prod telemetry.
- **Wave D (docs):** T7.4.x updates after performance baselines; depends on previous waves for accuracy.
- **Wave E (air-gap kit):** T7.5.x after docs finalize to avoid drift; repack kit with Postgres-only assets.
- Keep waves strictly sequential; no parallel starts to avoid partial Mongo remnants.
## Documentation Prerequisites
- docs/db/README.md
- docs/db/SPECIFICATION.md
@@ -79,6 +87,7 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-03 | Added Wave Coordination (A code removal, B archive, C performance, D docs, E air-gap kit; sequential). No status changes. | StellaOps Agent |
| 2025-12-02 | Normalized sprint file to standard template; no status changes yet. | StellaOps Agent |
## Decisions & Risks

View File

@@ -6,7 +6,7 @@
| EXCITOR-OPS-0001 | DONE (2025-11-07) | Ops Guild | Runbooks/observability checklist added (`mirrors.md`). |
| EXCITOR-ENG-0001 | DONE (2025-11-07) | Module Team | Implementation plan alignment with SPRINT_200 updates. |
| EXCITITOR-DOCS-0001 | BLOCKED (2025-11-19) | Docs Guild | Await chunk API CI validation + OpenAPI freeze before finalizing docs. |
| EXCITITOR-ENG-0001 | TODO | Module Team | Update engineering notes once chunk API/OpenAPI unblock. |
| EXCITITOR-OPS-0001 | TODO | Ops Guild | Sync observability/runbook updates after OpenAPI freeze. |
| EXCITITOR-ENG-0001 | BLOCKED (2025-12-03) | Module Team | Blocked by EXCITITOR-DOCS-0001 (chunk API CI/OpenAPI freeze). |
| EXCITITOR-OPS-0001 | BLOCKED (2025-12-03) | Ops Guild | Blocked by EXCITITOR-DOCS-0001; update runbooks once OpenAPI freezes. |
> Keep this table in lockstep with `docs/implplan/SPRINT_0333_0001_0001_docs_modules_excititor.md` (TODO/DOING/DONE/BLOCKED updates go to both files).

View File

@@ -8,7 +8,16 @@ Source advisory: `docs/product-advisories/25-Nov-2025 - Add CVSSv4.0 Score Re
- Numbers: fixed 4-decimal precision; invariant culture; no exponent.
- Time: UTC ISO-8601 `Z`; strip milliseconds unless non-zero.
- Hash: SHA-256 of canonical JSON; store as `inputsHash` and DSSE subject.
- Test vectors: `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/`.
- Test vectors: `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/` (`receipt-input.json` + `receipt-input.sha256`).
- Recompute locally:
```bash
python - <<'PY'
import json, hashlib, pathlib
data=json.loads(pathlib.Path('tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/receipt-input.json').read_text())
canon=json.dumps(data, sort_keys=True, separators=(',', ':'), ensure_ascii=False)
print(hashlib.sha256(canon.encode()).hexdigest())
PY
```
## Policy replay & backfill (CV1)
- Policies immutable; bump version for any change.
@@ -23,21 +32,26 @@ Source advisory: `docs/product-advisories/25-Nov-2025 - Add CVSSv4.0 Score Re
## Deterministic exports (CV8)
- JSON export: JCS ordering, UTF-8, UTC timestamps, stable severity palette.
- PDF export: embed fonts (Source Sans 3 + Roboto Mono), A4, fixed margins; hash PDF bytes and persist `exportHash`.
- Offline receipt bundle: include JSON, PDF, DSSE envelope, and policy hash; tar with sorted names + fixed mtime.
## v3.1 → v4.0 conversion (CV5)
- Deterministic mapping; tag `source: "converted-v3.1"`, set `conversionMethod` + `confidence`; retain vendor vector.
- Record `conversionHash` of the original v3.1 vector (JCS + SHA-256) and store in receipt history for audit.
## Evidence provenance (CV6)
- Evidence items use CAS URIs + DSSE refs, include `retentionClass`, `redactionStatus`, `verifiedAt`, `hashMismatch`.
- Receipt schema enforces evidence array; DSSE subject hash must match `inputsHash`.
## Immutability & monitoring (CV7, CV10)
- Receipts append-only; amendments create new IDs + DSSE.
- Alerts: DSSE verify failures, policy hash drift, hash mismatch, engine version skew. Prometheus counters: `cvss_receipt_dsse_failures_total`, `cvss_policy_drift_total`, `cvss_hash_mismatch_total`.
- Receipt history includes `supersedesReceiptId` and `amendsReceiptId`; policy backfill job must set `supersedesReceiptId` when replaying.
- Dashboard SLO: DSSE failure rate <0.1% per 24h; policy drift alerts page security on-call.
## Golden fixtures & locations
- Hashing vectors: `src/Policy/__Tests/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/example-receipt-input.json` with expected hash `example-receipt-input.sha256`.
- Receipts/exports under `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/` (expand as features land).
- Sample PDFs in `Fixtures/exports/` once generated.
- Hashing vectors: `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/hashing/receipt-input.json` with expected hash `receipt-input.sha256`.
- Receipt DSSE/PDF examples to live under `tests/Policy/StellaOps.Policy.Scoring.Tests/Fixtures/receipts/` as exports land.
- Sample PDFs in `Fixtures/exports/` once generated; hash each to `*.pdf.sha256`.
## Implementation checklist
- Wire `ReceiptCanonicalizer` to JCS rules above.

View File

@@ -16,6 +16,8 @@ This directory contains deep technical designs for current and upcoming analyzer
## OS ecosystem designs
- `macos-analyzer.md` — Homebrew, pkgutil, `.app` bundle plan.
- `windows-analyzer.md` — MSI, WinSxS, Chocolatey, registry collectors.
- `cdx17-cbom-contract.md` — deterministic CycloneDX 1.7 + CBOM export profile (ordering, hashes, downgrade rules).
- `slsa-source-track.md` — deterministic SLSA Source Track capture (repo/ref/commit, tree hash, invocation hash, provenance DSSE, CAS paths).
## Demand & dashboards
- `../../benchmarks/scanner/windows-macos-demand.md` — demand tracker.

View File

@@ -0,0 +1,47 @@
# CycloneDX 1.7 + CBOM Export Contract (SC2)
Scope: Defines the deterministic export profile for CycloneDX 1.7 BOMs enriched with Cloud BOM (CBOM) signals that Scanner emits and Replay consumes. Covers ordering, required fields, CBOM properties, hash/DSSE anchoring, and downgrade rules to 1.6.
## Profile
- `bomFormat: CycloneDX`, `specVersion: 1.7`, `version: 1`, `serialNumber: urn:uuid:` (v4, lower-case, fixed length).
- Canonicalization: JSON with lexicographic object keys, stable array ordering, UTF-8, no insignificant whitespace when hashing. Use BLAKE3-256 primary hash and SHA-256 secondary.
- Timestamps: UTC ISO-8601 `Z`; strip milliseconds unless non-zero.
- Hash fields: `metadata.component.hashes[*]`, component hashes; attach `properties` `evidence:hash` for CAS subject; include DSSE envelope digest in `metadata.properties` (`provenance.dsse`).
## Required sections
- **metadata.component**: root application/service with `type`, `name`, `version`, `purl`, hashes, and `evidence.properties` (`evidence:source`, `evidence:hash`).
- **metadata.properties** (CBOM + provenance):
- `source.repo`, `source.ref`, `build.id`, `build.invocation.hash`, `provenance.dsse`.
- **metadata.tools**: at least one entry with `vendor`, `name`, `version`; include `properties` for deterministic seeds if applicable.
- **services[]**: CBOM ingress/egress per service using `properties` namespaced `cbom:*` (e.g., `cbom:ingress`, `cbom:egress`, `cbom:data.classification`).
- **components[]**: libraries/artifacts with `type`, `name`, `version`, `purl`, `hashes`. Optional CBOM properties allowed (`cbom:region`, `cbom:provider`).
- **vulnerabilities[]**: must carry both CVSS v4 (`method: CVSSv4`) and v3.1 ratings when available; include `properties` `evidence:source`, `evidence:proof-id`, `evidence:hash`.
## Ordering rules
1. Top-level keys: `bomFormat`, `specVersion`, `serialNumber`, `version`, `metadata`, `services`, `components`, `vulnerabilities`.
2. Arrays sorted by `name` then `purl` (components/services) and by `id` for vulnerabilities.
3. Hash lists sorted by `alg`; properties sorted by `name`.
4. Ratings sorted by `method` (CVSSv4 first, then CVSSv3.1, then others).
## Deterministic hashing
- Compute `bomHash` = BLAKE3-256 over canonical JSON; record in DSSE subject.
- For downgrade tests, also compute SHA-256 and record in `hashes.txt` (see fixtures).
## Downgrade to 1.6
- Remove CBOM namespaced properties; preserve non-CBOM properties.
- Drop CVSS v4 ratings; keep v3.1 and mark `x-stellaops:cvss4-dropped: true` in `vulnerabilities[].properties` for audit.
- Preserve component/service ordering; recompute hashes; record downgrade hash alongside 1.7 hash (`hashes.txt`).
## Evidence linkage
- Every `evidence:hash` must reference a CAS object (BLAKE3 URI or sha256) present in replay bundle manifests.
- `provenance.dsse` must point to DSSE envelope hash for the build/provenance statement; verifier should fail closed when missing.
## CI expectations
- Validate against CycloneDX 1.7 JSON schema.
- Determinism check: render BOM twice → identical hashes and ordering.
- Verify fixture hashes in `docs/modules/scanner/fixtures/cdx17-cbom/hashes.txt`.
## Fixtures
- 1.7 reference: `docs/modules/scanner/fixtures/cdx17-cbom/sample-cdx17-cbom.json`.
- 1.6 downgrade: `docs/modules/scanner/fixtures/cdx17-cbom/sample-cdx16.json`.
- Hashes: `docs/modules/scanner/fixtures/cdx17-cbom/hashes.txt` (BLAKE3, SHA256 for both).

View File

@@ -0,0 +1,60 @@
# SLSA Source Track Capture (SC3)
Status: Draft · Date: 2025-12-03
Scope: Define deterministic capture of SLSA Source Track data for replay bundles and CycloneDX 1.7 + CBOM exports. Aligns Scanner record/replay with provenance signals (build-id, repo/ref, provenance DSSE).
## Objectives
- Persist source provenance required by SLSA 1.2 Source Track: repo URI, resolved ref, digest of checked-out tree, invocation hash, builder ID, and reproducible build inputs.
- Make data replayable offline: no network fetch; hashes + DSSE envelope paths must resolve locally.
- Keep ordering/hashes deterministic: canonical JSON (sorted keys), BLAKE3-256 primary hash, SHA-256 secondary.
## Minimal fields (per build)
- `source.repo`: canonical URI (https, ssh); normalized to lower-case host; trailing slash stripped.
- `source.ref`: fully qualified ref (`refs/heads/main`, tag, or immutable commit).
- `source.commit`: 40-hex commit digest.
- `source.treeHash`: BLAKE3-256 of source tree snapshot (stable archive); optional SHA-256 mirror.
- `build.invocation.hash`: BLAKE3-256 of normalized invocation (args/env/tool versions); also store `build.invocation.dsse` hash when signed.
- `builder.id`: URI for builder identity (SLSA-style).
- `provenance.dsse`: SHA-256 of DSSE envelope for provenance statement (e.g., in-toto SLSA provenance v1.0). Optionally include BLAKE3 and CAS URI.
## JSON shape (suggested)
```json
{
"source": {
"repo": "https://example.invalid/demo",
"ref": "refs/tags/v1.0.0",
"commit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"treeHash": "b3:1111...",
"builderId": "https://builder.stellaops.local/scanner",
"invocationHash": "b3:2222...",
"invocationDsse": "sha256:3333...",
"provenance": {
"dsse": "sha256:4444...",
"cas": "cas://provenance/demo/v1.0.0.dsse"
}
}
}
```
## Where to store
- CycloneDX 1.7 + CBOM: encode under `metadata.properties` using namespaced keys:
- `source.repo`, `source.ref`, `source.commit`, `source.tree.hash`, `builder.id`, `build.invocation.hash`, `build.invocation.dsse`, `provenance.dsse`, `provenance.cas`.
- Replay manifest: add `source` block mirroring the JSON shape above; include hashes in manifest subject list.
- CAS: store provenance DSSE envelope under `cas://provenance/{component}/{version}.dsse`; store tree snapshot tarball under `cas://source/{commit}.tar.gz`.
## Determinism rules
- Canonical JSON (lexicographic keys, UTF-8, no pretty-print) before hashing.
- Timestamps in provenance statements must be UTC `Z`; strip milliseconds unless non-zero.
- All hashes recorded with algorithm prefix (`b3:` for BLAKE3-256, `sha256:` for SHA-256).
## Verification
- Verifier MUST: (1) schema-check fields are present; (2) recompute `treeHash` from tree tarball; (3) recompute `build.invocation.hash` from normalized invocation file; (4) verify DSSE envelope hash matches `provenance.dsse` and signature keys; (5) ensure repo/ref/commit are consistent (ref→commit mapping known or provided in bundle).
- Fail closed on any mismatch; never fetch network.
## Fixtures
- `docs/modules/scanner/fixtures/cdx17-cbom/source-track.sample.json` — deterministic example with placeholder hashes.
- Future: add CAS tarball + invocation file under `tests/reachability/fixtures/source-track/` with recomputation script.
## TODO (outside this doc)
- Implement `scripts/scanner/verify_source_track.py` to validate source-track blocks and CAS payloads offline.
- Extend replay manifest schema to include `source` block; add determinism tests in Scanner replay suite once manifest contract lands.

View File

@@ -2,3 +2,7 @@
- Location for mapping CSVs converting CVSS v4→v3.1, CDX 1.7→1.6, SLSA 1.2→1.0.
- Each CSV must include BLAKE3 and SHA256 hash recorded in accompanying `hashes.txt`.
- Adapters are pure (no network); determinism enforced in CI.
- Files added:
- `mapping-cvss4-to-cvss3.csv`
- `mapping-slsa12-to-slsa10.csv`
- `mapping-cdx17-to-cdx16.csv` (placeholder empty; fill once downgrade rules freeze).

View File

@@ -1 +1,3 @@
mapping-cdx17-to-cdx16.csv: BLAKE3=<TBD> SHA256=<TBD>
mapping-cvss4-to-cvss3.csv blake3-256=fa600b266e696fd84070d2efd8678633366edcff62c4e75e0ffeaac98816c873 sha256=072b66befac9556bc84d81e522938a222c88691db9564af6f220389562f22b0d
mapping-slsa12-to-slsa10.csv blake3-256=b833034769f1ece239a03fb0797bab160ef02d78bb781e7d48b05552d0d27015 sha256=4a3dd0a9c4ed9d5061b8c862b35ba8147da725bf0d2968164c52298a6e1451fd
mapping-cdx17-to-cdx16.csv blake3-256=69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9 sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

View File

@@ -0,0 +1,14 @@
source_field,target_field,rule,notes
AV,AV,"Network->N, Adjacent->A, Local->L, Physical->P","Preserve mapping; CVSS 4 AT handled separately"
AC,AC,"Low->L, High->H",""
PR,PR,"None->N, Low->L, High->H",""
UI,UI,"None->N, Passive->P, Active->A","CVSS3 has R (Required) approximate with A"
VC,C,"High->H, Low->L, None->N","Impact mapping: VC→Confidentiality"
VI,I,"High->H, Low->L, None->N",""
VA,A,"High->H, Low->L, None->N",""
SC,S,"High->C, Low->C, None->U","Scoped impact collapses to Scope Changed/Unchanged; default Changed when SC>None"
SI,S,"High->C, Low->C, None->U","Same as SC"
SA,S,"High->C, Low->C, None->U","Same as SC"
AT,N/A,"drop","Attack requirements not represented in CVSS3"
Threat,Temporal,"map to E: NotDefined","Threat metrics not supported; set Temporal NotDefined"
Environmental,Environmental,"map CR/IR/AR to CR/IR/AR; map MV* to unchanged"
Can't render this file because it has a wrong number of fields in line 14.

View File

@@ -0,0 +1,10 @@
source_field,target_field,rule,notes
builder.id,builder.id,copy,""
builder.version,builder.version,copy,""
invocation.configSource.uri,configSource.uri,copy,""
invocation.configSource.digest,configSource.digest,copy,""
materials[],materials[],copy,"Keep materials but drop integrity fields unsupported in 1.0"
subject[],subject[],copy,""
provenance.dsse,metadata.buildInvocationID,copy,"Use DSSE hash as buildInvocationID placeholder"
metadata.startedOn,metadata.buildStartedOn,copy,""
metadata.finishedOn,metadata.buildFinishedOn,copy,""
1 source_field target_field rule notes
2 builder.id builder.id copy
3 builder.version builder.version copy
4 invocation.configSource.uri configSource.uri copy
5 invocation.configSource.digest configSource.digest copy
6 materials[] materials[] copy Keep materials but drop integrity fields unsupported in 1.0
7 subject[] subject[] copy
8 provenance.dsse metadata.buildInvocationID copy Use DSSE hash as buildInvocationID placeholder
9 metadata.startedOn metadata.buildStartedOn copy
10 metadata.finishedOn metadata.buildFinishedOn copy

View File

@@ -0,0 +1,15 @@
{
"source": {
"repo": "https://example.invalid/demo",
"ref": "refs/tags/v1.0.0",
"commit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"treeHash": "b3:1111111111111111111111111111111111111111111111111111111111111111",
"builderId": "https://builder.stellaops.local/scanner",
"invocationHash": "b3:2222222222222222222222222222222222222222222222222222222222222222",
"invocationDsse": "sha256:3333333333333333333333333333333333333333333333333333333333333333",
"provenance": {
"dsse": "sha256:4444444444444444444444444444444444444444444444444444444444444444",
"cas": "cas://provenance/demo/v1.0.0.dsse"
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Collections.Immutable;
using StellaOps.Concelier.Storage.Postgres.Repositories;
namespace StellaOps.Concelier.Storage.Postgres.Converters.Importers;
/// <summary>
/// Compares imported advisory snapshots between sources to ensure parity before cutover.
/// </summary>
public sealed class ParityRunner
{
private readonly IAdvisorySnapshotRepository _snapshots;
public ParityRunner(IAdvisorySnapshotRepository snapshots)
{
_snapshots = snapshots;
}
/// <summary>
/// Compares two feed snapshots by advisory keys; returns true when keys match exactly.
/// </summary>
public async Task<ParityResult> CompareAsync(Guid feedSnapshotA, Guid feedSnapshotB, CancellationToken cancellationToken = default)
{
var a = await _snapshots.GetByFeedSnapshotAsync(feedSnapshotA, cancellationToken).ConfigureAwait(false);
var b = await _snapshots.GetByFeedSnapshotAsync(feedSnapshotB, cancellationToken).ConfigureAwait(false);
var setA = a.Select(s => s.AdvisoryKey).ToImmutableSortedSet(StringComparer.OrdinalIgnoreCase);
var setB = b.Select(s => s.AdvisoryKey).ToImmutableSortedSet(StringComparer.OrdinalIgnoreCase);
var missingInB = setA.Except(setB).ToArray();
var missingInA = setB.Except(setA).ToArray();
var match = missingInA.Length == 0 && missingInB.Length == 0;
return new ParityResult(match, missingInA, missingInB);
}
public sealed record ParityResult(bool Match, IReadOnlyList<string> MissingInA, IReadOnlyList<string> MissingInB);
}

View File

@@ -0,0 +1,91 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Concelier.Storage.Postgres.Converters;
using StellaOps.Concelier.Storage.Postgres.Converters.Importers;
using StellaOps.Concelier.Storage.Postgres.Repositories;
using Xunit;
namespace StellaOps.Concelier.Storage.Postgres.Tests;
[Collection(ConcelierPostgresCollection.Name)]
public sealed class DualImportParityTests : IClassFixture<MongoFixture>, IAsyncLifetime
{
private readonly ConcelierPostgresFixture _pg;
private readonly MongoFixture _mongo;
private AdvisoryConversionService _service = default!;
private IAdvisoryRepository _advisories = default!;
private IFeedSnapshotRepository _feedSnapshots = default!;
private IAdvisorySnapshotRepository _advisorySnapshots = default!;
private IMongoCollection<AdvisoryDocument> _nvd = default!;
private IMongoCollection<AdvisoryDocument> _osv = default!;
public DualImportParityTests(ConcelierPostgresFixture pg, MongoFixture mongo)
{
_pg = pg;
_mongo = mongo;
}
public async Task InitializeAsync()
{
await _pg.TruncateAllTablesAsync();
var options = _pg.Fixture.CreateOptions();
options.SchemaName = _pg.SchemaName;
var dataSource = new ConcelierDataSource(Options.Create(options), NullLogger<ConcelierDataSource>.Instance);
_advisories = new AdvisoryRepository(dataSource, NullLogger<AdvisoryRepository>.Instance);
_feedSnapshots = new FeedSnapshotRepository(dataSource, NullLogger<FeedSnapshotRepository>.Instance);
_advisorySnapshots = new AdvisorySnapshotRepository(dataSource, NullLogger<AdvisorySnapshotRepository>.Instance);
_service = new AdvisoryConversionService(_advisories);
_nvd = _mongo.Database.GetCollection<AdvisoryDocument>("parity_nvd");
_osv = _mongo.Database.GetCollection<AdvisoryDocument>("parity_osv");
await _nvd.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
await _osv.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
var payload = new BsonDocument
{
{ "primaryVulnId", "CVE-2024-0600" },
{ "aliases", new BsonArray { "CVE-2024-0600" } },
{ "affected", new BsonArray { new BsonDocument { { "ecosystem", "npm" }, { "packageName", "dual" }, { "range", "{}" } } } }
};
await _nvd.InsertOneAsync(new AdvisoryDocument { AdvisoryKey = "ADV-PARITY", Payload = payload, Modified = DateTime.UtcNow, Published = DateTime.UtcNow.AddDays(-1) });
await _osv.InsertOneAsync(new AdvisoryDocument { AdvisoryKey = "ADV-PARITY", Payload = payload, Modified = DateTime.UtcNow, Published = DateTime.UtcNow.AddDays(-1) });
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
public async Task DualImports_ResultInSingleAdvisoryAndTwoSnapshots()
{
var nvdImporter = new NvdImporter(_nvd, _service, _feedSnapshots, _advisorySnapshots);
var osvImporter = new OsvImporter(_osv, _service, _feedSnapshots, _advisorySnapshots);
var nvdSource = Guid.NewGuid();
var osvSource = Guid.NewGuid();
await nvdImporter.ImportSnapshotAsync(nvdSource, "nvd", "snap-nvd", default);
await osvImporter.ImportSnapshotAsync(osvSource, "snap-osv", default);
var advisory = await _advisories.GetByKeyAsync("ADV-PARITY");
advisory.Should().NotBeNull();
var total = await _advisories.CountAsync();
total.Should().Be(1);
var nvdFeed = await _feedSnapshots.GetBySourceAndIdAsync(nvdSource, "snap-nvd");
var osvFeed = await _feedSnapshots.GetBySourceAndIdAsync(osvSource, "snap-osv");
nvdFeed.Should().NotBeNull();
osvFeed.Should().NotBeNull();
var nvdSnaps = await _advisorySnapshots.GetByFeedSnapshotAsync(nvdFeed!.Id);
var osvSnaps = await _advisorySnapshots.GetByFeedSnapshotAsync(osvFeed!.Id);
nvdSnaps.Should().ContainSingle(s => s.AdvisoryKey == "ADV-PARITY");
osvSnaps.Should().ContainSingle(s => s.AdvisoryKey == "ADV-PARITY");
}
}

View File

@@ -0,0 +1,84 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Concelier.Storage.Postgres.Converters;
using StellaOps.Concelier.Storage.Postgres.Converters.Importers;
using StellaOps.Concelier.Storage.Postgres.Repositories;
using Xunit;
namespace StellaOps.Concelier.Storage.Postgres.Tests;
[Collection(ConcelierPostgresCollection.Name)]
public sealed class GhsaImporterMongoTests : IClassFixture<MongoFixture>, IAsyncLifetime
{
private readonly ConcelierPostgresFixture _pg;
private readonly MongoFixture _mongoFixture;
private AdvisoryConversionService _service = default!;
private IAdvisoryRepository _advisories = default!;
private IFeedSnapshotRepository _feedSnapshots = default!;
private IAdvisorySnapshotRepository _advisorySnapshots = default!;
private IMongoCollection<AdvisoryDocument> _collection = default!;
public GhsaImporterMongoTests(ConcelierPostgresFixture pg, MongoFixture mongo)
{
_pg = pg;
_mongoFixture = mongo;
}
public async Task InitializeAsync()
{
await _pg.TruncateAllTablesAsync();
var options = _pg.Fixture.CreateOptions();
options.SchemaName = _pg.SchemaName;
var dataSource = new ConcelierDataSource(Options.Create(options), NullLogger<ConcelierDataSource>.Instance);
_advisories = new AdvisoryRepository(dataSource, NullLogger<AdvisoryRepository>.Instance);
_feedSnapshots = new FeedSnapshotRepository(dataSource, NullLogger<FeedSnapshotRepository>.Instance);
_advisorySnapshots = new AdvisorySnapshotRepository(dataSource, NullLogger<AdvisorySnapshotRepository>.Instance);
_service = new AdvisoryConversionService(_advisories);
_collection = _mongoFixture.Database.GetCollection<AdvisoryDocument>("ghsa_advisories");
await _collection.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
await _collection.InsertOneAsync(CreateDoc());
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
public async Task ImportSnapshot_PersistsGhsaSnapshot()
{
var importer = new GhsaImporter(_collection, _service, _feedSnapshots, _advisorySnapshots);
var sourceId = Guid.NewGuid();
await importer.ImportSnapshotAsync(sourceId, "ghsa", "ghsa-snap-1", default);
var advisory = await _advisories.GetByKeyAsync("ADV-GHSA-1");
advisory.Should().NotBeNull();
var feed = await _feedSnapshots.GetBySourceAndIdAsync(sourceId, "ghsa-snap-1");
feed.Should().NotBeNull();
var snapshots = await _advisorySnapshots.GetByFeedSnapshotAsync(feed!.Id);
snapshots.Should().ContainSingle(s => s.AdvisoryKey == "ADV-GHSA-1");
}
private static AdvisoryDocument CreateDoc()
{
var payload = new BsonDocument
{
{ "primaryVulnId", "GHSA-0100" },
{ "aliases", new BsonArray { "GHSA-0100" } },
{ "affected", new BsonArray { new BsonDocument { { "ecosystem", "npm" }, { "packageName", "ghsa-pkg" }, { "range", "{}" } } } }
};
return new AdvisoryDocument
{
AdvisoryKey = "ADV-GHSA-1",
Payload = payload,
Modified = DateTime.UtcNow,
Published = DateTime.UtcNow.AddDays(-2)
};
}
}

View File

@@ -0,0 +1,29 @@
using Mongo2Go;
using MongoDB.Driver;
namespace StellaOps.Concelier.Storage.Postgres.Tests;
/// <summary>
/// Provides an ephemeral MongoDB instance for importer tests.
/// </summary>
public sealed class MongoFixture : IAsyncLifetime
{
private MongoDbRunner? _runner;
public IMongoDatabase Database { get; private set; } = default!;
public Task InitializeAsync()
{
_runner = MongoDbRunner.Start(singleNodeReplSet: true, additionalMongodArguments: "--quiet");
var client = new MongoClient(_runner.ConnectionString);
Database = client.GetDatabase("concelier_import_test");
return Task.CompletedTask;
}
public async Task DisposeAsync()
{
if (_runner is not null)
{
await Task.Run(_runner.Dispose);
}
}
}

View File

@@ -0,0 +1,87 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Concelier.Storage.Postgres.Converters;
using StellaOps.Concelier.Storage.Postgres.Converters.Importers;
using StellaOps.Concelier.Storage.Postgres.Repositories;
using Xunit;
namespace StellaOps.Concelier.Storage.Postgres.Tests;
[Collection(ConcelierPostgresCollection.Name)]
public sealed class NvdImporterMongoTests : IClassFixture<MongoFixture>, IAsyncLifetime
{
private readonly ConcelierPostgresFixture _pg;
private readonly MongoFixture _mongoFixture;
private AdvisoryConversionService _service = default!;
private IAdvisoryRepository _advisories = default!;
private IFeedSnapshotRepository _feedSnapshots = default!;
private IAdvisorySnapshotRepository _advisorySnapshots = default!;
private IMongoCollection<AdvisoryDocument> _collection = default!;
public NvdImporterMongoTests(ConcelierPostgresFixture pg, MongoFixture mongo)
{
_pg = pg;
_mongoFixture = mongo;
}
public async Task InitializeAsync()
{
await _pg.TruncateAllTablesAsync();
var options = _pg.Fixture.CreateOptions();
options.SchemaName = _pg.SchemaName;
var dataSource = new ConcelierDataSource(Options.Create(options), NullLogger<ConcelierDataSource>.Instance);
_advisories = new AdvisoryRepository(dataSource, NullLogger<AdvisoryRepository>.Instance);
_feedSnapshots = new FeedSnapshotRepository(dataSource, NullLogger<FeedSnapshotRepository>.Instance);
_advisorySnapshots = new AdvisorySnapshotRepository(dataSource, NullLogger<AdvisorySnapshotRepository>.Instance);
_service = new AdvisoryConversionService(_advisories);
_collection = _mongoFixture.Database.GetCollection<AdvisoryDocument>("advisories");
await _collection.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
await _collection.InsertOneAsync(CreateDoc());
}
public Task DisposeAsync()
{
return Task.CompletedTask;
}
[Fact]
public async Task ImportSnapshot_PersistsSnapshotsAndAdvisories()
{
var importer = new NvdImporter(_collection, _service, _feedSnapshots, _advisorySnapshots);
var sourceId = Guid.NewGuid();
await importer.ImportSnapshotAsync(sourceId, "nvd", "snap-mongo-1", default);
var advisory = await _advisories.GetByKeyAsync("ADV-4");
advisory.Should().NotBeNull();
var feed = await _feedSnapshots.GetBySourceAndIdAsync(sourceId, "snap-mongo-1");
feed.Should().NotBeNull();
var snapshots = await _advisorySnapshots.GetByFeedSnapshotAsync(feed!.Id);
snapshots.Should().ContainSingle(s => s.AdvisoryKey == "ADV-4");
}
private static AdvisoryDocument CreateDoc()
{
var payload = new BsonDocument
{
{ "primaryVulnId", "CVE-2024-0004" },
{ "aliases", new BsonArray { "CVE-2024-0004" } },
{ "affected", new BsonArray { new BsonDocument { { "ecosystem", "npm" }, { "packageName", "pkg" }, { "range", "{}" } } } }
};
return new AdvisoryDocument
{
AdvisoryKey = "ADV-4",
Payload = payload,
Modified = DateTime.UtcNow,
Published = DateTime.UtcNow.AddDays(-4)
};
}
}

View File

@@ -0,0 +1,84 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Concelier.Storage.Postgres.Converters;
using StellaOps.Concelier.Storage.Postgres.Converters.Importers;
using StellaOps.Concelier.Storage.Postgres.Repositories;
using Xunit;
namespace StellaOps.Concelier.Storage.Postgres.Tests;
[Collection(ConcelierPostgresCollection.Name)]
public sealed class OsvImporterMongoTests : IClassFixture<MongoFixture>, IAsyncLifetime
{
private readonly ConcelierPostgresFixture _pg;
private readonly MongoFixture _mongoFixture;
private AdvisoryConversionService _service = default!;
private IAdvisoryRepository _advisories = default!;
private IFeedSnapshotRepository _feedSnapshots = default!;
private IAdvisorySnapshotRepository _advisorySnapshots = default!;
private IMongoCollection<AdvisoryDocument> _collection = default!;
public OsvImporterMongoTests(ConcelierPostgresFixture pg, MongoFixture mongo)
{
_pg = pg;
_mongoFixture = mongo;
}
public async Task InitializeAsync()
{
await _pg.TruncateAllTablesAsync();
var options = _pg.Fixture.CreateOptions();
options.SchemaName = _pg.SchemaName;
var dataSource = new ConcelierDataSource(Options.Create(options), NullLogger<ConcelierDataSource>.Instance);
_advisories = new AdvisoryRepository(dataSource, NullLogger<AdvisoryRepository>.Instance);
_feedSnapshots = new FeedSnapshotRepository(dataSource, NullLogger<FeedSnapshotRepository>.Instance);
_advisorySnapshots = new AdvisorySnapshotRepository(dataSource, NullLogger<AdvisorySnapshotRepository>.Instance);
_service = new AdvisoryConversionService(_advisories);
_collection = _mongoFixture.Database.GetCollection<AdvisoryDocument>("osv_advisories");
await _collection.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
await _collection.InsertOneAsync(CreateDoc());
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
public async Task ImportSnapshot_PersistsOsvSnapshot()
{
var importer = new OsvImporter(_collection, _service, _feedSnapshots, _advisorySnapshots);
var sourceId = Guid.NewGuid();
await importer.ImportSnapshotAsync(sourceId, "osv-snap-1", default);
var advisory = await _advisories.GetByKeyAsync("ADV-OSV-1");
advisory.Should().NotBeNull();
var feed = await _feedSnapshots.GetBySourceAndIdAsync(sourceId, "osv-snap-1");
feed.Should().NotBeNull();
var snapshots = await _advisorySnapshots.GetByFeedSnapshotAsync(feed!.Id);
snapshots.Should().ContainSingle(s => s.AdvisoryKey == "ADV-OSV-1");
}
private static AdvisoryDocument CreateDoc()
{
var payload = new BsonDocument
{
{ "primaryVulnId", "CVE-2024-0100" },
{ "aliases", new BsonArray { "CVE-2024-0100" } },
{ "affected", new BsonArray { new BsonDocument { { "ecosystem", "npm" }, { "packageName", "osv-pkg" }, { "range", "{}" } } } }
};
return new AdvisoryDocument
{
AdvisoryKey = "ADV-OSV-1",
Payload = payload,
Modified = DateTime.UtcNow,
Published = DateTime.UtcNow.AddDays(-2)
};
}
}

View File

@@ -0,0 +1,82 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Concelier.Storage.Postgres.Converters;
using StellaOps.Concelier.Storage.Postgres.Converters.Importers;
using StellaOps.Concelier.Storage.Postgres.Repositories;
using Xunit;
namespace StellaOps.Concelier.Storage.Postgres.Tests;
[Collection(ConcelierPostgresCollection.Name)]
public sealed class ParityRunnerTests : IClassFixture<MongoFixture>, IAsyncLifetime
{
private readonly ConcelierPostgresFixture _pg;
private readonly MongoFixture _mongo;
private AdvisoryConversionService _service = default!;
private IAdvisoryRepository _advisories = default!;
private IFeedSnapshotRepository _feedSnapshots = default!;
private IAdvisorySnapshotRepository _advisorySnapshots = default!;
private IMongoCollection<AdvisoryDocument> _nvd = default!;
private IMongoCollection<AdvisoryDocument> _osv = default!;
public ParityRunnerTests(ConcelierPostgresFixture pg, MongoFixture mongo)
{
_pg = pg;
_mongo = mongo;
}
public async Task InitializeAsync()
{
await _pg.TruncateAllTablesAsync();
var options = _pg.Fixture.CreateOptions();
options.SchemaName = _pg.SchemaName;
var dataSource = new ConcelierDataSource(Options.Create(options), NullLogger<ConcelierDataSource>.Instance);
_advisories = new AdvisoryRepository(dataSource, NullLogger<AdvisoryRepository>.Instance);
_feedSnapshots = new FeedSnapshotRepository(dataSource, NullLogger<FeedSnapshotRepository>.Instance);
_advisorySnapshots = new AdvisorySnapshotRepository(dataSource, NullLogger<AdvisorySnapshotRepository>.Instance);
_service = new AdvisoryConversionService(_advisories);
_nvd = _mongo.Database.GetCollection<AdvisoryDocument>("parity_nvd");
_osv = _mongo.Database.GetCollection<AdvisoryDocument>("parity_osv");
await _nvd.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
await _osv.DeleteManyAsync(FilterDefinition<AdvisoryDocument>.Empty);
var payload = new BsonDocument
{
{ "primaryVulnId", "CVE-2024-0700" },
{ "aliases", new BsonArray { "CVE-2024-0700" } },
{ "affected", new BsonArray { new BsonDocument { { "ecosystem", "npm" }, { "packageName", "parity-pkg" }, { "range", "{}" } } } }
};
await _nvd.InsertOneAsync(new AdvisoryDocument { AdvisoryKey = "ADV-PARITY-KEY", Payload = payload, Modified = DateTime.UtcNow, Published = DateTime.UtcNow.AddDays(-1) });
await _osv.InsertOneAsync(new AdvisoryDocument { AdvisoryKey = "ADV-PARITY-KEY", Payload = payload, Modified = DateTime.UtcNow, Published = DateTime.UtcNow.AddDays(-1) });
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
public async Task ParityRunner_FindsMatchingKeys()
{
var nvdImporter = new NvdImporter(_nvd, _service, _feedSnapshots, _advisorySnapshots);
var osvImporter = new OsvImporter(_osv, _service, _feedSnapshots, _advisorySnapshots);
var nvdSource = Guid.NewGuid();
var osvSource = Guid.NewGuid();
var nvdFeed = (await nvdImporter.ImportSnapshotAsync(nvdSource, "nvd", "snap-parity-nvd", default));
var osvFeed = (await osvImporter.ImportSnapshotAsync(osvSource, "snap-parity-osv", default));
var runner = new ParityRunner(_advisorySnapshots);
var result = await runner.CompareAsync(nvdFeed.Id, osvFeed.Id, default);
result.Match.Should().BeTrue();
result.MissingInA.Should().BeEmpty();
result.MissingInB.Should().BeEmpty();
}
}

View File

@@ -21,6 +21,7 @@ Local status mirror for orchestration sprints to keep doc and code views aligned
| 0151 | ORCH-AIRGAP-57-001 | BLOCKED | Await upstream 56-002. |
| 0151 | ORCH-AIRGAP-58-001 | BLOCKED | Await upstream 57-001. |
| 0151 | ORCH-SVC-32-001 | DONE | Service bootstrap + initial schema/migrations. |
| 0151 | ORCH-GAPS-151-016 | DONE | OR1OR10 gaps: canonical hashes, replay inputs.lock, heartbeat ordering, log/artifact integrity. |
| 0152 | ORCH-SVC-32-002…37-101 | DONE | See `src/Orchestrator/StellaOps.Orchestrator/TASKS.md` for per-task detail. |
Last synced: 2025-11-30 (UTC).
Last synced: 2025-12-03 (UTC).

View File

@@ -24,11 +24,15 @@ public sealed class EvaluationRunRepository : RepositoryBase<PolicyDataSource>,
const string sql = """
INSERT INTO policy.evaluation_runs (
id, tenant_id, project_id, artifact_id, pack_id, pack_version,
risk_profile_id, status, input_hash, metadata, created_by
risk_profile_id, status, result, score,
findings_count, critical_count, high_count, medium_count, low_count,
input_hash, metadata, duration_ms, error_message, created_by
)
VALUES (
@id, @tenant_id, @project_id, @artifact_id, @pack_id, @pack_version,
@risk_profile_id, @status, @input_hash, @metadata::jsonb, @created_by
@risk_profile_id, @status, @result, @score,
@findings_count, @critical_count, @high_count, @medium_count, @low_count,
@input_hash, @metadata::jsonb, @duration_ms, @error_message, @created_by
)
RETURNING *
""";
@@ -45,8 +49,17 @@ public sealed class EvaluationRunRepository : RepositoryBase<PolicyDataSource>,
AddParameter(command, "pack_version", run.PackVersion);
AddParameter(command, "risk_profile_id", run.RiskProfileId);
AddParameter(command, "status", StatusToString(run.Status));
AddParameter(command, "result", run.Result.HasValue ? ResultToString(run.Result.Value) : null);
AddParameter(command, "score", run.Score);
AddParameter(command, "findings_count", run.FindingsCount);
AddParameter(command, "critical_count", run.CriticalCount);
AddParameter(command, "high_count", run.HighCount);
AddParameter(command, "medium_count", run.MediumCount);
AddParameter(command, "low_count", run.LowCount);
AddParameter(command, "input_hash", run.InputHash);
AddJsonbParameter(command, "metadata", run.Metadata);
AddParameter(command, "duration_ms", run.DurationMs);
AddParameter(command, "error_message", run.ErrorMessage);
AddParameter(command, "created_by", run.CreatedBy);
await using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);

View File

@@ -12,7 +12,11 @@ public sealed class EvaluationRunRepositoryTests : IAsyncLifetime
{
private readonly PolicyPostgresFixture _fixture;
private readonly EvaluationRunRepository _repository;
private readonly PackRepository _packRepository;
private readonly PackVersionRepository _packVersionRepository;
private readonly string _tenantId = Guid.NewGuid().ToString();
private readonly Guid _packId = Guid.NewGuid();
private const int SeedPackVersion = 1;
public EvaluationRunRepositoryTests(PolicyPostgresFixture fixture)
{
@@ -21,10 +25,41 @@ public sealed class EvaluationRunRepositoryTests : IAsyncLifetime
var options = fixture.Fixture.CreateOptions();
options.SchemaName = fixture.SchemaName;
var dataSource = new PolicyDataSource(Options.Create(options), NullLogger<PolicyDataSource>.Instance);
_packRepository = new PackRepository(dataSource, NullLogger<PackRepository>.Instance);
_packVersionRepository = new PackVersionRepository(dataSource, NullLogger<PackVersionRepository>.Instance);
_repository = new EvaluationRunRepository(dataSource, NullLogger<EvaluationRunRepository>.Instance);
}
public Task InitializeAsync() => _fixture.TruncateAllTablesAsync();
public async Task InitializeAsync()
{
await _fixture.TruncateAllTablesAsync();
var pack = new PackEntity
{
Id = _packId,
TenantId = _tenantId,
Name = "eval-pack",
DisplayName = "Evaluation Pack",
ActiveVersion = SeedPackVersion,
CreatedAt = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow,
CreatedBy = "tests"
};
await _packRepository.CreateAsync(pack);
var packVersion = new PackVersionEntity
{
Id = Guid.NewGuid(),
PackId = _packId,
Version = SeedPackVersion,
RulesHash = "seed-hash",
IsPublished = true,
PublishedAt = DateTimeOffset.UtcNow,
PublishedBy = "tests",
CreatedBy = "tests"
};
await _packVersionRepository.CreateAsync(packVersion);
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
@@ -37,8 +72,8 @@ public sealed class EvaluationRunRepositoryTests : IAsyncLifetime
TenantId = _tenantId,
ProjectId = "project-123",
ArtifactId = "registry.example.com/app:v1.0",
PackId = Guid.NewGuid(),
PackVersion = 1,
PackId = _packId,
PackVersion = SeedPackVersion,
Status = EvaluationStatus.Pending
};
@@ -204,6 +239,8 @@ public sealed class EvaluationRunRepositoryTests : IAsyncLifetime
{
Id = Guid.NewGuid(),
TenantId = _tenantId,
PackId = _packId,
PackVersion = SeedPackVersion,
Status = EvaluationStatus.Completed,
Result = EvaluationResult.Pass,
Score = 100,
@@ -215,6 +252,8 @@ public sealed class EvaluationRunRepositoryTests : IAsyncLifetime
{
Id = Guid.NewGuid(),
TenantId = _tenantId,
PackId = _packId,
PackVersion = SeedPackVersion,
Status = EvaluationStatus.Completed,
Result = EvaluationResult.Fail,
Score = 50,
@@ -245,6 +284,8 @@ public sealed class EvaluationRunRepositoryTests : IAsyncLifetime
Id = Guid.NewGuid(),
TenantId = _tenantId,
ProjectId = projectId,
PackId = _packId,
PackVersion = SeedPackVersion,
Status = EvaluationStatus.Pending
};
}

View File

@@ -170,6 +170,8 @@ public sealed class PolicyAuditRepositoryTests : IAsyncLifetime
public async Task DeleteOld_RemovesOldAudits()
{
// Arrange
// Clear any cross-test residue defensively.
await _repository.DeleteOldAsync(DateTimeOffset.MaxValue);
await _repository.CreateAsync(CreateAudit("old-action"));
// Act - Delete audits older than future date

View File

@@ -12,7 +12,11 @@ public sealed class RuleRepositoryTests : IAsyncLifetime
{
private readonly PolicyPostgresFixture _fixture;
private readonly RuleRepository _repository;
private readonly PackRepository _packRepository;
private readonly PackVersionRepository _packVersionRepository;
private readonly Guid _packId = Guid.NewGuid();
private readonly Guid _packVersionId = Guid.NewGuid();
private readonly string _tenantId = Guid.NewGuid().ToString();
public RuleRepositoryTests(PolicyPostgresFixture fixture)
{
@@ -21,10 +25,46 @@ public sealed class RuleRepositoryTests : IAsyncLifetime
var options = fixture.Fixture.CreateOptions();
options.SchemaName = fixture.SchemaName;
var dataSource = new PolicyDataSource(Options.Create(options), NullLogger<PolicyDataSource>.Instance);
_packRepository = new PackRepository(dataSource, NullLogger<PackRepository>.Instance);
_packVersionRepository = new PackVersionRepository(dataSource, NullLogger<PackVersionRepository>.Instance);
_repository = new RuleRepository(dataSource, NullLogger<RuleRepository>.Instance);
}
public Task InitializeAsync() => _fixture.TruncateAllTablesAsync();
public async Task InitializeAsync()
{
await _fixture.TruncateAllTablesAsync();
var pack = new PackEntity
{
Id = _packId,
TenantId = _tenantId,
Name = "test-pack",
DisplayName = "Test Pack",
Description = "Seed pack for rule tests",
ActiveVersion = 1,
CreatedAt = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow,
CreatedBy = "tests"
};
await _packRepository.CreateAsync(pack);
var packVersion = new PackVersionEntity
{
Id = _packVersionId,
PackId = _packId,
Version = 1,
Description = "seed version",
RulesHash = "hash",
IsPublished = true,
PublishedAt = DateTimeOffset.UtcNow,
PublishedBy = "tests",
CreatedBy = "tests"
};
await _packVersionRepository.CreateAsync(packVersion);
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]

17924
stdout Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
{
"baseMetrics": {
"ac": "Low",
"at": "None",
"av": "Network",
"pr": "None",
"sa": "High",
"sc": "High",
"si": "High",
"ui": "None",
"va": "High",
"vc": "High",
"vi": "High"
},
"createdAt": "2025-12-03T00:00:00Z",
"createdBy": "policy-scorer@stella",
"environmentalMetrics": {
"ar": "Medium",
"cr": "High",
"ir": "Medium",
"mac": "Low",
"mat": "None",
"mav": "Network",
"mpr": "None",
"ms": "Unchanged",
"mui": "None",
"mva": "High",
"mvc": "High",
"mvi": "High"
},
"policyRef": {
"hash": "3c1dff9075a14da4c6ae4e8b1e2c9f7569af5f5e90e78c9a0a82f86ccb63d4f9",
"id": "cvss-policy-v1",
"version": "1.2.0"
},
"scores": {
"base": 9.8,
"environmental": 9.4,
"threat": 9.8
},
"supplementalMetrics": {
"safety": "Safe"
},
"tenantId": "tenant-acme",
"threatMetrics": {
"ad": "High",
"rs": "Unreported"
},
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/AD:H/RS:X/CR:H/IR:M/AR:M/MAV:N/MAC:L/MAT:N/MPR:N/MUI:N/MVC:H/MVI:H/MVA:H/MS:U",
"vulnerabilityId": "CVE-2024-1234"
}

View File

@@ -0,0 +1 @@
bac7e113ad5a27a7fc013608ef3a3b90a3e4d98efbdedbc5953d2c29a3545fef