This commit is contained in:
StellaOps Bot
2025-11-30 21:01:00 +02:00
parent 25254e3831
commit 808ab87b21
54 changed files with 1163 additions and 8 deletions

View File

@@ -0,0 +1,15 @@
{
"files": {
"src/app.js": {
"lines_covered": [
5,
6,
7,
13,
18,
19
],
"lines_total": 40
}
}
}

View File

@@ -0,0 +1,10 @@
{
"entry": "POST /api/admin/exec",
"path": [
"app.js::createServer",
"handler",
"eval(code)"
],
"sink": "ExpressEval::exec",
"notes": "Admin exec reached"
}

View File

@@ -0,0 +1,16 @@
{
"files": {
"src/app.js": {
"lines_covered": [
5,
6,
7,
12,
13,
14,
15
],
"lines_total": 50
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /api/admin/exec",
"path": [
"app.js::createServer",
"guard: ALLOW_EXEC!=true"
],
"sink": "ExpressGuarded::exec",
"notes": "Guard blocked sink"
}

View File

@@ -0,0 +1,15 @@
{
"files": {
"src/app.js": {
"lines_covered": [
5,
6,
7,
13,
18,
20
],
"lines_total": 45
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /api/render",
"path": [
"app.js::createServer",
"render template"
],
"sink": "FastifyTemplate::render",
"notes": "Template rendered with user input"
}

View File

@@ -0,0 +1,15 @@
{
"files": {
"src/app.js": {
"lines_covered": [
5,
6,
7,
9,
10,
11
],
"lines_total": 32
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /api/exec",
"path": [
"app.js:handleRequest",
"guard: FEATURE_ENABLE != 1"
],
"sink": "GuardedEval::handleRequest",
"notes": "Guard prevented sink execution"
}

View File

@@ -0,0 +1,14 @@
{
"files": {
"src/app.js": {
"lines_covered": [
5,
6,
7,
12,
15
],
"lines_total": 30
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /api/exec",
"path": [
"app.js:handleRequest",
"eval(code)"
],
"sink": "UnsafeEval::handleRequest",
"notes": "Test-driven dynamic trace"
}

View File

@@ -0,0 +1,16 @@
{
"files": {
"src/app.py": {
"lines_covered": [
3,
4,
5,
7,
8,
9,
10
],
"lines_total": 38
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /render",
"path": [
"app.py::handle_request",
"render"
],
"sink": "DjangoSSTI::render",
"notes": "Template rendered (autoescape off)"
}

View File

@@ -0,0 +1,15 @@
{
"files": {
"src/app.py": {
"lines_covered": [
3,
4,
5,
8,
9,
11
],
"lines_total": 40
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /exec",
"path": [
"app.py::handle_request",
"guard: ALLOW_EXEC!=true"
],
"sink": "FastApiGuarded::handle_request",
"notes": "Guard blocked eval"
}

View File

@@ -0,0 +1,16 @@
{
"files": {
"src/app.py": {
"lines_covered": [
4,
5,
6,
8,
9,
10,
11
],
"lines_total": 40
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /render",
"path": [
"app.py::handle_request",
"render"
],
"sink": "FlaskTemplate::render",
"notes": "Template rendered"
}

View File

@@ -0,0 +1,15 @@
{
"files": {
"src/app.py": {
"lines_covered": [
3,
4,
5,
8,
9,
11
],
"lines_total": 34
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /api/exec",
"path": [
"app.py::handle_request",
"guard: FEATURE_ENABLE != 1"
],
"sink": "PyGuardedExec::handle_request",
"notes": "Guard blocked eval"
}

View File

@@ -0,0 +1,14 @@
{
"files": {
"src/app.py": {
"lines_covered": [
3,
4,
5,
8,
10
],
"lines_total": 30
}
}
}

View File

@@ -0,0 +1,9 @@
{
"entry": "POST /api/exec",
"path": [
"app.py::handle_request",
"eval(code)"
],
"sink": "PyUnsafeExec::handle_request",
"notes": "Eval reached"
}

View File

@@ -21,8 +21,9 @@
1. **Value in context** [Overview](overview.md) compresses the “Why” + “What” stories and shows how StellaOps stands apart. 1. **Value in context** [Overview](overview.md) compresses the “Why” + “What” stories and shows how StellaOps stands apart.
2. **Try it fast** [Quickstart](quickstart.md) walks through fetching the signed bundles, configuring `.env`, and verifying the first scan. 2. **Try it fast** [Quickstart](quickstart.md) walks through fetching the signed bundles, configuring `.env`, and verifying the first scan.
3. **Feature confidence** [Key Features](key-features.md) gives five capability cards covering Delta SBOM, VEXfirst policy, Sovereign crypto, Deterministic replay, and Transparent quotas. 3. **Feature confidence** [Key Features](key-features.md) gives five capability cards covering Delta SBOM, VEX-first policy, Sovereign crypto, Deterministic replay, and Transparent quotas.
4. **Up-next checkpoints** [Evaluation checklist](evaluate/checklist.md) helps teams plan Day0 to Day30 adoption milestones. 4. **Up-next checkpoints** [Evaluation checklist](evaluate/checklist.md) helps teams plan Day-0 to Day-30 adoption milestones.
5. **Be dev-ready** [Developer Quickstart](onboarding/dev-quickstart.md) (29-Nov-2025 advisory) walks through the core repos, determinism tests, attestations, and starter issues for a mid-level .NET engineer.
## Key capabilities that define StellaOps ## Key capabilities that define StellaOps

View File

@@ -24,7 +24,7 @@
| 1 | WEB-AIAI-31-001 | BLOCKED (2025-11-22) | Gateway policy/contract for `/advisory/ai/*` not present in Web workspace; need backend gateway service location + policy spec to proceed. | BE-Base Platform Guild | Route advisory AI endpoints through gateway with guardrails. | | 1 | WEB-AIAI-31-001 | BLOCKED (2025-11-22) | Gateway policy/contract for `/advisory/ai/*` not present in Web workspace; need backend gateway service location + policy spec to proceed. | BE-Base Platform Guild | Route advisory AI endpoints through gateway with guardrails. |
| 2 | WEB-AIAI-31-002 | BLOCKED (2025-11-22) | Blocked by WEB-AIAI-31-001; batching/streaming cannot start until gateway contract exists. | BE-Base Platform Guild | Streaming responses for CLI automation with job orchestration. | | 2 | WEB-AIAI-31-002 | BLOCKED (2025-11-22) | Blocked by WEB-AIAI-31-001; batching/streaming cannot start until gateway contract exists. | BE-Base Platform Guild | Streaming responses for CLI automation with job orchestration. |
| 3 | WEB-AIAI-31-003 | BLOCKED (2025-11-22) | Blocked by WEB-AIAI-31-002; telemetry targets depend on routing/batching contract. | BE-Base Platform Guild; Observability Guild | Telemetry + audit for advisory AI, guardrail block visibility. | | 3 | WEB-AIAI-31-003 | BLOCKED (2025-11-22) | Blocked by WEB-AIAI-31-002; telemetry targets depend on routing/batching contract. | BE-Base Platform Guild; Observability Guild | Telemetry + audit for advisory AI, guardrail block visibility. |
| 4 | WEB-AOC-19-002 | TODO | Depends on WEB-AOC-19-001; align DSSE/CMS helper APIs. | BE-Base Platform Guild | Ship `ProvenanceBuilder`, checksum utilities, signature verification helper with tests. | | 4 | WEB-AOC-19-002 | DONE (2025-11-30) | Depends on WEB-AOC-19-001; align DSSE/CMS helper APIs. | BE-Base Platform Guild | Ship `ProvenanceBuilder`, checksum utilities, signature verification helper with tests. |
| 5 | WEB-AOC-19-003 | TODO | Depends on WEB-AOC-19-002; confirm Roslyn analyzer rules. | QA Guild; BE-Base Platform Guild | Analyzer to prevent forbidden key writes; shared guard-validation fixtures. | | 5 | WEB-AOC-19-003 | TODO | Depends on WEB-AOC-19-002; confirm Roslyn analyzer rules. | QA Guild; BE-Base Platform Guild | Analyzer to prevent forbidden key writes; shared guard-validation fixtures. |
| 6 | WEB-CONSOLE-23-001 | DONE (2025-11-28) | `/console/dashboard` and `/console/filters` endpoints implemented with tenant-scoped aggregates. | BE-Base Platform Guild; Product Analytics Guild | Tenant-scoped aggregates for findings, VEX overrides, advisory deltas, run health, policy change log. | | 6 | WEB-CONSOLE-23-001 | DONE (2025-11-28) | `/console/dashboard` and `/console/filters` endpoints implemented with tenant-scoped aggregates. | BE-Base Platform Guild; Product Analytics Guild | Tenant-scoped aggregates for findings, VEX overrides, advisory deltas, run health, policy change log. |
| 7 | CONSOLE-VULN-29-001 | BLOCKED (2025-11-19) | Blocked on WEB-CONSOLE-23-001 contract and Concelier graph schema freeze. | Console Guild; BE-Base Platform Guild | `/console/vuln/*` workspace endpoints with filters/reachability badges and DTOs once schemas stabilize. | | 7 | CONSOLE-VULN-29-001 | BLOCKED (2025-11-19) | Blocked on WEB-CONSOLE-23-001 contract and Concelier graph schema freeze. | Console Guild; BE-Base Platform Guild | `/console/vuln/*` workspace endpoints with filters/reachability badges and DTOs once schemas stabilize. |
@@ -80,3 +80,4 @@
| 2025-11-22 | Added completion dates in `tasks-all` for WEB-CONTAINERS-44/45/46 and aligned BLOCKED dates for VULN-29-001/VEX-30-001. | Planning | | 2025-11-22 | Added completion dates in `tasks-all` for WEB-CONTAINERS-44/45/46 and aligned BLOCKED dates for VULN-29-001/VEX-30-001. | Planning |
| 2025-11-22 | Harmonized all `CONTAINERS-44/45/46` rows in `tasks-all` to DONE with dates to match sprint status. | Planning | | 2025-11-22 | Harmonized all `CONTAINERS-44/45/46` rows in `tasks-all` to DONE with dates to match sprint status. | Planning |
| 2025-11-28 | Completed WEB-CONSOLE-23-001: Implemented `/console/dashboard` and `/console/filters` endpoints in Authority module. Dashboard returns tenant-scoped aggregates (findings summary, VEX overrides, advisory deltas, run health, policy change log) with 30-day trend data. Filters endpoint returns deterministic filter categories with counts and cache-validation hash. Added 8 unit tests for dashboard/filters endpoints. Implementation in `src/Authority/StellaOps.Authority/StellaOps.Authority/Console/`. | Policy Guild | | 2025-11-28 | Completed WEB-CONSOLE-23-001: Implemented `/console/dashboard` and `/console/filters` endpoints in Authority module. Dashboard returns tenant-scoped aggregates (findings summary, VEX overrides, advisory deltas, run health, policy change log) with 30-day trend data. Filters endpoint returns deterministic filter categories with counts and cache-validation hash. Added 8 unit tests for dashboard/filters endpoints. Implementation in `src/Authority/StellaOps.Authority/StellaOps.Authority/Console/`. | Policy Guild |
| 2025-11-30 | Completed WEB-AOC-19-002: added deterministic provenance builder, checksum utilities, and DSSE/CMS signature verification helpers with unit tests under `src/Web/StellaOps.Web/src/app/core/aoc`. Added Web TASKS board and marked task DONE. | BE-Base Platform Guild |

View File

@@ -32,7 +32,7 @@
| 4 | BENCH-CASES-PY-513-004 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild · Python Track (`bench/reachability-benchmark/cases/py`) | Create 5-8 Python cases: Flask, Django, FastAPI. Include requirements.txt pinned, pytest oracles, coverage.py output. Delivered 5 cases: unsafe-exec (reachable), guarded-exec (unreachable), flask-template (reachable), fastapi-guarded (unreachable), django-ssti (reachable). | | 4 | BENCH-CASES-PY-513-004 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild · Python Track (`bench/reachability-benchmark/cases/py`) | Create 5-8 Python cases: Flask, Django, FastAPI. Include requirements.txt pinned, pytest oracles, coverage.py output. Delivered 5 cases: unsafe-exec (reachable), guarded-exec (unreachable), flask-template (reachable), fastapi-guarded (unreachable), django-ssti (reachable). |
| 5 | BENCH-CASES-JAVA-513-005 | BLOCKED (2025-11-30) | Depends on 513-002. | Bench Guild · Java Track (`bench/reachability-benchmark/cases/java`) | Create 5-8 Java cases: Spring Boot, Micronaut. Include pom.xml locked, JUnit oracles, JaCoCo coverage. Progress: 2/5 seeded (`spring-deserialize` reachable, `spring-guarded` unreachable); build/test blocked by missing JDK (`javac` not available in runner). | | 5 | BENCH-CASES-JAVA-513-005 | BLOCKED (2025-11-30) | Depends on 513-002. | Bench Guild · Java Track (`bench/reachability-benchmark/cases/java`) | Create 5-8 Java cases: Spring Boot, Micronaut. Include pom.xml locked, JUnit oracles, JaCoCo coverage. Progress: 2/5 seeded (`spring-deserialize` reachable, `spring-guarded` unreachable); build/test blocked by missing JDK (`javac` not available in runner). |
| 6 | BENCH-CASES-C-513-006 | TODO | Depends on 513-002. | Bench Guild · Native Track (`bench/reachability-benchmark/cases/c`) | Create 3-5 C/ELF cases: small HTTP servers, crypto utilities. Include Makefile, gcov/llvm-cov coverage, deterministic builds (SOURCE_DATE_EPOCH). | | 6 | BENCH-CASES-C-513-006 | TODO | Depends on 513-002. | Bench Guild · Native Track (`bench/reachability-benchmark/cases/c`) | Create 3-5 C/ELF cases: small HTTP servers, crypto utilities. Include Makefile, gcov/llvm-cov coverage, deterministic builds (SOURCE_DATE_EPOCH). |
| 7 | BENCH-BUILD-513-007 | DOING | Depends on 513-003 through 513-006. | Bench Guild · DevOps Guild | Implement `build_all.py` and `validate_builds.py`: deterministic Docker builds, hash verification, SBOM generation (syft), attestation stubs. Progress: added scripts (hash check, deterministic ordering); SBOM/attestation stubs pending. | | 7 | BENCH-BUILD-513-007 | DOING | Depends on 513-003 through 513-006. | Bench Guild · DevOps Guild | Implement `build_all.py` and `validate_builds.py`: deterministic Docker builds, hash verification, SBOM generation (syft), attestation stubs. Progress: added scripts (hash check, deterministic ordering) + README; SBOM/attestation stubs pending. |
| 8 | BENCH-SCORER-513-008 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild (`bench/reachability-benchmark/tools/scorer`) | Implement `rb-score` CLI: load cases/truth, validate submissions, compute precision/recall/F1, explainability score (0-3), runtime stats, determinism rate. | | 8 | BENCH-SCORER-513-008 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild (`bench/reachability-benchmark/tools/scorer`) | Implement `rb-score` CLI: load cases/truth, validate submissions, compute precision/recall/F1, explainability score (0-3), runtime stats, determinism rate. |
| 9 | BENCH-EXPLAIN-513-009 | DONE (2025-11-30) | Depends on 513-008. | Bench Guild | Implement explainability scoring rules: 0=no context, 1=path with ≥2 nodes, 2=entry+≥3 nodes, 3=guards/constraints included. Unit tests for each level. | | 9 | BENCH-EXPLAIN-513-009 | DONE (2025-11-30) | Depends on 513-008. | Bench Guild | Implement explainability scoring rules: 0=no context, 1=path with ≥2 nodes, 2=entry+≥3 nodes, 3=guards/constraints included. Unit tests for each level. |
| 10 | BENCH-BASELINE-SEMGREP-513-010 | TODO | Depends on 513-008 and cases. | Bench Guild | Semgrep baseline runner: `baselines/semgrep/run_case.sh`, rule config, output normalization to submission format. | | 10 | BENCH-BASELINE-SEMGREP-513-010 | TODO | Depends on 513-008 and cases. | Bench Guild | Semgrep baseline runner: `baselines/semgrep/run_case.sh`, rule config, output normalization to submission format. |
@@ -98,3 +98,4 @@
| 2025-11-30 | BLOCKED BENCH-CASES-JAVA-513-005: `javac`/JDK not available in runner; Java builds/tests cannot execute. Need JDK (>=17) in CI/runner before unblocking. | Implementer | | 2025-11-30 | BLOCKED BENCH-CASES-JAVA-513-005: `javac`/JDK not available in runner; Java builds/tests cannot execute. Need JDK (>=17) in CI/runner before unblocking. | Implementer |
| 2025-11-30 | BENCH-EXPLAIN-513-009 DONE: added explainability tier tests (03) to scorer; tiers already implemented (guards→3, entry+path>=3→2, path>=2→1, else 0). | Implementer | | 2025-11-30 | BENCH-EXPLAIN-513-009 DONE: added explainability tier tests (03) to scorer; tiers already implemented (guards→3, entry+path>=3→2, path>=2→1, else 0). | Implementer |
| 2025-11-30 | BENCH-BUILD-513-007 DOING: added `tools/build/build_all.py` and `tools/build/validate_builds.py` for deterministic builds and hash checks; SBOM/attestation stubs still pending. | Implementer | | 2025-11-30 | BENCH-BUILD-513-007 DOING: added `tools/build/build_all.py` and `tools/build/validate_builds.py` for deterministic builds and hash checks; SBOM/attestation stubs still pending. | Implementer |
| 2025-11-30 | BENCH-BUILD-513-007: build_all/validate_builds run; all JS/PY cases deterministic, Java cases fail due to missing `javac` (same blocker as task 5). | Implementer |

View File

@@ -18,12 +18,34 @@
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| 200.A Docs Tasks.md ladder (Sprint 301 onwards) | BLOCKED (2025-11-19) | Docs Guild · Ops Guild | Attestor 100.A; Advisory AI 110.A; AirGap 120.A; Scanner 130.A; Graph 140.A; Orchestrator 150.A; EvidenceLocker 160.A; Notifier 170.A; CLI 180.A; Ops Deployment 190.A | Awaiting upstream artefacts (SBOM/CLI/Policy/AirGap determinism) before Md.I template rollout can continue. | | 200.A Docs Tasks.md ladder (Sprint 301 onwards) | BLOCKED (2025-11-19) | Docs Guild · Ops Guild | Attestor 100.A; Advisory AI 110.A; AirGap 120.A; Scanner 130.A; Graph 140.A; Orchestrator 150.A; EvidenceLocker 160.A; Notifier 170.A; CLI 180.A; Ops Deployment 190.A | Awaiting upstream artefacts (SBOM/CLI/Policy/AirGap determinism) before Md.I template rollout can continue. |
| 200.B Module dossiers (Sprints 312335) | TODO | Docs Guild · Module Guild owners | Docs Tasks Md ladder to at least Md.II; Ops deployment evidence | Stays queued until Docs Tasks Md ladder provides updated process + assets. | | 200.B Module dossiers (Sprints 312335) | TODO | Docs Guild · Module Guild owners | Docs Tasks Md ladder to at least Md.II; Ops deployment evidence | Stays queued until Docs Tasks Md ladder provides updated process + assets. |
| Developer quickstart advisory sync | TODO | Docs Guild | 29-Nov-2025 advisory + onboarding doc draft | Publish the onboarding quickstart advisory + `docs/onboarding/dev-quickstart.md`, update `docs/README.md`, `modules/platform/architecture-overview.md`, and `ADVISORY_INDEX.md`, and confirm sprint/AGENTS references per the advisory workflow. |
| Acceptance tests guardrails sync | TODO | Docs Guild | 29-Nov-2025 advisory + checklist draft | Publish the Acceptance Tests Pack advisory, cross-link to sprint/guardrail docs, and capture sprint board checklist for CI/DB/rew definitions. |
| CVSS v4.0 momentum sync | TODO | Docs Guild | 29-Nov-2025 advisory + briefing draft | Publish the CVSS v4.0 momentum briefing, highlight adoption signals, and link to sprint decisions for SPRINT_0190.* and docs coverage. |
| SBOM→VEX proof blueprint sync | TODO | Docs Guild | 29-Nov-2025 advisory + blueprint draft | Publish the SBOM→VEX blueprint, link to platform/blueprint docs, and capture diagram/stub updates for DSSE/Rekor/VEX. |
| SCA failure catalogue sync | TODO | Docs Guild | 29-Nov-2025 advisory + catalogue draft | Publish the SCA failure catalogue, reference the concrete regressions, and tie the test-vector guidance back into sprint risk logs. |
| Implementor guidelines sync | TODO | Docs Guild | 30-Nov-2025 advisory + checklist draft | Publish the Implementor Guidelines advisory, note the checklist extraction, and mention the doc in sprint/AGENTS references. |
| Rekor receipt checklist sync | TODO | Docs Guild | 30-Nov-2025 advisory + checklist draft | Publish the Rekor Receipt Checklist, update module docs (Authority/Sbomer/Vexer) with ownership map, highlight offline metadata requirements. |
| Unknowns decay/triage sync | TODO | Docs Guild | 30-Nov-2025 advisory + heuristic draft | Publish the Unknowns Decay & Triage brief, link to UnknownsRegistry docs, and capture UI artifacts for cards + queue exports. |
| Ecosystem reality test cases sync | TODO | Docs Guild | 30-Nov-2025 advisory + test spec draft | Publish the Ecosystem Reality Test Cases advisory, link each incident to an acceptance test, and note exported artifacts/commands. |
| Standup sprint kickstarters sync | TODO | Docs Guild | 30-Nov-2025 advisory + task plan draft | Publish the Standup Sprint Kickstarters advisory, surface ticket names, and tie the tasks into MSC sprint logs. |
| Evidence + suppression pattern sync | TODO | Docs Guild | 30-Nov-2025 advisory + comparison draft | Publish the Comparative Evidence Patterns advisory, highlight the UX/data-model takeaways, and reference doc links per tool. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-11-13 | Sprint 300 switched to topic-oriented template; Docs Tasks Md ladder marked DOING to reflect ongoing restructuring work. | Docs Guild | | 2025-11-13 | Sprint 300 switched to topic-oriented template; Docs Tasks Md ladder marked DOING to reflect ongoing restructuring work. | Docs Guild |
| 2025-11-19 | Marked Docs Tasks Md ladder BLOCKED pending upstream artefacts for Md.I dossier rollouts. | Implementer | | 2025-11-19 | Marked Docs Tasks Md ladder BLOCKED pending upstream artefacts for Md.I dossier rollouts. | Implementer |
| 2025-11-30 | Added the 29-Nov-2025 Developer Quickstart advisory, `docs/onboarding/dev-quickstart.md`, and cross-links (README/platform/ADVISORY_INDEX); created this advisory sync task row. | Docs Guild |
| 2025-11-30 | Added the 29-Nov-2025 Acceptance Tests Pack advisory and checklist; noted new task row for guardrail sprint artifacts. | Docs Guild |
| 2025-11-30 | Added the 29-Nov-2025 CVSS v4.0 Momentum advisory and indexed the adoption briefing; noted sprint sync row for CVSS momentum context. | Docs Guild |
| 2025-11-30 | Added the 29-Nov-2025 SCA Failure Catalogue advisory and indexed the concrete test vectors; noted sprint sync row for failure catalog references. | Docs Guild |
| 2025-11-30 | Added the 29-Nov-2025 SBOM→VEX Proof Blueprint advisory and outlined diagram/stub follow-up; logged sprint sync row for the blueprint. | Docs Guild |
| 2025-11-30 | Added the 30-Nov-2025 Rekor Receipt Checklist advisory and noted the ownership/action map for Authority/Sbomer/Vexer. | Docs Guild |
| 2025-11-30 | Added the 30-Nov-2025 Ecosystem Reality Test Cases advisory (credential leak, Trivy offline DB, SBOM parity, Grype divergence) and logged the acceptance test intent. | Docs Guild |
| 2025-11-30 | Added the 30-Nov-2025 Unknowns Decay & Triage advisory and noted UI + export artifacts for UnknownsRegistry + queues. | Docs Guild |
| 2025-11-30 | Added the 30-Nov-2025 Standup Sprint Kickstarters advisory, highlighting the three unblocker tasks/tickets and the proposed owners. | Docs Guild |
| 2025-11-30 | Added the 30-Nov-2025 Comparative Evidence Patterns advisory and recorded cross-tool evidence/suppression nuggets for UX designers. | Docs Guild |
| 2025-11-30 | Added the 30-Nov-2025 Implementor Guidelines advisory and checked the docs + sprint sync references; the row stays TODO until docs link updates finish. | Docs Guild |
## Decisions & Risks ## Decisions & Risks
| Item | Type | Owner(s) | Due | Notes | | Item | Type | Owner(s) | Due | Notes |

View File

@@ -30,7 +30,7 @@ Depends on: Sprint 100.A - Attestor, Sprint 110.A - AdvisoryAI, Sprint 120.A - A
| DEVOPS-AIRGAP-56-003 | DONE (2025-11-30) | Build Bootstrap Pack pipeline bundling images/charts, generating checksums, and publishing manifest for offline transfer. Dependencies: DEVOPS-AIRGAP-56-002. | DevOps Guild, Container Distribution Guild (ops/devops) | | DEVOPS-AIRGAP-56-003 | DONE (2025-11-30) | Build Bootstrap Pack pipeline bundling images/charts, generating checksums, and publishing manifest for offline transfer. Dependencies: DEVOPS-AIRGAP-56-002. | DevOps Guild, Container Distribution Guild (ops/devops) |
| DEVOPS-AIRGAP-57-001 | DONE (2025-11-30) | Automate Mirror Bundle creation jobs with dual-control approvals, artifact signing, and checksum publication. Dependencies: DEVOPS-AIRGAP-56-003. | DevOps Guild, Mirror Creator Guild (ops/devops) | | DEVOPS-AIRGAP-57-001 | DONE (2025-11-30) | Automate Mirror Bundle creation jobs with dual-control approvals, artifact signing, and checksum publication. Dependencies: DEVOPS-AIRGAP-56-003. | DevOps Guild, Mirror Creator Guild (ops/devops) |
| DEVOPS-AIRGAP-57-002 | BLOCKED (2025-11-18) | Waiting on upstream DEVOPS-AIRGAP-57-001 (mirror bundle automation) to provide artifacts/endpoints for sealed-mode CI; no sealed fixtures available to exercise tests. | DevOps Guild, Authority Guild (ops/devops) | | DEVOPS-AIRGAP-57-002 | BLOCKED (2025-11-18) | Waiting on upstream DEVOPS-AIRGAP-57-001 (mirror bundle automation) to provide artifacts/endpoints for sealed-mode CI; no sealed fixtures available to exercise tests. | DevOps Guild, Authority Guild (ops/devops) |
| DEVOPS-AIRGAP-58-001 | TODO | Provide local SMTP/syslog container templates and health checks for sealed environments; integrate into Bootstrap Pack. Dependencies: DEVOPS-AIRGAP-57-002. | DevOps Guild, Notifications Guild (ops/devops) | | DEVOPS-AIRGAP-58-001 | DONE (2025-11-30) | Provide local SMTP/syslog container templates and health checks for sealed environments; integrate into Bootstrap Pack. Dependencies: DEVOPS-AIRGAP-57-002. | DevOps Guild, Notifications Guild (ops/devops) |
| DEVOPS-AIRGAP-58-002 | TODO | Ship sealed-mode observability stack (Prometheus/Grafana/Tempo/Loki) pre-configured with offline dashboards and no remote exporters. Dependencies: DEVOPS-AIRGAP-58-001. | DevOps Guild, Observability Guild (ops/devops) | | DEVOPS-AIRGAP-58-002 | TODO | Ship sealed-mode observability stack (Prometheus/Grafana/Tempo/Loki) pre-configured with offline dashboards and no remote exporters. Dependencies: DEVOPS-AIRGAP-58-001. | DevOps Guild, Observability Guild (ops/devops) |
| DEVOPS-AOC-19-001 | BLOCKED (2025-10-26) | Integrate the AOC Roslyn analyzer and guard tests into CI, failing builds when ingestion projects attempt banned writes. | DevOps Guild, Platform Guild (ops/devops) | | DEVOPS-AOC-19-001 | BLOCKED (2025-10-26) | Integrate the AOC Roslyn analyzer and guard tests into CI, failing builds when ingestion projects attempt banned writes. | DevOps Guild, Platform Guild (ops/devops) |
| DEVOPS-AOC-19-002 | BLOCKED (2025-10-26) | Add pipeline stage executing `stella aoc verify --since` against seeded Mongo snapshots for Concelier + Excititor, publishing violation report artefacts. Dependencies: DEVOPS-AOC-19-001. | DevOps Guild (ops/devops) | | DEVOPS-AOC-19-002 | BLOCKED (2025-10-26) | Add pipeline stage executing `stella aoc verify --since` against seeded Mongo snapshots for Concelier + Excititor, publishing violation report artefacts. Dependencies: DEVOPS-AOC-19-001. | DevOps Guild (ops/devops) |
@@ -47,13 +47,14 @@ Depends on: Sprint 100.A - Attestor, Sprint 110.A - AdvisoryAI, Sprint 120.A - A
| DEVOPS-CONCELIER-CI-24-101 | DONE (2025-11-25) | Provide clean CI runner + warmed NuGet cache + vstest harness for Concelier WebService & Storage; deliver TRX/binlogs and unblock CONCELIER-GRAPH-24-101/28-102 and LNM-21-004..203. | DevOps Guild, Concelier Core Guild (ops/devops) | | DEVOPS-CONCELIER-CI-24-101 | DONE (2025-11-25) | Provide clean CI runner + warmed NuGet cache + vstest harness for Concelier WebService & Storage; deliver TRX/binlogs and unblock CONCELIER-GRAPH-24-101/28-102 and LNM-21-004..203. | DevOps Guild, Concelier Core Guild (ops/devops) |
| DEVOPS-SCANNER-CI-11-001 | DONE (2025-11-30) | Supply warmed cache/diag runner for Scanner analyzers (LANG-11-001, JAVA 21-005/008) with binlogs + TRX; unblock restore/test hangs. | DevOps Guild, Scanner EPDR Guild (ops/devops) | | DEVOPS-SCANNER-CI-11-001 | DONE (2025-11-30) | Supply warmed cache/diag runner for Scanner analyzers (LANG-11-001, JAVA 21-005/008) with binlogs + TRX; unblock restore/test hangs. | DevOps Guild, Scanner EPDR Guild (ops/devops) |
| DEVOPS-SCANNER-JAVA-21-011-REL | TODO | Package/sign Java analyzer plug-in once dev task 21-011 delivers; publish to Offline Kit/CLI release pipelines with provenance. | DevOps Guild, Scanner Release Guild (ops/devops) | | DEVOPS-SCANNER-JAVA-21-011-REL | TODO | Package/sign Java analyzer plug-in once dev task 21-011 delivers; publish to Offline Kit/CLI release pipelines with provenance. | DevOps Guild, Scanner Release Guild (ops/devops) |
| DEVOPS-SBOM-23-001 | TODO | Publish vetted offline NuGet feed + CI recipe for SbomService; prove with `dotnet test` run and share cache hashes; unblock SBOM-CONSOLE-23-001/002. | DevOps Guild, SBOM Service Guild (ops/devops) | | DEVOPS-SBOM-23-001 | DONE (2025-11-30) | Publish vetted offline NuGet feed + CI recipe for SbomService; prove with `dotnet test` run and share cache hashes; unblock SBOM-CONSOLE-23-001/002. | DevOps Guild, SBOM Service Guild (ops/devops) |
| FEED-REMEDIATION-1001 | BLOCKED (2025-11-24) | Define remediation scope and runbook for overdue feeds (CCCS/CERTBUND); schedule refresh; depends on PREP-FEEDCONN-ICS-KISA-PLAN. | Concelier Feed Owners (ops/devops) | | FEED-REMEDIATION-1001 | BLOCKED (2025-11-24) | Define remediation scope and runbook for overdue feeds (CCCS/CERTBUND); schedule refresh; depends on PREP-FEEDCONN-ICS-KISA-PLAN. | Concelier Feed Owners (ops/devops) |
| FEEDCONN-ICSCISA-02-012 / FEEDCONN-KISA-02-008 | BLOCKED (2025-11-24) | Publish provenance refresh/connector schedule for ICSCISA/KISA feeds; execute remediation per runbook once owners provide plan. | Concelier Feed Owners (ops/devops) | | FEEDCONN-ICSCISA-02-012 / FEEDCONN-KISA-02-008 | BLOCKED (2025-11-24) | Publish provenance refresh/connector schedule for ICSCISA/KISA feeds; execute remediation per runbook once owners provide plan. | Concelier Feed Owners (ops/devops) |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-11-30 | Completed DEVOPS-SBOM-23-001: added SBOM CI runner (`ops/devops/sbom-ci-runner/run-sbom-ci.sh`) with warmed-cache restore, binlog/TRX outputs, and NuGet cache hash evidence; documented in runner README. | DevOps |
| 2025-11-30 | Completed DEVOPS-SCANNER-CI-11-001: added offline-friendly Scanner CI runner (`ops/devops/scanner-ci-runner/run-scanner-ci.sh`) and README; produces build binlog + TRX outputs from key test projects with warmed NuGet cache. | DevOps | | 2025-11-30 | Completed DEVOPS-SCANNER-CI-11-001: added offline-friendly Scanner CI runner (`ops/devops/scanner-ci-runner/run-scanner-ci.sh`) and README; produces build binlog + TRX outputs from key test projects with warmed NuGet cache. | DevOps |
| 2025-11-30 | Completed DEVOPS-ATTEST-73-001/73-002: added attestor CI stub (`ops/devops/attestation/ci.yml`) and secrets/rotation plan in `ops/devops/attestation/README.md`; pending mirror into `.gitea/workflows/attestor-ci.yml` for live runs. | DevOps | | 2025-11-30 | Completed DEVOPS-ATTEST-73-001/73-002: added attestor CI stub (`ops/devops/attestation/ci.yml`) and secrets/rotation plan in `ops/devops/attestation/README.md`; pending mirror into `.gitea/workflows/attestor-ci.yml` for live runs. | DevOps |
| 2025-11-30 | Completed DEVOPS-SPANSINK-31-003: added OTLP span sink compose stack + collector config (`docker-compose.spansink.yml`, `otel-spansink.yaml`), run script, and Grafana dashboard stub (`ops/devops/signals/dashboards/excititor-vex-traces.json`). | DevOps | | 2025-11-30 | Completed DEVOPS-SPANSINK-31-003: added OTLP span sink compose stack + collector config (`docker-compose.spansink.yml`, `otel-spansink.yaml`), run script, and Grafana dashboard stub (`ops/devops/signals/dashboards/excititor-vex-traces.json`). | DevOps |
@@ -68,6 +69,7 @@ Depends on: Sprint 100.A - Attestor, Sprint 110.A - AdvisoryAI, Sprint 120.A - A
| 2025-11-24 | Added DEVOPS-SCANNER-JAVA-21-011-REL (moved from SPRINT_0131_0001_0001_scanner_surface.md) to keep DevOps release packaging in ops track. | Project Mgmt | | 2025-11-24 | Added DEVOPS-SCANNER-JAVA-21-011-REL (moved from SPRINT_0131_0001_0001_scanner_surface.md) to keep DevOps release packaging in ops track. | Project Mgmt |
| 2025-11-24 | Added DEVOPS-SPANSINK-31-003 (Excititor span sink for 31-003 traces) moved from SPRINT_0119_0001_0001_excititor_i per ops-only directive. | Project Mgmt | | 2025-11-24 | Added DEVOPS-SPANSINK-31-003 (Excititor span sink for 31-003 traces) moved from SPRINT_0119_0001_0001_excititor_i per ops-only directive. | Project Mgmt |
| 2025-11-24 | Imported Concelier feed ops items FEED-REMEDIATION-1001 and FEEDCONN-ICSCISA/KISA from Sprint 110; keeping feed remediation in ops track. | Project Mgmt | | 2025-11-24 | Imported Concelier feed ops items FEED-REMEDIATION-1001 and FEEDCONN-ICSCISA/KISA from Sprint 110; keeping feed remediation in ops track. | Project Mgmt |
| 2025-11-30 | Completed DEVOPS-AIRGAP-58-001: added syslog/SMTP compose stack (`ops/devops/airgap/compose-syslog-smtp.yaml`) and health script (`health_syslog_smtp.sh`); documented in airgap README for sealed environments. | DevOps |
| 2025-11-30 | DEVOPS-AIAI-31-001 DONE: added Advisory AI CI harness (`ops/devops/advisoryai-ci-runner/run-advisoryai-ci.sh`) producing binlog/TRX/summary; warmed local NuGet cache for offline runs; docs in runner README. | DevOps | | 2025-11-30 | DEVOPS-AIAI-31-001 DONE: added Advisory AI CI harness (`ops/devops/advisoryai-ci-runner/run-advisoryai-ci.sh`) producing binlog/TRX/summary; warmed local NuGet cache for offline runs; docs in runner README. | DevOps |
## Decisions & Risks ## Decisions & Risks

View File

@@ -2100,7 +2100,7 @@
| WEB-AIRGAP-56-002 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AIRGAP-56-002 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AIRGAP-57-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Policy Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AIRGAP-57-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Policy Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AIRGAP-58-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Importer Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AIRGAP-58-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Importer Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AOC-19-002 | TODO | | SPRINT_0212_0001_0001_web_i | BE-Base Platform Guild (src/Web/StellaOps.Web) | src/Web/StellaOps.Web | Ship `ProvenanceBuilder`, checksum utilities, and signature verification helper integrated with guard logging. Cover DSSE/CMS formats with unit tests. Dependencies: WEB-AOC-19-001. | | | | WEB-AOC-19-002 | DONE (2025-11-30) | | SPRINT_0212_0001_0001_web_i | BE-Base Platform Guild (src/Web/StellaOps.Web) | src/Web/StellaOps.Web | Ship `ProvenanceBuilder`, checksum utilities, and signature verification helper integrated with guard logging. Cover DSSE/CMS formats with unit tests. Dependencies: WEB-AOC-19-001. | | |
| WEB-AOC-19-003 | TODO | | SPRINT_116_concelier_v | QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AOC-19-003 | TODO | | SPRINT_116_concelier_v | QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AOC-19-004 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AOC-19-004 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AOC-19-005 | TODO | 2025-11-08 | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AOC-19-005 | TODO | 2025-11-08 | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
@@ -4290,7 +4290,7 @@
| WEB-AIRGAP-56-002 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AIRGAP-56-002 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AIRGAP-57-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Policy Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AIRGAP-57-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Policy Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AIRGAP-58-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Importer Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AIRGAP-58-001 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, AirGap Importer Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AOC-19-002 | TODO | | SPRINT_0212_0001_0001_web_i | BE-Base Platform Guild (src/Web/StellaOps.Web) | src/Web/StellaOps.Web | Ship `ProvenanceBuilder`, checksum utilities, and signature verification helper integrated with guard logging. Cover DSSE/CMS formats with unit tests. Dependencies: WEB-AOC-19-001. | | | | WEB-AOC-19-002 | DONE (2025-11-30) | | SPRINT_0212_0001_0001_web_i | BE-Base Platform Guild (src/Web/StellaOps.Web) | src/Web/StellaOps.Web | Ship `ProvenanceBuilder`, checksum utilities, and signature verification helper integrated with guard logging. Cover DSSE/CMS formats with unit tests. Dependencies: WEB-AOC-19-001. | | |
| WEB-AOC-19-003 | TODO | | SPRINT_116_concelier_v | QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AOC-19-003 | TODO | | SPRINT_116_concelier_v | QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AOC-19-004 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AOC-19-004 | TODO | | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |
| WEB-AOC-19-005 | TODO | 2025-11-08 | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | | | WEB-AOC-19-005 | TODO | 2025-11-08 | SPRINT_116_concelier_v | Concelier WebService Guild, QA Guild (src/Concelier/StellaOps.Concelier.WebService) | src/Concelier/StellaOps.Concelier.WebService | | | |

View File

@@ -8,6 +8,23 @@ This dossier summarises the end-to-end runtime topology after the Aggregation-On
--- ---
> Need a quick orientation? The [Developer Quickstart](../onboarding/dev-quickstart.md) (29-Nov-2025 advisory) captures the core repositories, determinism checks, DSSE conventions, and starter tasks that explain how the platform pieces fit together.
> Planner note: the [SBOM→VEX proof blueprint](../product-advisories/29-Nov-2025 - SBOM to VEX Proof Pipeline Blueprint.md) shows the DSSE → Rekor v2 tiles → VEX linkage, so threat-model and compliance teams can copy the capture/verification checkpoints.
> Working on a feature? Check the [Implementor Guidelines](../product-advisories/30-Nov-2025 - Implementor Guidelines for Stella Ops.md) to align with the SRS + release playbook checklist before you merge anything into main.
> Need to prove Rekor receipts? The [Rekor Receipt Checklist](../product-advisories/30-Nov-2025 - Rekor Receipt Checklist for Stella Ops.md) maps each field to a module owner and explains offline metadata for deterministic re-verification.
> Taming unknowns? The [Unknowns Decay & Triage Heuristics](../product-advisories/30-Nov-2025 - Unknowns Decay & Triage Heuristics.md) explains the confidence decay card, triage queue view, and the daily export artifact for planning.
> Check the [Ecosystem Reality Test Cases](../product-advisories/30-Nov-2025 - Ecosystem Reality Test Cases for StellaOps.md) for reproducible acceptance tests based on credential leaks, offline DB schema issues, SBOM parity drift, and scanner version divergence.
> Need unblocker tasks? The [Standup Sprint Kickstarters](../product-advisories/30-Nov-2025 - Standup Sprint Kickstarters.md) lists three day-0 wins (scanner regressions, Postgres slice, DSSE/Rekor sweep) plus ready-to-copy ticket names.
> Compare how evidence/suppression/audit flows work elsewhere via the [Comparative Evidence Patterns](../product-advisories/30-Nov-2025 - Comparative Evidence Patterns for Stella Ops.md) brief—Snyk, GitHub, Aqua, Anchore/Grype, Prisma Cloud, and the UX trade-offs.
> Evaluate public scanner incidents? The [Ecosystem Test Cases](../product-advisories/30-Nov-2025 - Ecosystem Test Cases for StellaOps.md) document five hardened regressions (Grype credential leak, Trivy offline schema, SBOM parity, Grype instability) that you can turn into acceptance tests today.
## 1·System landscape ## 1·System landscape
```mermaid ```mermaid

View File

@@ -0,0 +1,326 @@
# StellaOps Developer Quickstart
> **Audience:** Mid-level .NET developers
> **Goal:** Get you productive on StellaOps in 12 days, with special focus on determinism, cryptographic attestations, and the canonical data model.
---
This quickstart mirrors the 29-Nov-2025 Developer Onboarding advisory (`docs/product-advisories/29-Nov-2025 - StellaOps Mid-Level .NET Onboarding (Quick Start).md`) and keeps the determinism-first guidance in sync with that release note.
## 1. What Youre Building (Context)
StellaOps is a sovereign, air-gap-friendly platform that turns **SBOMs → VEX** with a fully **replayable, deterministic trust graph**.
Core concepts:
- **Deterministic scans:** Same inputs → same graph, hashes, and verdicts.
- **Cryptographic attestations:** DSSE/in-toto envelopes, optional PQC.
- **Trust lattice:** Merges vendor VEX, runtime signals, configs, etc. into a single deterministic verdict.
- **Audit trail:** Every decision is reproducible from stored inputs and proofs.
If you think “content-addressed trust pipeline for SBOMs + VEX,” youre in the right mental model.
---
## 2. Repository & Docs Map
Start by opening these projects **in order**:
1. `src/StellaOps.Scanner.WebService/`
Scanning endpoints, rule plumbing, and calls into the trust lattice.
2. `src/StellaOps.Vexer/` (a.k.a. *Excititor*)
VEX verdict engine and trust-merge logic.
3. `src/StellaOps.Sbomer/`
SBOM ingest / normalize (CycloneDX, SPDX).
4. `src/StellaOps.Authority/`
Key management, DSSE/in-toto attestations, license tokens, Rekor integration.
5. `src/StellaOps.Scheduler/`
Batch processing, replay orchestration.
6. `src/StellaOps.Shared/CanonicalModel/`
Canonical entities & graph IDs. **Read this carefully** it underpins determinism.
Helpful docs:
- `docs/modules/platform/*` protocols (DSSE envelopes, lattice terms, trust receipts).
- `docs/architecture/*` high-level diagrams and flows.
---
## 3. Local Dev Setup
### 3.1 Prerequisites
- **.NET 10 SDK** (preview as specified in repo).
- **Docker** (for DB, queues, object storage).
- **Node.js** (for Angular UI, if youre touching the frontend).
- **WSL2** (optional but convenient on Windows).
### 3.2 Bring Up Infra
From the repo root:
```bash
# Bring up core infra for offline / air-gap friendly dev
docker compose -f compose/offline-kit.yml up -d
```
This usually includes:
- MongoDB or Postgres (configurable).
- RabbitMQ (or equivalent queue).
- MinIO / object storage (depending on profile).
### 3.3 Configure Environment
```bash
cp env/example.local.env .env
```
Key settings:
- `STELLAOPS_DB=Mongo` or `Postgres`.
- `AUTHORITY_*` key material and config (see comments in `example.local.env`).
- Optional: `AUTHORITY_PQC=on` to enable post-quantum keys (Dilithium).
### 3.4 Build & Run Backend
```bash
# Restore & build everything
dotnet restore
dotnet build -c Debug
# Run a focused slice for development
dotnet run --project src/StellaOps.Authority/StellaOps.Authority.csproj
dotnet run --project src/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj
```
Health checks (adjust ports if needed):
```bash
curl -s http://localhost:5080/health # Authority
curl -s http://localhost:5081/health # Scanner
```
---
## 4. Deterministic Sanity Tests
These tests prove your local environment is configured correctly for **determinism**. If any of these fail due to snapshot mismatch, fix your environment before writing new features.
### 4.1 SBOM → VEX “Not Affected” (Reachability False)
```bash
dotnet test tests/Determinism/Det_SbomToVex_NotAffected.csproj
```
**What it checks:**
- Two consecutive runs with the same SBOM produce identical `GraphRevisionID` and DSSE payload hashes.
If they differ, inspect:
- JSON canonicalization.
- Locale / culture.
- Line endings.
### 4.2 In-toto Chain: Source → Build → Image Attestation
```bash
dotnet test tests/Attestations/Att_InToto_Chain.csproj
```
**What it checks:**
- DSSE envelope canonicalization is stable.
- Signature over CBOR-canonical JSON matches the stored hash.
- Full in-toto chain can be replayed deterministically.
### 4.3 Lattice Merge: Vendor VEX + Runtime Signal
```bash
dotnet test tests/Lattice/Lattice_VendorPlusRuntime.csproj
```
**What it checks:**
- Merge verdict is stable regardless of input set order.
- Resulting `TrustReceipt` is byte-for-byte identical between runs.
If any “golden” snapshots differ, you likely have:
- Non-canonical JSON.
- Unstable enumeration (e.g., iterating `Dictionary<>` directly).
- Locale or newline drift.
---
## 5. Coding Conventions (Determinism & Crypto)
These are **non-negotiable** in code that affects trust graphs, proofs, or attestations.
### 5.1 JSON & Canonicalization
- Use the **`CanonicalJson`** helper whenever a payload is hashed, signed, or used for IDs.
- Rules: UTF-8, sorted keys, no insignificant whitespace, `\n` line endings.
### 5.2 DSSE Envelopes
- `payloadType` must always be `application/vnd.stellaops.trust+json`.
- Sign over the canonicalized bytes.
```csharp
var payload = CanonicalJson.Serialize(trustDoc);
var env = DsseEnvelope.Create("application/vnd.stellaops.trust+json", payload);
var signed = await keyRing.SignAsync(env.CanonicalizeBytes());
await rekor.SubmitAsync(signed, RekorMode.OfflineMirrorIfAirgapped);
```
### 5.3 Hashing
- **BLAKE3** for internal content addressing.
- **SHA-256** where interop demands it.
- Never mix algorithms within the same ID type.
### 5.4 Keys & Algorithms
- Default signatures: **Ed25519** via `Authority.KeyRing`.
- Optional PQC: **Dilithium** when `AUTHORITY_PQC=on`.
- Always go through the keyring abstraction; never manage raw keys manually.
### 5.5 Time & Clocks
- Use `Instant`/`DateTimeOffset` (UTC), truncated to milliseconds.
- Never use `DateTime.Now` or local clocks in canonical data.
### 5.6 IDs & Graph Nodes
- Canonical/public IDs derive from hashes of canonical bytes.
- DB primary keys are implementation details.
- Do not depend on DB auto-increment or implicit sort order when hashing.
### 5.7 VEX Verdicts
Every VEX verdict must:
- Carry `proofs[]` (reachability, config guards, runtime paths).
- Emit a `receipt` signed by Authority, covering verdict, proof hashes, and context.
---
## 6. Daily Workflow
1. Pick a focused issue (see starter tasks below).
2. Write tests first, especially determinism scenarios.
3. Implement changes with canonicalization boundaries explicit and signing centralized.
4. Run `dotnet test --filter Category=Determinism`.
5. Commit with the appropriate prefix (`feat(scanner):`, `feat(vexer):`, `feat(authority):`) and mention the affected `GraphRevisionID` if your change alters the trust graph.
---
## 7. Suggested Starter Tasks
These introduce the canonical data model and determinism mindset.
### 7.1 Normalize CycloneDX Components → Canonical Packages
**Area:** `StellaOps.Sbomer`
**Tests:** `tests/Determinism/Det_SbomMapping`
**Definition of done:**
- Equivalent SBOMs (even if fields shuffle) yield identical package sets and canonical IDs.
- `CanonicalPackageSet.hash` is stable.
- Edge cases covered: missing `purl`, duplicate components, case variation.
### 7.2 Implement “Not-Affected by Configuration” Proof
**Area:** `StellaOps.Vexer/Proofs/ConfigSwitchProof.cs`
**Definition of done:**
- With `FeatureX=false`, CVE-1234 reports `status = not_affected` and the proof records `configPath` + `observed=false`.
- Proof hash is deterministic and included in the DSSE receipt.
- Lattice merge flips the verdict to `not_affected` when the runtime/config proof weight crosses the threshold, even if the vendor says `affected`.
### 7.3 Authority Offline Rekor Mirror Submitter
**Area:** `StellaOps.Authority/Rekor/RekorMirrorClient.cs`
**Definition of done:**
- `RekorMode.OfflineMirrorIfAirgapped` records canonical entries (JSON + hash path) locally.
- `rekor sync` replays entries in order, preserving entry IDs.
- Golden test ensures the same input sequence → same mirror tree hash.
---
## 8. Database Notes (Mongo ↔ Postgres)
- Use `StellaOps.Shared.Persistence` repository interfaces.
- Canonical/public IDs are hash-derived; DB keys are internal details.
- Never rely on DB sort order for anything that affects hashes or verdicts; re-canonicalize before hashing and apply deterministic ordering afterwards.
---
## 9. Common Pitfalls
1. Non-canonical JSON (unsorted keys, extra whitespace, mixed `\r\n`).
2. Local time creeping into proofs (`DateTime.Now`).
3. Unstable GUIDs in tests or canonical entities.
4. Unordered collections (`Dictionary<>` iterations, LINQ without `OrderBy`) while hashing or serializing.
5. Platform drift (Windows vs Linux newline/culture differences) always use invariant culture and `\n` in canonical data.
---
## 10. Useful Commands
### 10.1 Determinism Pack
```bash
# Run determinism-tagged fixtures
dotnet test --filter Category=Determinism
```
Update golden snapshots deliberately:
```bash
dotnet test --filter Category=Determinism -- \
TestRunParameters.Parameter(name="UpdateSnapshots", value="true")
```
### 10.2 Quick API Smoke
```bash
curl -s http://localhost:5080/health
curl -s -X POST \
http://localhost:5081/scan \
-H "Content-Type: application/json" \
-d @samples/nginx.sbom.json
```
### 10.3 Verify DSSE Signature Locally
```bash
dotnet run --project tools/StellaOps.Tools.Verify -- file trust.receipt.json
```
---
## 11. Glossary (Ask-Once)
- **SBOM** Software Bill of Materials (CycloneDX/SPDX).
- **VEX** Vulnerability Exploitability eXchange: verdicts include `affected`, `not_affected`, `under_investigation`.
- **DSSE** Dead Simple Signing Envelope; we sign canonical bytes.
- **In-toto** Supply-chain attestation framework for source → build → artifact chains.
- **Lattice** Rule system merging multiple verdicts/proofs into deterministic outcomes.
- **GraphRevisionID** Hash of the canonical trust graph; acts like a build number for audits.
Welcome aboard. Your best “map” is:
1. Read the CanonicalModel types.
2. Run the determinism tests.
3. Ship one of the starter tasks with deterministic, test-covered changes.
Keep everything **canonical, hashable, and replayable** and youll fit right in.

View File

@@ -10,5 +10,7 @@ Artifacts supporting `DEVOPS-AIRGAP-56-001`:
- `build_bootstrap_pack.py` — Builds a Bootstrap Pack from images/charts/extras listed in a JSON config, writing `bootstrap-manifest.json` + `checksums.sha256` deterministically. - `build_bootstrap_pack.py` — Builds a Bootstrap Pack from images/charts/extras listed in a JSON config, writing `bootstrap-manifest.json` + `checksums.sha256` deterministically.
- `build_bootstrap_pack.sh` — Wrapper for the bootstrap pack builder. - `build_bootstrap_pack.sh` — Wrapper for the bootstrap pack builder.
- `build_mirror_bundle.py` — Generates mirror bundle manifest + checksums with dual-control approvals; optional cosign signing. Outputs `mirror-bundle-manifest.json`, `checksums.sha256`, and optional signature/cert. - `build_mirror_bundle.py` — Generates mirror bundle manifest + checksums with dual-control approvals; optional cosign signing. Outputs `mirror-bundle-manifest.json`, `checksums.sha256`, and optional signature/cert.
- `compose-syslog-smtp.yaml` — Local SMTP (MailHog) + syslog-ng stack for sealed environments.
- `health_syslog_smtp.sh` — Brings up the syslog/SMTP stack via docker compose and performs health checks (MailHog API + syslog logger).
See also `ops/devops/sealed-mode-ci/` for the full sealed-mode compose harness and `egress_probe.py`, which this verification script wraps. See also `ops/devops/sealed-mode-ci/` for the full sealed-mode compose harness and `egress_probe.py`, which this verification script wraps.

View File

@@ -0,0 +1,31 @@
version: "3.9"
services:
smtp:
image: mailhog/mailhog:v1.0.1
container_name: mailhog
ports:
- "1025:1025" # SMTP (plain)
- "8025:8025" # Web UI
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8025/api/v2/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
restart: unless-stopped
syslog:
image: balabit/syslog-ng:4.7.1
container_name: syslog-ng
ports:
- "514:514/udp"
- "514:514/tcp"
command: ["/usr/sbin/syslog-ng", "-F", "-p", "/var/run/syslogd.pid"]
healthcheck:
test: ["CMD", "syslog-ng-ctl", "stats"]
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
restart: unless-stopped

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
# Health check for compose-syslog-smtp.yaml (DEVOPS-AIRGAP-58-001)
COMPOSE_FILE="$(cd "$(dirname "$0")" && pwd)/compose-syslog-smtp.yaml"
echo "Starting syslog+smtp stack..."
docker compose -f "$COMPOSE_FILE" up -d
echo "Waiting for health checks..."
docker compose -f "$COMPOSE_FILE" wait >/dev/null 2>&1 || true
echo "Current health status:"
docker compose -f "$COMPOSE_FILE" ps
echo "Sending test syslog message (UDP)..."
logger -n 127.0.0.1 -P 514 -d "test syslog message $(date -u +%s)"
echo "SMTP health endpoint:"
curl -sf http://127.0.0.1:8025/api/v2/health || exit 1
echo "Done."

View File

@@ -0,0 +1,29 @@
# SBOM Service CI Runner Harness (DEVOPS-SBOM-23-001)
Purpose: deterministic, offline-friendly CI harness for SBOM Service. Produces warmed-cache restore, build binlog, TRX outputs, and a NuGet cache hash to unblock SBOM console/consumer sprints.
Usage
- From repo root run: `ops/devops/sbom-ci-runner/run-sbom-ci.sh`
- Outputs land in `ops/devops/artifacts/sbom-ci/<UTC timestamp>/`:
- `build.binlog` (solution build)
- `tests/sbom.trx` (VSTest results)
- `nuget-cache.hash` (sha256 over file name+size listing for offline cache traceability)
- `summary.json` (paths + sources + cache hash)
Environment defaults
- `DOTNET_CLI_TELEMETRY_OPTOUT=1`, `DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1`, `DOTNET_RESTORE_DISABLE_PARALLEL=1`
- `NUGET_PACKAGES=$REPO/.nuget/packages`
- `NUGET_SOURCES=$REPO/local-nugets;$REPO/.nuget/packages`
- `TEST_FILTER` empty (set to narrow tests)
What it does
1) Warm NuGet cache from `local-nugets/` into `$NUGET_PACKAGES` for air-gap parity.
2) `dotnet restore` + `dotnet build` on `src/SbomService/StellaOps.SbomService.sln` with `/bl`.
3) Run `StellaOps.SbomService.Tests` with TRX output (honors `TEST_FILTER`).
4) Produce `nuget-cache.hash` using sorted file name+size list hashed with sha256 (lightweight evidence of cache contents).
5) Emit `summary.json` with artefact paths and cache hash value.
Notes
- Offline-only; no external services required.
- Timestamped output folders keep ordering deterministic; consumers should sort lexicographically.
- Extend `test_project` in the script if additional SBOM test projects are added.

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail
# SBOM Service CI runner (DEVOPS-SBOM-23-001)
# Builds SBOM solution and runs tests with warmed NuGet cache; emits binlog + TRX + cache hash summary.
repo_root="$(cd "$(dirname "$0")/../../.." && pwd)"
ts="$(date -u +%Y%m%dT%H%M%SZ)"
out_dir="$repo_root/ops/devops/artifacts/sbom-ci/$ts"
logs_dir="$out_dir/tests"
mkdir -p "$logs_dir"
export DOTNET_CLI_TELEMETRY_OPTOUT=${DOTNET_CLI_TELEMETRY_OPTOUT:-1}
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=${DOTNET_SKIP_FIRST_TIME_EXPERIENCE:-1}
export DOTNET_RESTORE_DISABLE_PARALLEL=${DOTNET_RESTORE_DISABLE_PARALLEL:-1}
export NUGET_PACKAGES=${NUGET_PACKAGES:-$repo_root/.nuget/packages}
export NUGET_SOURCES=${NUGET_SOURCES:-"$repo_root/local-nugets;$repo_root/.nuget/packages"}
export TEST_FILTER=${TEST_FILTER:-""}
mkdir -p "$NUGET_PACKAGES"
rsync -a "$repo_root/local-nugets/" "$NUGET_PACKAGES/" >/dev/null 2>&1 || true
restore_sources=()
IFS=';' read -ra SRC_ARR <<< "$NUGET_SOURCES"
for s in "${SRC_ARR[@]}"; do
[[ -n "$s" ]] && restore_sources+=(--source "$s")
done
solution="$repo_root/src/SbomService/StellaOps.SbomService.sln"
dotnet restore "$solution" --ignore-failed-sources "${restore_sources[@]}"
build_binlog="$out_dir/build.binlog"
dotnet build "$solution" -c Release /p:ContinuousIntegrationBuild=true /bl:"$build_binlog"
trx_name="sbom.trx"
test_project="$repo_root/src/SbomService/StellaOps.SbomService.Tests/StellaOps.SbomService.Tests.csproj"
common_test_args=( -c Release --no-build --results-directory "$logs_dir" )
if [[ -n "$TEST_FILTER" ]]; then
common_test_args+=( --filter "$TEST_FILTER" )
fi
if [[ -f "$test_project" ]]; then
dotnet test "$test_project" "${common_test_args[@]}" --logger "trx;LogFileName=$trx_name"
fi
# Lightweight cache hash: list files with size, hash the listing
cache_listing="$out_dir/nuget-cache.list"
find "$NUGET_PACKAGES" -type f -printf "%P %s\n" | sort > "$cache_listing"
cache_hash=$(sha256sum "$cache_listing" | awk '{print $1}')
echo "$cache_hash nuget-cache.list" > "$out_dir/nuget-cache.hash"
summary="$out_dir/summary.json"
{
printf '{\n'
printf ' "timestamp_utc": "%s",\n' "$ts"
printf ' "build_binlog": "%s",\n' "${build_binlog#${repo_root}/}"
printf ' "tests": [\n'
printf ' {"project":"SbomService","trx":"%s"}\n' "${logs_dir#${repo_root}/}/$trx_name"
printf ' ],\n'
printf ' "nuget_packages": "%s",\n' "${NUGET_PACKAGES#${repo_root}/}"
printf ' "cache_hash": "%s",\n' "$cache_hash"
printf ' "sources": [\n'
for i in "${!SRC_ARR[@]}"; do
sep=","; [[ $i -eq $((${#SRC_ARR[@]}-1)) ]] && sep=""
printf ' "%s"%s\n' "${SRC_ARR[$i]}" "$sep"
done
printf ' ]\n'
printf '}\n'
} > "$summary"
echo "Artifacts written to ${out_dir#${repo_root}/}"

View File

@@ -0,0 +1,8 @@
# Web Guild Tasks
| Task ID | State | Notes |
| --- | --- | --- |
| WEB-AOC-19-002 | DONE (2025-11-30) | Added provenance builder, checksum utilities, and DSSE/CMS signature verification helpers with unit tests. |
| WEB-AOC-19-003 | TODO | Analyzer/guard validation remains; will align once helper APIs settle. |
| WEB-CONSOLE-23-002 | TODO | Status/stream endpoints to proxy Scheduler once contracts finalized. |
| WEB-EXC-25-001 | TODO | Exceptions workflow CRUD pending policy scopes. |

View File

@@ -0,0 +1,71 @@
export type HashAlgorithm = 'SHA-256' | 'SHA-512';
export interface DigestResult {
algorithm: HashAlgorithm;
hex: string;
uri: string; // e.g. sha256:abcd...
}
const encoder = new TextEncoder();
function toUint8(payload: string | ArrayBuffer | Uint8Array): Uint8Array {
if (typeof payload === 'string') {
return encoder.encode(payload);
}
if (payload instanceof ArrayBuffer) {
return new Uint8Array(payload);
}
return payload;
}
function toHex(buffer: ArrayBuffer): string {
return Array.from(new Uint8Array(buffer))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Deterministically compute a digest over an input payload using WebCrypto.
* Returns both the raw hex and a URI-style prefix (sha256:...).
*/
export async function computeDigest(
payload: string | ArrayBuffer | Uint8Array,
algorithm: HashAlgorithm = 'SHA-256'
): Promise<DigestResult> {
if (!globalThis.crypto?.subtle) {
throw new Error('WebCrypto unavailable: cannot compute digest');
}
const data = toUint8(payload);
const digestBuffer = await globalThis.crypto.subtle.digest(algorithm, data);
const hex = toHex(digestBuffer);
const prefix = algorithm.toLowerCase().replace('-', '');
return {
algorithm,
hex,
uri: `${prefix}:${hex}`,
};
}
/**
* Convenience helper to deterministically serialize an object before hashing.
* Uses stable key ordering and JSON without spaces.
*/
export function canonicalJson(input: unknown): string {
const replacer = (_key: string, value: unknown) => {
if (value && typeof value === 'object' && !Array.isArray(value)) {
return Object.keys(value as Record<string, unknown>)
.sort()
.reduce<Record<string, unknown>>((acc, key) => {
acc[key] = (value as Record<string, unknown>)[key];
return acc;
}, {});
}
return value;
};
return JSON.stringify(input, replacer);
}

View File

@@ -0,0 +1,128 @@
import { ProvenanceBuilder } from './provenance-builder';
import { canonicalJson, computeDigest } from './checksum.util';
import {
dssePreAuthEncode,
verifyCmsSignature,
verifyDsseSignature,
} from './signature-verifier';
async function exportPublicKeyPem(publicKey: CryptoKey): Promise<string> {
const spki = await crypto.subtle.exportKey('spki', publicKey);
const base64 = btoa(String.fromCharCode(...new Uint8Array(spki)));
const wrapped = base64.match(/.{1,64}/g)?.join('\n') ?? base64;
return `-----BEGIN PUBLIC KEY-----\n${wrapped}\n-----END PUBLIC KEY-----`;
}
describe('Provenance utilities', () => {
it('builds deterministic provenance for objects', async () => {
const builder = new ProvenanceBuilder(() => new Date('2025-11-30T12:00:00Z'));
const provenance = await builder.build(
{ b: 2, a: 1 },
{
sourceId: 'registry-1',
sourceType: 'registry',
sourceUrl: 'https://example.test/manifest',
submitter: 'ci-bot',
}
);
expect(provenance.ingestedAt).toBe('2025-11-30T12:00:00.000Z');
expect(provenance.digest.startsWith('sha256:')).toBeTrue();
const canonical = canonicalJson({ b: 2, a: 1 });
const digest = await computeDigest(canonical, 'SHA-256');
expect(provenance.digest).toBe(digest.uri);
});
it('computes DSSE pre-auth encoding with correct prefix', () => {
const payload = new TextEncoder().encode('hello');
const pae = dssePreAuthEncode('text/plain', payload);
const asText = new TextDecoder().decode(pae);
expect(asText.startsWith('DSSEv1 10 text/plain 5 ')).toBeTrue();
expect(asText.endsWith('hello')).toBeTrue();
});
it('verifies CMS signatures', async () => {
const keyPair = await crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
true,
['sign', 'verify']
);
const message = new TextEncoder().encode('aoc-proof');
const signature = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', keyPair.privateKey, message);
const pem = await exportPublicKeyPem(keyPair.publicKey);
const result = await verifyCmsSignature({
payload: message,
signature,
publicKeyPem: pem,
algorithm: 'RSASSA-PKCS1-v1_5',
hash: 'SHA-256',
});
expect(result.valid).toBeTrue();
});
it('fails verification when payload changes', async () => {
const keyPair = await crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
true,
['sign', 'verify']
);
const message = new TextEncoder().encode('aoc-proof');
const signature = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', keyPair.privateKey, message);
const pem = await exportPublicKeyPem(keyPair.publicKey);
const result = await verifyCmsSignature({
payload: 'tampered',
signature,
publicKeyPem: pem,
algorithm: 'RSASSA-PKCS1-v1_5',
hash: 'SHA-256',
});
expect(result.valid).toBeFalse();
});
it('verifies DSSE signatures using pre-auth encoding', async () => {
const keyPair = await crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
true,
['sign', 'verify']
);
const payloadBytes = new TextEncoder().encode('{"sub":"example"}');
const pae = dssePreAuthEncode('application/json', payloadBytes);
const signature = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', keyPair.privateKey, pae);
const pem = await exportPublicKeyPem(keyPair.publicKey);
const result = await verifyDsseSignature({
payload: payloadBytes,
payloadType: 'application/json',
signature,
publicKeyPem: pem,
algorithm: 'RSASSA-PKCS1-v1_5',
hash: 'SHA-256',
});
expect(result.valid).toBeTrue();
expect(result.message?.startsWith('sha256:')).toBeTrue();
});
});

View File

@@ -0,0 +1,40 @@
import { AocProvenance } from '../api/aoc.models';
import { HashAlgorithm, canonicalJson, computeDigest } from './checksum.util';
export interface ProvenanceOptions {
sourceId: string;
sourceType?: AocProvenance['sourceType'];
sourceUrl?: string;
submitter?: string;
ingestedAt?: string;
hashAlgorithm?: HashAlgorithm;
}
/**
* Deterministic provenance builder used by the AOC workspace to attach
* digests and timing metadata to documents before verification/signing.
*/
export class ProvenanceBuilder {
constructor(private readonly clock: () => Date = () => new Date()) {}
async build(
payload: string | ArrayBuffer | Uint8Array | Record<string, unknown>,
options: ProvenanceOptions
): Promise<AocProvenance> {
const serialised =
typeof payload === 'string' || payload instanceof ArrayBuffer || payload instanceof Uint8Array
? payload
: canonicalJson(payload);
const digest = await computeDigest(serialised, options.hashAlgorithm ?? 'SHA-256');
return {
sourceId: options.sourceId,
sourceType: options.sourceType,
sourceUrl: options.sourceUrl,
submitter: options.submitter,
ingestedAt: options.ingestedAt ?? this.clock().toISOString(),
digest: digest.uri,
};
}
}

View File

@@ -0,0 +1,133 @@
import { HashAlgorithm, computeDigest } from './checksum.util';
export interface VerificationResult {
valid: boolean;
message?: string;
}
export interface VerifyOptions {
payload: string | ArrayBuffer | Uint8Array;
signature: ArrayBuffer | Uint8Array | string; // base64 if string
publicKeyPem: string;
algorithm?: 'RSASSA-PKCS1-v1_5' | 'RSA-PSS';
hash?: HashAlgorithm;
saltLength?: number; // RSA-PSS only
}
function base64ToArrayBuffer(input: string): ArrayBuffer {
const normalized = input.replace(/\s+/g, '');
const binary = atob(normalized);
const buffer = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i += 1) {
buffer[i] = binary.charCodeAt(i);
}
return buffer.buffer;
}
async function importPublicKey(
pem: string,
algorithm: 'RSASSA-PKCS1-v1_5' | 'RSA-PSS',
hash: HashAlgorithm
): Promise<CryptoKey> {
const clean = pem
.replace('-----BEGIN PUBLIC KEY-----', '')
.replace('-----END PUBLIC KEY-----', '')
.replace(/\s+/g, '');
const binaryDer = base64ToArrayBuffer(clean);
return globalThis.crypto.subtle.importKey(
'spki',
binaryDer,
{
name: algorithm,
hash: { name: hash },
},
true,
['verify']
);
}
function toUint8(payload: string | ArrayBuffer | Uint8Array): Uint8Array {
if (typeof payload === 'string') {
return new TextEncoder().encode(payload);
}
if (payload instanceof ArrayBuffer) {
return new Uint8Array(payload);
}
return payload;
}
function normalizeSignature(sig: ArrayBuffer | Uint8Array | string): ArrayBuffer {
if (typeof sig === 'string') {
return base64ToArrayBuffer(sig);
}
if (sig instanceof ArrayBuffer) {
return sig;
}
return sig.buffer;
}
export async function verifyCmsSignature(options: VerifyOptions): Promise<VerificationResult> {
if (!globalThis.crypto?.subtle) {
return { valid: false, message: 'WebCrypto unavailable' };
}
const algorithm = options.algorithm ?? 'RSASSA-PKCS1-v1_5';
const hash = options.hash ?? 'SHA-256';
const key = await importPublicKey(options.publicKeyPem, algorithm, hash);
const payload = toUint8(options.payload);
const signature = normalizeSignature(options.signature);
const verified = await globalThis.crypto.subtle.verify(
{ name: algorithm, saltLength: options.saltLength ?? 32 },
key,
signature,
payload
);
return verified
? { valid: true }
: { valid: false, message: 'Signature verification failed' };
}
export interface DsseVerifyOptions extends VerifyOptions {
payloadType: string;
}
/**
* DSSE pre-authentication encoding (PAE) as defined by sigstore.
* Format: `DSSEv1 <len(type)> <type> <len(payload)> <payload>`
*/
export function dssePreAuthEncode(payloadType: string, payload: Uint8Array): Uint8Array {
const enc = new TextEncoder();
const header = `DSSEv1 ${payloadType.length} ${payloadType} ${payload.length} `;
const headerBytes = enc.encode(header);
const output = new Uint8Array(headerBytes.length + payload.length);
output.set(headerBytes, 0);
output.set(payload, headerBytes.length);
return output;
}
export async function verifyDsseSignature(
options: DsseVerifyOptions
): Promise<VerificationResult> {
if (!globalThis.crypto?.subtle) {
return { valid: false, message: 'WebCrypto unavailable' };
}
const payloadBytes = toUint8(options.payload);
const pae = dssePreAuthEncode(options.payloadType, payloadBytes);
const cmsResult = await verifyCmsSignature({
...options,
payload: pae,
});
if (!cmsResult.valid) {
return cmsResult;
}
// Provide a digest hint for downstream display/debugging
const digest = await computeDigest(payloadBytes, options.hash ?? 'SHA-256');
return { valid: true, message: digest.uri };
}