up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-03 00:10:19 +02:00
parent ea1d58a89b
commit 37cba83708
158 changed files with 147438 additions and 867 deletions

View File

@@ -2,9 +2,9 @@
| Task ID | Status | Sprint | Owners | Key dependency / next step | Notes |
| --- | --- | --- | --- | --- | --- |
| SAMPLES-GRAPH-24-003 | BLOCKED (2025-11-18) | SPRINT_0509_0001_0001_samples | Samples Guild · SBOM Service Guild | Await Graph Guild overlay field/manifest decision (checkpoint 2025-11-22) and approval of mock SBOM source list. | Large-scale SBOM graph fixture (~40k nodes) + policy overlay snapshot for perf/regression suites. |
| SAMPLES-GRAPH-24-004 | TODO | SPRINT_0509_0001_0001_samples | Samples Guild · UI Guild | Depends on SAMPLES-GRAPH-24-003 fixture availability. | Vulnerability explorer JSON/CSV fixtures with conflicting evidence/policy outputs for UI/CLI tests. |
| SAMPLES-LNM-22-001 | BLOCKED | SPRINT_0509_0001_0001_samples | Samples Guild · Concelier Guild | Waiting on finalized Concelier advisory linkset schema. | Advisory observation/linkset fixtures (NVD, GHSA, OSV disagreements). |
| SAMPLES-LNM-22-002 | BLOCKED | SPRINT_0509_0001_0001_samples | Samples Guild · Excititor Guild | Depends on 22-001 outputs + Excititor linkset implementation. | VEX observation/linkset fixtures with status conflicts/path relevance; include raw blobs. |
| SAMPLES-GRAPH-24-003 | DONE (2025-12-02) | SPRINT_0509_0001_0001_samples | Samples Guild · SBOM Service Guild | Delivered `samples/graph/graph-40k` fixture with overlay and manifest; see README + hashes. | Large-scale SBOM graph fixture (~40k nodes) + policy overlay snapshot for perf/regression suites. |
| SAMPLES-GRAPH-24-004 | DONE (2025-12-02) | SPRINT_0509_0001_0001_samples | Samples Guild · UI Guild | Built from graph-40k overlays; artefacts in `samples/graph/graph-40k/explorer`. | Vulnerability explorer JSON/CSV fixtures with conflicting evidence/policy outputs for UI/CLI tests. |
| SAMPLES-LNM-22-001 | DONE (2025-11-24) | SPRINT_0509_0001_0001_samples | Samples Guild · Concelier Guild | Fixtures published under `samples/linkset/lnm-22-001/`. | Advisory observation/linkset fixtures (NVD, GHSA, OSV disagreements). |
| SAMPLES-LNM-22-002 | DONE (2025-11-24) | SPRINT_0509_0001_0001_samples | Samples Guild · Excititor Guild | Fixtures published under `samples/linkset/lnm-22-002/`. | VEX observation/linkset fixtures with status conflicts/path relevance; include raw blobs. |
Status updates must stay in sync with the corresponding sprint tracker.

View File

@@ -6,37 +6,34 @@
- Ensure offline parity: fixtures packaged for Offline Kit consumption (NDJSON + manifest hashes).
## Assumptions / Pending confirmations
- Overlay format: waiting on Graph Guild decision (checkpoint 2025-11-22) regarding overlay fields and snapshot manifest layout.
- SBOM bundle source: use scanner surface mock bundle v1 unless real caches land; confirm with Graph Guild.
- Tenant: default to `demo-tenant` unless advised otherwise; all IDs and timestamps must be deterministic.
- Overlay format resolved: `policy.overlay.v1` with `overlay_id = sha256(tenant|nodeId|overlayKind)`, verdict + severity, optional edge to policy rule node for bench compatibility.
- SBOM bundle source: scanner surface mock bundle v1; swap in real cache when approved without schema changes.
- Tenant: `demo-tenant`; timestamps frozen to `2025-11-22T00:00:00Z`.
## Proposed fixture contents
- `nodes.ndjson`: ~40k nodes; sorted by id; includes artifact, package, relationship nodes.
- `edges.ndjson`: matching edges; sorted by id.
- `overlays/policy.ndjson`: policy overlay snapshot aligned with chosen overlay schema.
- `manifest.json`: hashes (SHA-256) of all files plus counts; UTC timestamps rounded to seconds.
- `README.md`: execution + verification steps, expected counts/hashes.
## Canonical fixture (delivered 2025-12-02)
- Location: `samples/graph/graph-40k/`
- `nodes.ndjson`: 40,000 component nodes (`pkg:pypi/demo-*`)
- `edges.ndjson`: 100,071 `DEPENDS_ON` edges (fan-out ≤4, DAG order)
- `overlay.ndjson`: 100 `policy.overlay.v1` records (verdict/severity + optional policy-rule edge)
- `manifest.json`: hashes (SHA-256) and counts (nodes `d14e8c64…`, edges `143a2944…`, overlay `627a0d8c…`)
- `README.md` and `verify.py`: usage, hashes, offline verification
## Generation sketch
1) Start from existing mock SBOM bundle (scanner surface v1); sample driver script will:
- deterministically seed random generators;
- produce nodes/edges via Graph Indexer schema helpers;
- emit overlays using placeholder policy verdicts (allow/deny/defer) until final schema confirmed.
2) Write NDJSON with stable ordering; compute SHA-256 for each file; write manifest.
3) Run validation script to assert counts, schema shape, and hash reproducibility.
## Generation sketch (implemented)
1) Deterministic generator `samples/graph/scripts/generate_canonical.py` (seed `424242`, snapshot `graph-40k-policy-overlay-20251122`).
2) Writes nodes/edges/overlay with sorted keys, then manifest with hashes/counts.
3) `verify.py` recomputes hashes/counts to confirm reproducibility.
## Interim fixtures (delivered 2025-12-01)
- Synthetic deterministic graphs generated under `samples/graph/interim/`:
## Interim fixtures (still available, delivered 2025-12-01)
- Synthetic deterministic graphs under `samples/graph/interim/`:
- `graph-50k` (50k nodes, ~200k edges)
- `graph-100k` (100k nodes, ~400k edges)
- Minimal schema (`id, kind, name, version, tenant`), seeded RNG, stable ordering, manifests with hashes.
- Purpose: unblock BENCH-GRAPH-21-001/002 while overlay format is finalized. Overlays not included yet.
- Purpose: throughput/latency benches; overlay-free.
## Open items (to resolve before canonical data generation)
- Confirm overlay field set and file naming (Graph Guild, due 2025-11-22).
- Confirm allowed mock SBOM source list and artifact naming (Graph Guild / SBOM Service Guild).
- Provide expected node/edge cardinality breakdown (packages vs files vs relationships) to guide generation.
## Open items
- Regenerate if Graph overlay schema changes; update manifest/hashes and downstream references.
- Consider adding advisory/VEX nodes once Graph/Concelier schema freeze lands; currently component-focused.
## Next steps
- Keep SAMPLES-GRAPH-24-003 blocked until overlay/schema confirmation, but interim fixtures are available for benches.
- Once overlay schema final, extend generator to emit overlays + CAS manifests and promote to official fixture.
- Wire `graph-40k` into BENCH-GRAPH-21-001/002 results and UI fixtures (SAMPLES-GRAPH-24-004).
- Add CAS/DSSE manifest once Offline Kit package format is finalized.

View File

@@ -0,0 +1,33 @@
# Graph-40k fixture (SAMPLES-GRAPH-24-003)
Canonical large SBOM graph fixture with policy overlay for performance/regression suites.
## Contents
- `nodes.ndjson` — 40,000 component nodes (`pkg:pypi/demo-*`) for tenant `demo-tenant`.
- `edges.ndjson` — 100,071 `DEPENDS_ON` edges (fan-out ≤4, DAG order).
- `overlay.ndjson` — 100 `policy.overlay.v1` records with verdict/severity + optional edge to policy rule node.
- `manifest.json` — counts and SHA-256 hashes.
- `verify.py` — offline verifier for hashes/counts.
## Determinism
- Fixed seed `424242`, snapshot `graph-40k-policy-overlay-20251122`, timestamp `2025-11-22T00:00:00Z`.
- Sorted NDJSON rows, stable overlay ID scheme `sha256(tenant|nodeId|overlayKind)`.
- Generated via `samples/graph/scripts/generate_canonical.py` (no network access).
## Hashes (from manifest)
- nodes: `d14e8c642d1b4450d8779971da79cecc190af22fe237dee56ec0dd583f0442f5`
- edges: `143a294446f46ffa273846e821f83fd5e5023aea2cf74947ba7ccaeeab7ceba4`
- overlay: `627a0d8c273f55b2426c8c005037ef01d88324a75084ad44bd620b1330a539cc`
## Verify
```bash
cd samples/graph/graph-40k
python verify.py
```
## Regenerate (optional)
```bash
python ../scripts/generate_canonical.py --out-dir samples/graph/graph-40k
```
Ensure manifest hashes match after regeneration before promoting to offline kits.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
# Vulnerability Explorer fixtures (SAMPLES-GRAPH-24-004)
Derives a small, deterministic explorer dataset from the canonical graph-40k fixture.
## Files
- `vuln-explorer.json` — 5 records covering mixed policy verdicts and reachability (reachable/unreachable alternation).
- `vuln-explorer.csv` — same data for CSV-driven UI/CLI tests; `evidence` is `;`-separated.
- `manifest.json` — SHA-256 hashes for both files.
## Source
- Built from `samples/graph/graph-40k/overlay.ndjson` (policy overlays) using `samples/graph/scripts/build_explorer_fixture.py`.
- Tenant: `demo-tenant`; snapshot: `graph-40k-policy-overlay-20251122`.
## Determinism
- Fixed advisory list and order.
- Overlay rows sorted by `overlay_id`; first 5 overlays selected.
- No randomness; rerunning `build_explorer_fixture.py` produces identical hashes.
## Verify
```bash
python samples/graph/scripts/build_explorer_fixture.py
python - <<'PY'
import json,hashlib,Pathlib
from pathlib import Path
base=Path("samples/graph/graph-40k/explorer")
for name in ["vuln-explorer.json","vuln-explorer.csv"]:
h=hashlib.sha256((base/name).read_bytes()).hexdigest()
print(name, h)
PY
```
## Consumption hints
- UI: seed list/detail views and policy conflict badges (fields: `reachability`, `policy_verdict`, `conflict`).
- CLI: pipe JSON into explorer tests or convert from CSV as needed.

View File

@@ -0,0 +1,15 @@
{
"advisories": [
"CVE-2024-0001",
"CVE-2024-0002",
"CVE-2023-9999",
"CVE-2025-1234",
"CVE-2022-4242"
],
"count": 5,
"fixture": "graph-40k",
"hashes": {
"vuln-explorer.csv": "d116f2451fe82c7895325b1ceda10f024dd5df822131bf5882f5a1b97ea60ea0",
"vuln-explorer.json": "d34938dbb6d7de14751c6e49392de27766703425f18ab949861bc1d1147abb01"
}
}

View File

@@ -0,0 +1,6 @@
component,advisory,advisory_severity,reachability,status,policy_overlay_id,policy_verdict,policy_severity,policy_rule_id,evidence,conflict,snapshot,tenant
pkg:pypi/demo-15400@1.0.0,CVE-2024-0001,critical,reachable,affected,00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24,deny,low,RULE-06000,sbom:mock-sbom-v1;overlay:00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24,policy_deny_vs_scanner_affected,graph-40k-policy-overlay-20251122,demo-tenant
pkg:pypi/demo-6040@1.0.0,CVE-2024-0002,high,unreachable,not_affected,065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99,defer,critical,RULE-35600,sbom:mock-sbom-v1;overlay:065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99,,graph-40k-policy-overlay-20251122,demo-tenant
pkg:pypi/demo-14320@1.0.0,CVE-2023-9999,medium,reachable,affected,06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457,deny,none,RULE-04800,sbom:mock-sbom-v1;overlay:06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457,policy_deny_vs_scanner_affected,graph-40k-policy-overlay-20251122,demo-tenant
pkg:pypi/demo-4961@1.0.1,CVE-2025-1234,low,unreachable,not_affected,076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6,defer,high,RULE-34400,sbom:mock-sbom-v1;overlay:076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6,,graph-40k-policy-overlay-20251122,demo-tenant
pkg:pypi/demo-6761@1.0.1,CVE-2022-4242,none,reachable,affected,08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546,deny,none,RULE-36400,sbom:mock-sbom-v1;overlay:08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546,policy_deny_vs_scanner_affected,graph-40k-policy-overlay-20251122,demo-tenant
1 component advisory advisory_severity reachability status policy_overlay_id policy_verdict policy_severity policy_rule_id evidence conflict snapshot tenant
2 pkg:pypi/demo-15400@1.0.0 CVE-2024-0001 critical reachable affected 00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24 deny low RULE-06000 sbom:mock-sbom-v1;overlay:00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24 policy_deny_vs_scanner_affected graph-40k-policy-overlay-20251122 demo-tenant
3 pkg:pypi/demo-6040@1.0.0 CVE-2024-0002 high unreachable not_affected 065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99 defer critical RULE-35600 sbom:mock-sbom-v1;overlay:065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99 graph-40k-policy-overlay-20251122 demo-tenant
4 pkg:pypi/demo-14320@1.0.0 CVE-2023-9999 medium reachable affected 06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457 deny none RULE-04800 sbom:mock-sbom-v1;overlay:06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457 policy_deny_vs_scanner_affected graph-40k-policy-overlay-20251122 demo-tenant
5 pkg:pypi/demo-4961@1.0.1 CVE-2025-1234 low unreachable not_affected 076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6 defer high RULE-34400 sbom:mock-sbom-v1;overlay:076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6 graph-40k-policy-overlay-20251122 demo-tenant
6 pkg:pypi/demo-6761@1.0.1 CVE-2022-4242 none reachable affected 08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546 deny none RULE-36400 sbom:mock-sbom-v1;overlay:08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546 policy_deny_vs_scanner_affected graph-40k-policy-overlay-20251122 demo-tenant

View File

@@ -0,0 +1,92 @@
[
{
"advisory": "CVE-2024-0001",
"advisory_severity": "critical",
"component": "pkg:pypi/demo-15400@1.0.0",
"conflict": "policy_deny_vs_scanner_affected",
"evidence": [
"sbom:mock-sbom-v1",
"overlay:00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24"
],
"policy_overlay_id": "00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24",
"policy_rule_id": "RULE-06000",
"policy_severity": "low",
"policy_verdict": "deny",
"reachability": "reachable",
"snapshot": "graph-40k-policy-overlay-20251122",
"status": "affected",
"tenant": "demo-tenant"
},
{
"advisory": "CVE-2024-0002",
"advisory_severity": "high",
"component": "pkg:pypi/demo-6040@1.0.0",
"conflict": "",
"evidence": [
"sbom:mock-sbom-v1",
"overlay:065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99"
],
"policy_overlay_id": "065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99",
"policy_rule_id": "RULE-35600",
"policy_severity": "critical",
"policy_verdict": "defer",
"reachability": "unreachable",
"snapshot": "graph-40k-policy-overlay-20251122",
"status": "not_affected",
"tenant": "demo-tenant"
},
{
"advisory": "CVE-2023-9999",
"advisory_severity": "medium",
"component": "pkg:pypi/demo-14320@1.0.0",
"conflict": "policy_deny_vs_scanner_affected",
"evidence": [
"sbom:mock-sbom-v1",
"overlay:06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457"
],
"policy_overlay_id": "06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457",
"policy_rule_id": "RULE-04800",
"policy_severity": "none",
"policy_verdict": "deny",
"reachability": "reachable",
"snapshot": "graph-40k-policy-overlay-20251122",
"status": "affected",
"tenant": "demo-tenant"
},
{
"advisory": "CVE-2025-1234",
"advisory_severity": "low",
"component": "pkg:pypi/demo-4961@1.0.1",
"conflict": "",
"evidence": [
"sbom:mock-sbom-v1",
"overlay:076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6"
],
"policy_overlay_id": "076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6",
"policy_rule_id": "RULE-34400",
"policy_severity": "high",
"policy_verdict": "defer",
"reachability": "unreachable",
"snapshot": "graph-40k-policy-overlay-20251122",
"status": "not_affected",
"tenant": "demo-tenant"
},
{
"advisory": "CVE-2022-4242",
"advisory_severity": "none",
"component": "pkg:pypi/demo-6761@1.0.1",
"conflict": "policy_deny_vs_scanner_affected",
"evidence": [
"sbom:mock-sbom-v1",
"overlay:08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546"
],
"policy_overlay_id": "08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546",
"policy_rule_id": "RULE-36400",
"policy_severity": "none",
"policy_verdict": "deny",
"reachability": "reachable",
"snapshot": "graph-40k-policy-overlay-20251122",
"status": "affected",
"tenant": "demo-tenant"
}
]

View File

@@ -0,0 +1,26 @@
{
"counts": {
"edges": 100071,
"nodes": 40000,
"overlays": {
"policy.overlay.v1": 100
}
},
"generated_at": "2025-11-22T00:00:00Z",
"hashes": {
"edges_ndjson_sha256": "143a294446f46ffa273846e821f83fd5e5023aea2cf74947ba7ccaeeab7ceba4",
"nodes_ndjson_sha256": "d14e8c642d1b4450d8779971da79cecc190af22fe237dee56ec0dd583f0442f5",
"overlay_ndjson_sha256": "627a0d8c273f55b2426c8c005037ef01d88324a75084ad44bd620b1330a539cc"
},
"inputs": {
"sbom_source": "mock-sbom-v1"
},
"overlay": {
"id_scheme": "sha256(tenant|nodeId|overlayKind)",
"kind": "policy.overlay.v1",
"path": "overlay.ndjson"
},
"seed": 424242,
"snapshot_id": "graph-40k-policy-overlay-20251122",
"tenant": "demo-tenant"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,100 @@
{"explain":"demo policy decision for demo-15400","node_id":"pkg:pypi/demo-15400@1.0.0","overlay_id":"00f6635f7e16f4249116313369beadeee8836ae6da36dedaec23b38130b92f24","overlay_kind":"policy.overlay.v1","rule_id":"RULE-06000","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-15400@1.0.0","target":"policy:rule:RULE-06000","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-6040","node_id":"pkg:pypi/demo-6040@1.0.0","overlay_id":"065090e3b6aa2f247cfd6844c5d00dda582516b606f068adbe497ac84fb71f99","overlay_kind":"policy.overlay.v1","rule_id":"RULE-35600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-6040@1.0.0","target":"policy:rule:RULE-35600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-14320","node_id":"pkg:pypi/demo-14320@1.0.0","overlay_id":"06a3cb2fef361ef22f596d1cb2a9dba7da6cf4316b43892f3aa1041b55fdf457","overlay_kind":"policy.overlay.v1","rule_id":"RULE-04800","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-14320@1.0.0","target":"policy:rule:RULE-04800","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-4961","node_id":"pkg:pypi/demo-4961@1.0.1","overlay_id":"076cf3660de3a883d6a148a1850347300bd368e8177491b3c8a880e1f000bda6","overlay_kind":"policy.overlay.v1","rule_id":"RULE-34400","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-4961@1.0.1","target":"policy:rule:RULE-34400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-6761","node_id":"pkg:pypi/demo-6761@1.0.1","overlay_id":"08471f5759128be339110c0a72cf3cc6de36da9f5315a148f9e41602af808546","overlay_kind":"policy.overlay.v1","rule_id":"RULE-36400","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-6761@1.0.1","target":"policy:rule:RULE-36400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-4600","node_id":"pkg:pypi/demo-4600@1.0.0","overlay_id":"08ca8c4ddf56fc9bea303282a6055aa6c206b353f765f022a8b496a68d555f4e","overlay_kind":"policy.overlay.v1","rule_id":"RULE-34000","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-4600@1.0.0","target":"policy:rule:RULE-34000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-22600","node_id":"pkg:pypi/demo-22600@1.0.0","overlay_id":"0f188a4d940341451b6dbfd4c521d1a048fc2b6a83984f441b436dae46a22622","overlay_kind":"policy.overlay.v1","rule_id":"RULE-14000","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-22600@1.0.0","target":"policy:rule:RULE-14000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-7120","node_id":"pkg:pypi/demo-7120@1.0.0","overlay_id":"11aeb49bf42ab18724a9b9f4b52179d76a77b9239ba08f1cf10a60f2d30e07bd","overlay_kind":"policy.overlay.v1","rule_id":"RULE-36800","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-7120@1.0.0","target":"policy:rule:RULE-36800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-26200","node_id":"pkg:pypi/demo-26200@1.0.0","overlay_id":"12a82e8a3e21aebc98fbe91de3bb3b7288b02754274b977be52e2741bc57520a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-18000","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-26200@1.0.0","target":"policy:rule:RULE-18000","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-31961","node_id":"pkg:pypi/demo-31961@1.0.1","overlay_id":"16fe80c9d4496b8bb446d8b260696b7882fe379ec29dd5284d737f4f39559183","overlay_kind":"policy.overlay.v1","rule_id":"RULE-24400","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-31961@1.0.1","target":"policy:rule:RULE-24400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-34841","node_id":"pkg:pypi/demo-34841@1.0.1","overlay_id":"1ed19ed6c761fcb27bfd9d67bad34da3572b2d1a228a8b0a0377bfc25e8cc38b","overlay_kind":"policy.overlay.v1","rule_id":"RULE-27600","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-34841@1.0.1","target":"policy:rule:RULE-27600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-20800","node_id":"pkg:pypi/demo-20800@1.0.0","overlay_id":"1f075142eea7b0d274a30b55f102e0ebaaa0d385a94dc60217270b632a516b31","overlay_kind":"policy.overlay.v1","rule_id":"RULE-12000","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-20800@1.0.0","target":"policy:rule:RULE-12000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-24400","node_id":"pkg:pypi/demo-24400@1.0.0","overlay_id":"1f8ad1be2a2298ef6ffc345171c22c2f0d8f8c64419f8c1797e1d0724bdc2970","overlay_kind":"policy.overlay.v1","rule_id":"RULE-16000","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-24400@1.0.0","target":"policy:rule:RULE-16000","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-29801","node_id":"pkg:pypi/demo-29801@1.0.1","overlay_id":"20141ff2685b718a9ecf5c609ebd207f28c105f6906976c57304c6eac8bc2e68","overlay_kind":"policy.overlay.v1","rule_id":"RULE-22000","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-29801@1.0.1","target":"policy:rule:RULE-22000","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-27641","node_id":"pkg:pypi/demo-27641@1.0.1","overlay_id":"20b6a284c90061f2fb4569e87ffe3afb341d6b5b8ce74622bf2516d1448862eb","overlay_kind":"policy.overlay.v1","rule_id":"RULE-19600","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-27641@1.0.1","target":"policy:rule:RULE-19600","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-29081","node_id":"pkg:pypi/demo-29081@1.0.1","overlay_id":"21c1c06a5e4ba9b26ed55ace0f98438a2064ff3adb91faf6a4753dfefcbaa797","overlay_kind":"policy.overlay.v1","rule_id":"RULE-21200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-29081@1.0.1","target":"policy:rule:RULE-21200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-19721","node_id":"pkg:pypi/demo-19721@1.0.1","overlay_id":"26bec47969f1bace1fb739006919f4c9435c379831fd7cc8c7983f3133098f20","overlay_kind":"policy.overlay.v1","rule_id":"RULE-10800","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-19721@1.0.1","target":"policy:rule:RULE-10800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-38441","node_id":"pkg:pypi/demo-38441@1.0.1","overlay_id":"27372ff2b40f0f0fefd4a9e463f97b45b67047152886e2ada438fb1c1a1952f2","overlay_kind":"policy.overlay.v1","rule_id":"RULE-31600","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-38441@1.0.1","target":"policy:rule:RULE-31600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-17921","node_id":"pkg:pypi/demo-17921@1.0.1","overlay_id":"2a7fcdb6269249a627ff44ee3a201f3ebd6f4a33edb87e1a4b1696d7b1025701","overlay_kind":"policy.overlay.v1","rule_id":"RULE-08800","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-17921@1.0.1","target":"policy:rule:RULE-08800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-17200","node_id":"pkg:pypi/demo-17200@1.0.0","overlay_id":"2da59c89375950b114eb110344c700df1af9e7a4ddcb241c1c0da5dc3d1d80a1","overlay_kind":"policy.overlay.v1","rule_id":"RULE-08000","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-17200@1.0.0","target":"policy:rule:RULE-08000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-35921","node_id":"pkg:pypi/demo-35921@1.0.1","overlay_id":"2e3df2943656dfbc96d5cc9d9fa4ed6a8f87df84789bf47855cf59d0b803ece1","overlay_kind":"policy.overlay.v1","rule_id":"RULE-28800","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-35921@1.0.1","target":"policy:rule:RULE-28800","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-23681","node_id":"pkg:pypi/demo-23681@1.0.1","overlay_id":"2e5d6cf6bca4774b171cd81a201d82d60c67d5c82be92e524e26edd216b74fd0","overlay_kind":"policy.overlay.v1","rule_id":"RULE-15200","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-23681@1.0.1","target":"policy:rule:RULE-15200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-4240","node_id":"pkg:pypi/demo-4240@1.0.0","overlay_id":"3522560d95b46feec42d0be807cf9a8cf9bcb8c71b35a4708af5e00c0ebd0d42","overlay_kind":"policy.overlay.v1","rule_id":"RULE-33600","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-4240@1.0.0","target":"policy:rule:RULE-33600","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-28361","node_id":"pkg:pypi/demo-28361@1.0.1","overlay_id":"3cd52522d3bcdf2e061be34e227f3314fe40848449c2552518b984da9b396eab","overlay_kind":"policy.overlay.v1","rule_id":"RULE-20400","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-28361@1.0.1","target":"policy:rule:RULE-20400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-20080","node_id":"pkg:pypi/demo-20080@1.0.0","overlay_id":"3f3c04bce6724af0f15641c75f4e8487226149c1c05913304f9f0ea0f8136c3c","overlay_kind":"policy.overlay.v1","rule_id":"RULE-11200","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-20080@1.0.0","target":"policy:rule:RULE-11200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-15040","node_id":"pkg:pypi/demo-15040@1.0.0","overlay_id":"46f8052bed54eed8f59a3ce612d24edf6f19b4361795f661d1682fe1d4c84df3","overlay_kind":"policy.overlay.v1","rule_id":"RULE-05600","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-15040@1.0.0","target":"policy:rule:RULE-05600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-28000","node_id":"pkg:pypi/demo-28000@1.0.0","overlay_id":"473d00587b256d93ecb8d3beabb6b6ed9d17ec65560ec29f3314a2334a47d88d","overlay_kind":"policy.overlay.v1","rule_id":"RULE-20000","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-28000@1.0.0","target":"policy:rule:RULE-20000","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-32320","node_id":"pkg:pypi/demo-32320@1.0.0","overlay_id":"4a5f730c78be6f6ab37b6585283834901c8d1149dce4c1c6a0d060095d04a74d","overlay_kind":"policy.overlay.v1","rule_id":"RULE-24800","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-32320@1.0.0","target":"policy:rule:RULE-24800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-21881","node_id":"pkg:pypi/demo-21881@1.0.1","overlay_id":"50e4cb1286bfddfee0fdbfa14e0171c55d8213cfeee3db952700070760d8b0cb","overlay_kind":"policy.overlay.v1","rule_id":"RULE-13200","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-21881@1.0.1","target":"policy:rule:RULE-13200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-8561","node_id":"pkg:pypi/demo-8561@1.0.1","overlay_id":"53887178f6f3c16748cf963697941c4394b328fa50585029151b7f8ce7b9c879","overlay_kind":"policy.overlay.v1","rule_id":"RULE-38400","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-8561@1.0.1","target":"policy:rule:RULE-38400","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-39161","node_id":"pkg:pypi/demo-39161@1.0.1","overlay_id":"5436b7bda222583e8a2424febc18f5cea9786c36ef5117b899bb12d7ca898d14","overlay_kind":"policy.overlay.v1","rule_id":"RULE-32400","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-39161@1.0.1","target":"policy:rule:RULE-32400","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-9641","node_id":"pkg:pypi/demo-9641@1.0.1","overlay_id":"569537ee49b377bafb1e0bc377b611bd1fc9deda7ee5fef159775b003f02afb0","overlay_kind":"policy.overlay.v1","rule_id":"RULE-39600","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-9641@1.0.1","target":"policy:rule:RULE-39600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-20440","node_id":"pkg:pypi/demo-20440@1.0.0","overlay_id":"57cb73d1d4d01babb0288fc3fa9920bfef967df09d8dfa1ddcadfaf5d19dda28","overlay_kind":"policy.overlay.v1","rule_id":"RULE-11600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-20440@1.0.0","target":"policy:rule:RULE-11600","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-17561","node_id":"pkg:pypi/demo-17561@1.0.1","overlay_id":"583342875a32559c2d5a8a7f4624a374cfc93bcaf1248c677eb9f8a4af2a5410","overlay_kind":"policy.overlay.v1","rule_id":"RULE-08400","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-17561@1.0.1","target":"policy:rule:RULE-08400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-37361","node_id":"pkg:pypi/demo-37361@1.0.1","overlay_id":"59692ee61859b213e4e35a8a38109c61214e49f4593731fc0448df45176df42f","overlay_kind":"policy.overlay.v1","rule_id":"RULE-30400","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-37361@1.0.1","target":"policy:rule:RULE-30400","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-0","node_id":"pkg:pypi/demo-0@1.0.0","overlay_id":"5ad64d33fe51038314ae68f71bd7c9fbbb9d3c6f1310a93faa43705f7158be24","overlay_kind":"policy.overlay.v1","rule_id":"RULE-00000","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-0@1.0.0","target":"policy:rule:RULE-00000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-30520","node_id":"pkg:pypi/demo-30520@1.0.0","overlay_id":"5aef610e89bd3533396db32a26396868c3dfc44be0fa062c717e8436c6faf68f","overlay_kind":"policy.overlay.v1","rule_id":"RULE-22800","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-30520@1.0.0","target":"policy:rule:RULE-22800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-36641","node_id":"pkg:pypi/demo-36641@1.0.1","overlay_id":"5b210791fdeabe175c63d4c02ea51ea28b3a230f9dfb28d5706d6f2ad156d23a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-29600","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-36641@1.0.1","target":"policy:rule:RULE-29600","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-30881","node_id":"pkg:pypi/demo-30881@1.0.1","overlay_id":"5bc784e95ed042b4234b55e15e164e8b3ed4259232fdeedeeefc407f21779aff","overlay_kind":"policy.overlay.v1","rule_id":"RULE-23200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-30881@1.0.1","target":"policy:rule:RULE-23200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-12881","node_id":"pkg:pypi/demo-12881@1.0.1","overlay_id":"5eb75e3b7cec31993f42304ef1bf4222c428d58afdde56b7749b277c0a7c12d8","overlay_kind":"policy.overlay.v1","rule_id":"RULE-03200","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-12881@1.0.1","target":"policy:rule:RULE-03200","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-37000","node_id":"pkg:pypi/demo-37000@1.0.0","overlay_id":"60ec249e7d9aaeb9152051ea30f8d5950e1863cca3a573e95e98fa9a147973d0","overlay_kind":"policy.overlay.v1","rule_id":"RULE-30000","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-37000@1.0.0","target":"policy:rule:RULE-30000","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-39521","node_id":"pkg:pypi/demo-39521@1.0.1","overlay_id":"647091b2c6a7a639ebc3f6c662c62402e16a60adb0d45f654f5e82e86bd13891","overlay_kind":"policy.overlay.v1","rule_id":"RULE-32800","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-39521@1.0.1","target":"policy:rule:RULE-32800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-33400","node_id":"pkg:pypi/demo-33400@1.0.0","overlay_id":"6600cb30c992e0969a7a1b5b6af50be2d5f2bd081d0593c1ddb7271aea9c994e","overlay_kind":"policy.overlay.v1","rule_id":"RULE-26000","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-33400@1.0.0","target":"policy:rule:RULE-26000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-11080","node_id":"pkg:pypi/demo-11080@1.0.0","overlay_id":"67f7f34940ab048a9cb4f13003a8d8f6331495217c2c13e8e1b19f50eb9035de","overlay_kind":"policy.overlay.v1","rule_id":"RULE-01200","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-11080@1.0.0","target":"policy:rule:RULE-01200","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-38801","node_id":"pkg:pypi/demo-38801@1.0.1","overlay_id":"6ae30cc9c4ffc69ca8b310922b102d8b1d7c45766cf4d874da08211c70eb56e9","overlay_kind":"policy.overlay.v1","rule_id":"RULE-32000","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-38801@1.0.1","target":"policy:rule:RULE-32000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-39882","node_id":"pkg:pypi/demo-39882@1.0.2","overlay_id":"6cf07249525dbbcc9a47f05c5819c3af869556c028aa6d6e3380eea61dbf9141","overlay_kind":"policy.overlay.v1","rule_id":"RULE-33200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-39882@1.0.2","target":"policy:rule:RULE-33200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-31240","node_id":"pkg:pypi/demo-31240@1.0.0","overlay_id":"6f8f777acdfa66e0be08b1626dc647def53cff7cb1ceb6ebdf9b18caf394d13b","overlay_kind":"policy.overlay.v1","rule_id":"RULE-23600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-31240@1.0.0","target":"policy:rule:RULE-23600","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-29441","node_id":"pkg:pypi/demo-29441@1.0.1","overlay_id":"738f32825ad6beeedac2bc5dfb4df56c93b39950296c516f8497e69f6394e11c","overlay_kind":"policy.overlay.v1","rule_id":"RULE-21600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-29441@1.0.1","target":"policy:rule:RULE-21600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-19000","node_id":"pkg:pypi/demo-19000@1.0.0","overlay_id":"73bb0f8c0d3229fa269ecd196204423a9261e9f3f98996cc729ce2231a20cdb1","overlay_kind":"policy.overlay.v1","rule_id":"RULE-10000","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-19000@1.0.0","target":"policy:rule:RULE-10000","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-7481","node_id":"pkg:pypi/demo-7481@1.0.1","overlay_id":"747fc10ceb149b0d8140a1d4c0485fdf3643204c06b4fb218670c9e97b38e4f3","overlay_kind":"policy.overlay.v1","rule_id":"RULE-37200","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-7481@1.0.1","target":"policy:rule:RULE-37200","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-35561","node_id":"pkg:pypi/demo-35561@1.0.1","overlay_id":"76900fee410f614d7b11c55a395ee5260408989421740dfd78b6ac55850d2542","overlay_kind":"policy.overlay.v1","rule_id":"RULE-28400","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-35561@1.0.1","target":"policy:rule:RULE-28400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-23320","node_id":"pkg:pypi/demo-23320@1.0.0","overlay_id":"76b56b10c70cc1dcdd872bbd065cd44dbd7c3aa7032e82ce3ef0820978d69e27","overlay_kind":"policy.overlay.v1","rule_id":"RULE-14800","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-23320@1.0.0","target":"policy:rule:RULE-14800","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-18641","node_id":"pkg:pypi/demo-18641@1.0.1","overlay_id":"773e4bd52baff94e36068ce517d63ebf78f89210b3e5163dc8b4c61966c8a47a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-09600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-18641@1.0.1","target":"policy:rule:RULE-09600","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-25481","node_id":"pkg:pypi/demo-25481@1.0.1","overlay_id":"7a536bc0ad6b4095768935986291ea7d6ee88c8c0d5b170ae8bba2c0a61179b7","overlay_kind":"policy.overlay.v1","rule_id":"RULE-17200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-25481@1.0.1","target":"policy:rule:RULE-17200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-21160","node_id":"pkg:pypi/demo-21160@1.0.0","overlay_id":"7f47db20c7fab82d4aef26cdffc6cb83e4acf32e976d2c8fef225f3573a69101","overlay_kind":"policy.overlay.v1","rule_id":"RULE-12400","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-21160@1.0.0","target":"policy:rule:RULE-12400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-30160","node_id":"pkg:pypi/demo-30160@1.0.0","overlay_id":"8137c50cbfdb865b279e2789d69381a27bc293147be20795364451f43900d6a6","overlay_kind":"policy.overlay.v1","rule_id":"RULE-22400","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-30160@1.0.0","target":"policy:rule:RULE-22400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-27281","node_id":"pkg:pypi/demo-27281@1.0.1","overlay_id":"82bbe85a44c2ff19be60a38ee8aa087c28364dcef6918305fbede27bf6a5f5d0","overlay_kind":"policy.overlay.v1","rule_id":"RULE-19200","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-27281@1.0.1","target":"policy:rule:RULE-19200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-28721","node_id":"pkg:pypi/demo-28721@1.0.1","overlay_id":"870bc162cf683f85b025396c6a30b468d4a32943c75c6ea8c60d2fb825fa570d","overlay_kind":"policy.overlay.v1","rule_id":"RULE-20800","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-28721@1.0.1","target":"policy:rule:RULE-20800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-8921","node_id":"pkg:pypi/demo-8921@1.0.1","overlay_id":"898d6aef138e31652e6b36f174fc94e47b12ba42a14d60919898c1c4e7d2b3cc","overlay_kind":"policy.overlay.v1","rule_id":"RULE-38800","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-8921@1.0.1","target":"policy:rule:RULE-38800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-35200","node_id":"pkg:pypi/demo-35200@1.0.0","overlay_id":"9065259780ee9c21319907a5c6b1bde32878a5eb00396052c3e9fdb5f35ca245","overlay_kind":"policy.overlay.v1","rule_id":"RULE-28000","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-35200@1.0.0","target":"policy:rule:RULE-28000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-11440","node_id":"pkg:pypi/demo-11440@1.0.0","overlay_id":"92e12041d5145e58289ae813615f19bc48779db5535c5b1807fcf643cc20f9ac","overlay_kind":"policy.overlay.v1","rule_id":"RULE-01600","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-11440@1.0.0","target":"policy:rule:RULE-01600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-33761","node_id":"pkg:pypi/demo-33761@1.0.1","overlay_id":"9873f2329abea8f4222d21321a59c92e232edf27a561d63942beb880bf3d31b0","overlay_kind":"policy.overlay.v1","rule_id":"RULE-26400","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-33761@1.0.1","target":"policy:rule:RULE-26400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-26921","node_id":"pkg:pypi/demo-26921@1.0.1","overlay_id":"9cb926442a11156980b13ce305a20f390e4ad7af84bbdd94a8511b0436d660b7","overlay_kind":"policy.overlay.v1","rule_id":"RULE-18800","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-26921@1.0.1","target":"policy:rule:RULE-18800","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-25841","node_id":"pkg:pypi/demo-25841@1.0.1","overlay_id":"9d1408c934dff88449d4ada0afc48806fcefa452aae778e4a16f5d2b2d8c589e","overlay_kind":"policy.overlay.v1","rule_id":"RULE-17600","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-25841@1.0.1","target":"policy:rule:RULE-17600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-16481","node_id":"pkg:pypi/demo-16481@1.0.1","overlay_id":"9d4592d52a314b009a849d5dea86912c28f8373f235563ac2549d14f2942147e","overlay_kind":"policy.overlay.v1","rule_id":"RULE-07200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-16481@1.0.1","target":"policy:rule:RULE-07200","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-34481","node_id":"pkg:pypi/demo-34481@1.0.1","overlay_id":"9e8c59b06c30d2127ba50fc0307efdb531a63e80d229595a90327b94675d99d9","overlay_kind":"policy.overlay.v1","rule_id":"RULE-27200","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-34481@1.0.1","target":"policy:rule:RULE-27200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-8200","node_id":"pkg:pypi/demo-8200@1.0.0","overlay_id":"9f9e64f467caf1cbd0d367b79a0e473871eeda5873c17c572e34a1857f45bd39","overlay_kind":"policy.overlay.v1","rule_id":"RULE-38000","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-8200@1.0.0","target":"policy:rule:RULE-38000","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-13961","node_id":"pkg:pypi/demo-13961@1.0.1","overlay_id":"a6be168a9f820a1f1713835ed6a814556ce117cfd6896d738f12153d51660aa6","overlay_kind":"policy.overlay.v1","rule_id":"RULE-04400","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-13961@1.0.1","target":"policy:rule:RULE-04400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-36281","node_id":"pkg:pypi/demo-36281@1.0.1","overlay_id":"a705207f1f1265799bc5f4e4387cd6f3ed82635921d00093da234c2ca7e7be17","overlay_kind":"policy.overlay.v1","rule_id":"RULE-29200","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-36281@1.0.1","target":"policy:rule:RULE-29200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-19361","node_id":"pkg:pypi/demo-19361@1.0.1","overlay_id":"a740234b5fa022f52bce480e1d70250cdc5936eb6d42fb8736543ef46c7fac31","overlay_kind":"policy.overlay.v1","rule_id":"RULE-10400","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-19361@1.0.1","target":"policy:rule:RULE-10400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-37721","node_id":"pkg:pypi/demo-37721@1.0.1","overlay_id":"a7f55958a80bde9440ab5e8a4d6ca1210f0b8dfe165b0d8cf363abdda1b5b60b","overlay_kind":"policy.overlay.v1","rule_id":"RULE-30800","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-37721@1.0.1","target":"policy:rule:RULE-30800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-13240","node_id":"pkg:pypi/demo-13240@1.0.0","overlay_id":"ab483d159acde6ade084c4b9bc238479962b0aab11ca539c53df916dafe69f05","overlay_kind":"policy.overlay.v1","rule_id":"RULE-03600","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-13240@1.0.0","target":"policy:rule:RULE-03600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-14681","node_id":"pkg:pypi/demo-14681@1.0.1","overlay_id":"b17887773245f01828ebb6aa286b094a3403e382fb353117e2893d744028148a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-05200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-14681@1.0.1","target":"policy:rule:RULE-05200","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-15761","node_id":"pkg:pypi/demo-15761@1.0.1","overlay_id":"b30375cf0b5d69e4aacc88d81fbf76e5e2410a16fe5d7f59f9f3a4d98ca4a866","overlay_kind":"policy.overlay.v1","rule_id":"RULE-06400","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-15761@1.0.1","target":"policy:rule:RULE-06400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-12160","node_id":"pkg:pypi/demo-12160@1.0.0","overlay_id":"b566f79616c3560fdbad0e8418c2ce9b78776ef59e0c2070663be7cf6a7f4c0a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-02400","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-12160@1.0.0","target":"policy:rule:RULE-02400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-22240","node_id":"pkg:pypi/demo-22240@1.0.0","overlay_id":"b95cdc7fb15fd216c22616040ca4b4c676a64edab04c06c16ffafd1738713fbc","overlay_kind":"policy.overlay.v1","rule_id":"RULE-13600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-22240@1.0.0","target":"policy:rule:RULE-13600","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-10720","node_id":"pkg:pypi/demo-10720@1.0.0","overlay_id":"c06cd9cf8b8074d90b31eac5ad85e92421b4e362480e092ce7c5e7b62565d928","overlay_kind":"policy.overlay.v1","rule_id":"RULE-00800","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-10720@1.0.0","target":"policy:rule:RULE-00800","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-9281","node_id":"pkg:pypi/demo-9281@1.0.1","overlay_id":"c27f4d22caaa383d0a6875e5dbdf17412961c51c220b98b324009526fade3a76","overlay_kind":"policy.overlay.v1","rule_id":"RULE-39200","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-9281@1.0.1","target":"policy:rule:RULE-39200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-13600","node_id":"pkg:pypi/demo-13600@1.0.0","overlay_id":"c739d23b17155d1516d7d984ba747bf050f2d8d08541da3a4a1d7df26e26712a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-04000","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-13600@1.0.0","target":"policy:rule:RULE-04000","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-32681","node_id":"pkg:pypi/demo-32681@1.0.1","overlay_id":"c768334e27b299ae15f65ab67b3f55be479fb5b9c4b74e430f2a4aaadea30162","overlay_kind":"policy.overlay.v1","rule_id":"RULE-25200","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-32681@1.0.1","target":"policy:rule:RULE-25200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-5320","node_id":"pkg:pypi/demo-5320@1.0.0","overlay_id":"c9f5ed394a4df94e31421a6ec0026b3a5ed5109716505caf65ba61a80af0115a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-34800","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-5320@1.0.0","target":"policy:rule:RULE-34800","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-12520","node_id":"pkg:pypi/demo-12520@1.0.0","overlay_id":"d0fc77494141b806cfce5988733178603a4dfaa0aa0efd426721027a1a85fdcc","overlay_kind":"policy.overlay.v1","rule_id":"RULE-02800","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-12520@1.0.0","target":"policy:rule:RULE-02800","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-6400","node_id":"pkg:pypi/demo-6400@1.0.0","overlay_id":"d3b8197c027e178dbe45b91c61e361f8835fbb1cea208a78698e576631386969","overlay_kind":"policy.overlay.v1","rule_id":"RULE-36000","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-6400@1.0.0","target":"policy:rule:RULE-36000","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-10360","node_id":"pkg:pypi/demo-10360@1.0.0","overlay_id":"d59fdeb104183f21f5a99297dcc8e5eb86ae1aa5e4779d57ee731995ad8d7368","overlay_kind":"policy.overlay.v1","rule_id":"RULE-00400","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-10360@1.0.0","target":"policy:rule:RULE-00400","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-16120","node_id":"pkg:pypi/demo-16120@1.0.0","overlay_id":"da8e4aaa7d7647c75c4e193e5a75fab30324c53a4b9afee451db071faff90d8e","overlay_kind":"policy.overlay.v1","rule_id":"RULE-06800","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-16120@1.0.0","target":"policy:rule:RULE-06800","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-21520","node_id":"pkg:pypi/demo-21520@1.0.0","overlay_id":"ded0fc8ab38cac14c6b658aca50704e487e5ad833426da232b9955b6e14ed327","overlay_kind":"policy.overlay.v1","rule_id":"RULE-12800","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-21520@1.0.0","target":"policy:rule:RULE-12800","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-26561","node_id":"pkg:pypi/demo-26561@1.0.1","overlay_id":"e00ff0db63667e6c55f9b20579a51276c1f49811f9f7604210f0eb7ce038e855","overlay_kind":"policy.overlay.v1","rule_id":"RULE-18400","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-26561@1.0.1","target":"policy:rule:RULE-18400","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-11800","node_id":"pkg:pypi/demo-11800@1.0.0","overlay_id":"e22a9972cc9be86bb5cec77f2947fa6762901e9dba57e0373bf3a6dee2bb8c84","overlay_kind":"policy.overlay.v1","rule_id":"RULE-02000","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-11800@1.0.0","target":"policy:rule:RULE-02000","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-24040","node_id":"pkg:pypi/demo-24040@1.0.0","overlay_id":"e31ee0323e6950c3d3e5f3422bc5dbdc387abbfe48d89e232cef45f1439bd99d","overlay_kind":"policy.overlay.v1","rule_id":"RULE-15600","severity":"none","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-24040@1.0.0","target":"policy:rule:RULE-15600","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-22961","node_id":"pkg:pypi/demo-22961@1.0.1","overlay_id":"e453eb7d805d37065b70bef0a38a2b5e9dc1fc8b5bd6ca7018609217f16642fa","overlay_kind":"policy.overlay.v1","rule_id":"RULE-14400","severity":"high","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-22961@1.0.1","target":"policy:rule:RULE-14400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-24761","node_id":"pkg:pypi/demo-24761@1.0.1","overlay_id":"e63454641abcc1e5982c58ca88490801b5a2e30f881f04a1566342d710bff7e9","overlay_kind":"policy.overlay.v1","rule_id":"RULE-16400","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-24761@1.0.1","target":"policy:rule:RULE-16400","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-38081","node_id":"pkg:pypi/demo-38081@1.0.1","overlay_id":"e6e57c9f3f4bea1c960388da97328eb7615e30b5ea67ccc7800501fabf0e3912","overlay_kind":"policy.overlay.v1","rule_id":"RULE-31200","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-38081@1.0.1","target":"policy:rule:RULE-31200","tenant":"demo-tenant","verdict":"allow"}
{"explain":"demo policy decision for demo-25120","node_id":"pkg:pypi/demo-25120@1.0.0","overlay_id":"ea8985e9a94c5649b08ca043155ea85aff78b86648d173052ff0d4f4ee34d15a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-16800","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-25120@1.0.0","target":"policy:rule:RULE-16800","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-5681","node_id":"pkg:pypi/demo-5681@1.0.1","overlay_id":"eaca42bea0d33828f5c3445fa2a2b518d9925b3c42cff93675fbbcd4de04d339","overlay_kind":"policy.overlay.v1","rule_id":"RULE-35200","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-5681@1.0.1","target":"policy:rule:RULE-35200","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-7841","node_id":"pkg:pypi/demo-7841@1.0.1","overlay_id":"edae72b82895f4feea26750764fdb4ae16b938cbd15bf5d48cdffc3ab23b79d3","overlay_kind":"policy.overlay.v1","rule_id":"RULE-37600","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-7841@1.0.1","target":"policy:rule:RULE-37600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-33040","node_id":"pkg:pypi/demo-33040@1.0.0","overlay_id":"ef05ab5b8b24f89cf3b4cceeb93e585e29f5e780c99aeda79b60f51be5b0d302","overlay_kind":"policy.overlay.v1","rule_id":"RULE-25600","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-33040@1.0.0","target":"policy:rule:RULE-25600","tenant":"demo-tenant","verdict":"defer"}
{"explain":"demo policy decision for demo-16841","node_id":"pkg:pypi/demo-16841@1.0.1","overlay_id":"f74c9409e13ffb8fdbbe81eb017fd4fa8f6fda9bd1ba665f9ab0635972a28f4a","overlay_kind":"policy.overlay.v1","rule_id":"RULE-07600","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-16841@1.0.1","target":"policy:rule:RULE-07600","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-34120","node_id":"pkg:pypi/demo-34120@1.0.0","overlay_id":"f80abfd650a8a18de358b126c0bbe743e379870dcf7ce67bb265989bc45285e8","overlay_kind":"policy.overlay.v1","rule_id":"RULE-26800","severity":"medium","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-34120@1.0.0","target":"policy:rule:RULE-26800","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-31600","node_id":"pkg:pypi/demo-31600@1.0.0","overlay_id":"f81295294c128612ccf8b6338fa4a0f908b440078cb91707ee0d9e4c4a71d745","overlay_kind":"policy.overlay.v1","rule_id":"RULE-24000","severity":"low","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-31600@1.0.0","target":"policy:rule:RULE-24000","tenant":"demo-tenant","verdict":"deny"}
{"explain":"demo policy decision for demo-18281","node_id":"pkg:pypi/demo-18281@1.0.1","overlay_id":"f8511677dc634ea2bd5fe93d2c739ee177340773dd57be164563e4217fe8938f","overlay_kind":"policy.overlay.v1","rule_id":"RULE-09200","severity":"critical","snapshot":"graph-40k-policy-overlay-20251122","source":"pkg:pypi/demo-18281@1.0.1","target":"policy:rule:RULE-09200","tenant":"demo-tenant","verdict":"allow"}

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Verify graph-40k fixture hashes and counts against manifest.json.
"""
from __future__ import annotations
import hashlib
import json
import sys
from pathlib import Path
from typing import Tuple
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def count_lines(path: Path) -> int:
with path.open("r", encoding="utf-8") as f:
return sum(1 for _ in f if _.strip())
def verify(manifest_path: Path) -> Tuple[bool, str]:
manifest = json.loads(manifest_path.read_text())
base = manifest_path.parent
nodes_path = base / "nodes.ndjson"
edges_path = base / "edges.ndjson"
overlay_path = base / manifest["overlay"]["path"]
checks = [
("nodes hash", sha256(nodes_path) == manifest["hashes"]["nodes_ndjson_sha256"]),
("edges hash", sha256(edges_path) == manifest["hashes"]["edges_ndjson_sha256"]),
("overlay hash", sha256(overlay_path) == manifest["hashes"]["overlay_ndjson_sha256"]),
("nodes count", count_lines(nodes_path) == manifest["counts"]["nodes"]),
("edges count", count_lines(edges_path) == manifest["counts"]["edges"]),
("overlay count", count_lines(overlay_path) == manifest["counts"]["overlays"]["policy.overlay.v1"]),
]
failed = [name for name, ok in checks if not ok]
if failed:
return False, f"Failed checks: {', '.join(failed)}"
return True, "All hashes and counts match manifest."
def main() -> int:
manifest_path = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path(__file__).resolve().parent / "manifest.json"
ok, message = verify(manifest_path)
print(message)
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
Build vulnerability explorer fixtures (JSON + CSV) from the canonical graph-40k fixture.
Generates deterministic outputs in `samples/graph/graph-40k/explorer/`:
- vuln-explorer.json
- vuln-explorer.csv
- manifest.json (hashes + counts)
"""
from __future__ import annotations
import csv
import hashlib
import json
from pathlib import Path
from typing import List
ROOT = Path(__file__).resolve().parent.parent
GRAPH_ROOT = ROOT / "graph-40k"
OVERLAY_PATH = GRAPH_ROOT / "overlay.ndjson"
OUT_DIR = GRAPH_ROOT / "explorer"
# Fixed advisory set to keep fixtures stable and small.
ADVISORIES = [
("CVE-2024-0001", "critical"),
("CVE-2024-0002", "high"),
("CVE-2023-9999", "medium"),
("CVE-2025-1234", "low"),
("CVE-2022-4242", "none"),
]
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def load_overlays() -> List[dict]:
overlays: List[dict] = []
with OVERLAY_PATH.open("r", encoding="utf-8") as f:
for line in f:
if line.strip():
overlays.append(json.loads(line))
overlays.sort(key=lambda o: o["overlay_id"])
return overlays
def build_records() -> List[dict]:
overlays = load_overlays()[: len(ADVISORIES)]
records: List[dict] = []
for idx, overlay in enumerate(overlays):
advisory_id, advisory_sev = ADVISORIES[idx]
reachable = idx % 2 == 0 # alternate reachable/unreachable for UI coverage
status = "affected" if reachable else "not_affected"
conflict = "policy_deny_vs_scanner_affected" if overlay["verdict"] == "deny" and reachable else None
record = {
"component": overlay["node_id"],
"advisory": advisory_id,
"advisory_severity": advisory_sev,
"reachability": "reachable" if reachable else "unreachable",
"status": status,
"policy_overlay_id": overlay["overlay_id"],
"policy_verdict": overlay["verdict"],
"policy_severity": overlay["severity"],
"policy_rule_id": overlay["rule_id"],
"evidence": [
"sbom:mock-sbom-v1",
f"overlay:{overlay['overlay_id']}",
],
"conflict": conflict or "",
"snapshot": overlay["snapshot"],
"tenant": overlay["tenant"],
}
records.append(record)
return records
def write_json(records: List[dict], path: Path) -> None:
path.write_text(json.dumps(records, indent=2, sort_keys=True))
def write_csv(records: List[dict], path: Path) -> None:
fieldnames = [
"component",
"advisory",
"advisory_severity",
"reachability",
"status",
"policy_overlay_id",
"policy_verdict",
"policy_severity",
"policy_rule_id",
"evidence",
"conflict",
"snapshot",
"tenant",
]
with path.open("w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for r in records:
row = r.copy()
row["evidence"] = ";".join(r["evidence"])
writer.writerow(row)
def write_manifest(json_path: Path, csv_path: Path, count: int, manifest_path: Path) -> None:
manifest = {
"fixture": "graph-40k",
"advisories": [a for a, _ in ADVISORIES],
"count": count,
"hashes": {
"vuln-explorer.json": sha256(json_path),
"vuln-explorer.csv": sha256(csv_path),
},
}
manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True))
def main() -> int:
OUT_DIR.mkdir(parents=True, exist_ok=True)
records = build_records()
json_path = OUT_DIR / "vuln-explorer.json"
csv_path = OUT_DIR / "vuln-explorer.csv"
manifest_path = OUT_DIR / "manifest.json"
write_json(records, json_path)
write_csv(records, csv_path)
write_manifest(json_path, csv_path, len(records), manifest_path)
print(f"Wrote {len(records)} records to {OUT_DIR}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
Generate canonical SAMPLES-GRAPH-24-003 fixture.
Outputs:
- nodes.ndjson, edges.ndjson, overlay.ndjson
- manifest.json with counts and SHA-256 hashes
Deterministic and offline-only: fixed seed, fixed timestamps, sorted output.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import random
from pathlib import Path
from typing import Iterable, List, Tuple
TENANT = "demo-tenant"
SNAPSHOT_ID = "graph-40k-policy-overlay-20251122"
GENERATED_AT = "2025-11-22T00:00:00Z"
DEFAULT_NODE_COUNT = 40_000
SEED = 424_242
MAX_FANOUT = 4
OVERLAY_INTERVAL = 400 # one overlay per 400 nodes -> ~100 overlays for 40k nodes
OVERLAY_VERDICTS = ("allow", "deny", "defer")
OVERLAY_SEVERITIES = ("none", "low", "medium", "high", "critical")
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def write_ndjson(path: Path, rows: Iterable[dict]) -> None:
with path.open("w", encoding="utf-8", newline="\n") as f:
for row in rows:
f.write(json.dumps(row, sort_keys=True, separators=(",", ":")))
f.write("\n")
def build_nodes(count: int, rng: random.Random) -> List[dict]:
nodes: List[dict] = []
for i in range(count):
version_patch = i % 5
purl = f"pkg:pypi/demo-{i}@1.0.{version_patch}"
node = {
"id": purl,
"kind": "component",
"name": f"demo-{i}",
"purl": purl,
"tenant": TENANT,
"version": f"1.0.{version_patch}",
"snapshot": SNAPSHOT_ID,
}
nodes.append(node)
nodes.sort(key=lambda n: n["id"])
return nodes
def build_edges(nodes: List[dict], rng: random.Random) -> List[dict]:
edges: List[dict] = []
for idx, node in enumerate(nodes):
if idx == 0:
continue
fanout = rng.randint(1, min(MAX_FANOUT, idx))
targets_idx = rng.sample(range(idx), fanout)
for tgt_idx in targets_idx:
edges.append(
{
"source": node["id"],
"target": nodes[tgt_idx]["id"],
"kind": "DEPENDS_ON",
"provenance": "mock-sbom-v1",
"snapshot": SNAPSHOT_ID,
"tenant": TENANT,
}
)
edges.sort(key=lambda e: (e["source"], e["target"]))
return edges
def build_overlays(nodes: List[dict], rng: random.Random) -> List[dict]:
overlays: List[dict] = []
for idx, node in enumerate(nodes):
if idx % OVERLAY_INTERVAL != 0:
continue
verdict = rng.choice(OVERLAY_VERDICTS)
severity = rng.choice(OVERLAY_SEVERITIES)
rule_id = f"RULE-{idx:05d}"
overlay_id = hashlib.sha256(f"{TENANT}|{node['id']}|policy.overlay.v1".encode()).hexdigest()
overlays.append(
{
"overlay_id": overlay_id,
"overlay_kind": "policy.overlay.v1",
"tenant": TENANT,
"snapshot": SNAPSHOT_ID,
"node_id": node["id"],
"verdict": verdict,
"rule_id": rule_id,
"severity": severity,
"explain": f"demo policy decision for {node['name']}",
# bridge to bench overlay support (optional edge application)
"source": node["id"],
"target": f"policy:rule:{rule_id}",
}
)
overlays.sort(key=lambda o: o["overlay_id"])
return overlays
def generate(out_dir: Path, node_count: int, seed: int) -> Tuple[Path, Path, Path, Path]:
out_dir.mkdir(parents=True, exist_ok=True)
rng = random.Random(seed)
nodes = build_nodes(node_count, rng)
edges = build_edges(nodes, rng)
overlays = build_overlays(nodes, rng)
nodes_path = out_dir / "nodes.ndjson"
edges_path = out_dir / "edges.ndjson"
overlay_path = out_dir / "overlay.ndjson"
write_ndjson(nodes_path, nodes)
write_ndjson(edges_path, edges)
write_ndjson(overlay_path, overlays)
manifest = {
"snapshot_id": SNAPSHOT_ID,
"tenant": TENANT,
"generated_at": GENERATED_AT,
"seed": seed,
"counts": {
"nodes": len(nodes),
"edges": len(edges),
"overlays": {"policy.overlay.v1": len(overlays)},
},
"hashes": {
"nodes_ndjson_sha256": sha256(nodes_path),
"edges_ndjson_sha256": sha256(edges_path),
"overlay_ndjson_sha256": sha256(overlay_path),
},
"overlay": {
"path": "overlay.ndjson",
"kind": "policy.overlay.v1",
"id_scheme": "sha256(tenant|nodeId|overlayKind)",
},
"inputs": {"sbom_source": "mock-sbom-v1"},
}
manifest_path = out_dir / "manifest.json"
manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True))
return nodes_path, edges_path, overlay_path, manifest_path
def main() -> int:
parser = argparse.ArgumentParser(description="Generate canonical graph fixture (SAMPLES-GRAPH-24-003).")
parser.add_argument("--out-dir", default="samples/graph/graph-40k", help="Output directory for fixture files")
parser.add_argument("--nodes", type=int, default=DEFAULT_NODE_COUNT, help="Number of nodes to generate")
parser.add_argument("--seed", type=int, default=SEED, help="Seed for deterministic generation")
args = parser.parse_args()
out_dir = Path(args.out_dir).resolve()
nodes_path, edges_path, overlay_path, manifest_path = generate(out_dir, args.nodes, args.seed)
print("Generated fixture:")
print(f" nodes: {nodes_path}")
print(f" edges: {edges_path}")
print(f" overlay: {overlay_path}")
print(f" manifest:{manifest_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())