up
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.js": {
|
||||||
|
"lines_covered": [
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
13,
|
||||||
|
18,
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"lines_total": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /api/admin/exec",
|
||||||
|
"path": [
|
||||||
|
"app.js::createServer",
|
||||||
|
"handler",
|
||||||
|
"eval(code)"
|
||||||
|
],
|
||||||
|
"sink": "ExpressEval::exec",
|
||||||
|
"notes": "Admin exec reached"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.js": {
|
||||||
|
"lines_covered": [
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15
|
||||||
|
],
|
||||||
|
"lines_total": 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /api/admin/exec",
|
||||||
|
"path": [
|
||||||
|
"app.js::createServer",
|
||||||
|
"guard: ALLOW_EXEC!=true"
|
||||||
|
],
|
||||||
|
"sink": "ExpressGuarded::exec",
|
||||||
|
"notes": "Guard blocked sink"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.js": {
|
||||||
|
"lines_covered": [
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
13,
|
||||||
|
18,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"lines_total": 45
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /api/render",
|
||||||
|
"path": [
|
||||||
|
"app.js::createServer",
|
||||||
|
"render template"
|
||||||
|
],
|
||||||
|
"sink": "FastifyTemplate::render",
|
||||||
|
"notes": "Template rendered with user input"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.js": {
|
||||||
|
"lines_covered": [
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11
|
||||||
|
],
|
||||||
|
"lines_total": 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /api/exec",
|
||||||
|
"path": [
|
||||||
|
"app.js:handleRequest",
|
||||||
|
"guard: FEATURE_ENABLE != 1"
|
||||||
|
],
|
||||||
|
"sink": "GuardedEval::handleRequest",
|
||||||
|
"notes": "Guard prevented sink execution"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.js": {
|
||||||
|
"lines_covered": [
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
12,
|
||||||
|
15
|
||||||
|
],
|
||||||
|
"lines_total": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /api/exec",
|
||||||
|
"path": [
|
||||||
|
"app.js:handleRequest",
|
||||||
|
"eval(code)"
|
||||||
|
],
|
||||||
|
"sink": "UnsafeEval::handleRequest",
|
||||||
|
"notes": "Test-driven dynamic trace"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.py": {
|
||||||
|
"lines_covered": [
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"lines_total": 38
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /render",
|
||||||
|
"path": [
|
||||||
|
"app.py::handle_request",
|
||||||
|
"render"
|
||||||
|
],
|
||||||
|
"sink": "DjangoSSTI::render",
|
||||||
|
"notes": "Template rendered (autoescape off)"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.py": {
|
||||||
|
"lines_covered": [
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
11
|
||||||
|
],
|
||||||
|
"lines_total": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /exec",
|
||||||
|
"path": [
|
||||||
|
"app.py::handle_request",
|
||||||
|
"guard: ALLOW_EXEC!=true"
|
||||||
|
],
|
||||||
|
"sink": "FastApiGuarded::handle_request",
|
||||||
|
"notes": "Guard blocked eval"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.py": {
|
||||||
|
"lines_covered": [
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11
|
||||||
|
],
|
||||||
|
"lines_total": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /render",
|
||||||
|
"path": [
|
||||||
|
"app.py::handle_request",
|
||||||
|
"render"
|
||||||
|
],
|
||||||
|
"sink": "FlaskTemplate::render",
|
||||||
|
"notes": "Template rendered"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.py": {
|
||||||
|
"lines_covered": [
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
11
|
||||||
|
],
|
||||||
|
"lines_total": 34
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true
|
||||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"src/app.py": {
|
||||||
|
"lines_covered": [
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
8,
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"lines_total": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"entry": "POST /api/exec",
|
||||||
|
"path": [
|
||||||
|
"app.py::handle_request",
|
||||||
|
"eval(code)"
|
||||||
|
],
|
||||||
|
"sink": "PyUnsafeExec::handle_request",
|
||||||
|
"notes": "Eval reached"
|
||||||
|
}
|
||||||
@@ -21,8 +21,9 @@
|
|||||||
|
|
||||||
1. **Value in context** – [Overview](overview.md) compresses the “Why” + “What” stories and shows how Stella Ops stands apart.
|
1. **Value in context** – [Overview](overview.md) compresses the “Why” + “What” stories and shows how Stella Ops 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, VEX‑first 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 Day‑0 to Day‑30 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 Stella Ops
|
## Key capabilities that define Stella Ops
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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 (0–3) 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 (0–3) 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 |
|
||||||
|
|||||||
@@ -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 312–335) | 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 312–335) | 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 |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 | | | |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
326
docs/onboarding/dev-quickstart.md
Normal file
326
docs/onboarding/dev-quickstart.md
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
# StellaOps Developer Quickstart
|
||||||
|
|
||||||
|
> **Audience:** Mid-level .NET developers
|
||||||
|
> **Goal:** Get you productive on StellaOps in 1–2 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 You’re 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,” you’re 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 you’re 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 you’ll fit right in.
|
||||||
@@ -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.
|
||||||
|
|||||||
31
ops/devops/airgap/compose-syslog-smtp.yaml
Normal file
31
ops/devops/airgap/compose-syslog-smtp.yaml
Normal 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
|
||||||
23
ops/devops/airgap/health_syslog_smtp.sh
Normal file
23
ops/devops/airgap/health_syslog_smtp.sh
Normal 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."
|
||||||
29
ops/devops/sbom-ci-runner/README.md
Normal file
29
ops/devops/sbom-ci-runner/README.md
Normal 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.
|
||||||
72
ops/devops/sbom-ci-runner/run-sbom-ci.sh
Normal file
72
ops/devops/sbom-ci-runner/run-sbom-ci.sh
Normal 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}/}"
|
||||||
8
src/Web/StellaOps.Web/TASKS.md
Normal file
8
src/Web/StellaOps.Web/TASKS.md
Normal 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. |
|
||||||
71
src/Web/StellaOps.Web/src/app/core/aoc/checksum.util.ts
Normal file
71
src/Web/StellaOps.Web/src/app/core/aoc/checksum.util.ts
Normal 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);
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
40
src/Web/StellaOps.Web/src/app/core/aoc/provenance-builder.ts
Normal file
40
src/Web/StellaOps.Web/src/app/core/aoc/provenance-builder.ts
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
133
src/Web/StellaOps.Web/src/app/core/aoc/signature-verifier.ts
Normal file
133
src/Web/StellaOps.Web/src/app/core/aoc/signature-verifier.ts
Normal 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 };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user