Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly. - Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps. - Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges. - Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges. - Set up project file for the test project with necessary dependencies and configurations. - Include JSON fixture files for testing purposes.
This commit is contained in:
@@ -58,6 +58,8 @@ Air-Gapped Mode is the supported operating profile for deployments with **zero e
|
||||
- **Authority scopes:** enforce `airgap:status:read`, `airgap:import`, and `airgap:seal` via tenant-scoped roles; require operator reason/ticket metadata for sealing.
|
||||
- **Incident response:** maintain scripts for replaying imports, regenerating manifests, and exporting forensic data without egress.
|
||||
- **EgressPolicy facade:** all services route outbound calls through `StellaOps.AirGap.Policy`. In sealed mode `EgressPolicy` enforces the `airgap.egressAllowlist`, auto-permits loopback targets, and raises `AIRGAP_EGRESS_BLOCKED` exceptions with remediation text (add host to allowlist or coordinate break-glass). Unsealed mode logs intents but does not block, giving operators a single toggle for rehearsals. Task Runner now feeds every `run.egress` declaration and runtime network hint into the shared policy during planning, preventing sealed-mode packs from executing unless destinations are declared and allow-listed.
|
||||
- **CLI guard:** the CLI now routes outbound HTTP through the shared egress policy. When sealed, commands that would dial external endpoints (for example, `scanner download` or remote `sources ingest` URIs) are refused with `AIRGAP_EGRESS_BLOCKED` messaging and remediation guidance instead of attempting the network call.
|
||||
- **Observability exporters:** `StellaOps.Telemetry.Core` now binds OTLP exporters to the configured egress policy. When sealed, any collector endpoint that is not loopback or allow-listed is skipped at startup and a structured warning is written so operators see the remediation guidance without leaving sealed mode.
|
||||
- **Linting/CI:** enable the `StellaOps.AirGap.Policy.Analyzers` package in solution-level analyzers so CI fails on raw `HttpClient` usage. The analyzer emits `AIRGAP001` and the bundled code fix rewrites to `EgressHttpClientFactory.Create(...)`; treat analyzer warnings as errors in sealed-mode pipelines.
|
||||
|
||||
## Testing & verification
|
||||
|
||||
90
docs/airgap/portable-evidence.md
Normal file
90
docs/airgap/portable-evidence.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Portable Evidence Bundles (Sealed/Air-Gapped)
|
||||
|
||||
> Sprint 160 · Task EVID-OBS-60-001
|
||||
> Audience: Evidence Locker operators, Air-Gap controllers, incident responders
|
||||
|
||||
Portable bundles let operators hand off sealed evidence across enclaves without exposing tenant identifiers or internal storage coordinates. The Evidence Locker produces a deterministic archive (`portable-bundle-v1.tgz`) that carries the manifest + signature alongside redacted metadata, checksum manifest, and an offline verification script.
|
||||
|
||||
## 1. When to use the portable flow
|
||||
|
||||
- **Sealed mode exports.** Regulatory or incident response teams that cannot access the primary enclave directly.
|
||||
- **Chain-of-custody transfers.** Moving evidence into offline review systems while keeping the DSSE provenance intact.
|
||||
- **Break-glass rehearsals.** Validating incident response playbooks without exposing internal bundle metadata.
|
||||
|
||||
Avoid portable bundles for regular intra-enclave automation; the full `bundle.tgz` already carries richer metadata for automated tooling.
|
||||
|
||||
## 2. Generating the bundle
|
||||
|
||||
1. Seal the evidence bundle as usual (`POST /evidence/snapshot` or via CLI).
|
||||
2. Request the portable artefact using the new endpoint:
|
||||
|
||||
```
|
||||
GET /evidence/{bundleId}/portable
|
||||
Scope: evidence:read
|
||||
```
|
||||
|
||||
Response headers mirror the standard download (`application/gzip`, `Content-Disposition: attachment; filename="portable-evidence-bundle-{bundleId}.tgz"`).
|
||||
|
||||
The Evidence Locker caches the portable archive using write-once semantics. Subsequent requests reuse the existing object and the audit log records whether the file was newly created or served from cache.
|
||||
|
||||
## 3. Archive layout
|
||||
|
||||
```
|
||||
portable-bundle-v1.tgz
|
||||
├── manifest.json # Canonical bundle manifest (identical to sealed bundle)
|
||||
├── signature.json # DSSE signature + optional RFC3161 timestamp (base64 token)
|
||||
├── bundle.json # Redacted metadata (bundleId, kind, rootHash, timestamps, incidentMetadata)
|
||||
├── checksums.txt # Merkle root + per-entry SHA-256 digests
|
||||
├── instructions-portable.txt # Human-readable guidance for sealed transfers
|
||||
└── verify-offline.sh # POSIX shell helper (extract + checksum verify + reminder to run DSSE verification)
|
||||
```
|
||||
|
||||
Redaction rules:
|
||||
|
||||
- No tenant identifiers, storage keys, descriptions, or free-form metadata.
|
||||
- Incident metadata is retained *only* under the `incidentMetadata` object (`incident.mode`, `incident.changedAt`, etc.).
|
||||
- `portableGeneratedAt` records when the archive was produced so downstream systems can reason about freshness.
|
||||
|
||||
## 4. Offline verification workflow
|
||||
|
||||
1. Copy `portable-bundle-v1.tgz` into the sealed environment (USB, sneaker-net, etc.).
|
||||
2. Run the included helper from a POSIX shell:
|
||||
|
||||
```sh
|
||||
chmod +x verify-offline.sh
|
||||
./verify-offline.sh portable-bundle-v1.tgz
|
||||
```
|
||||
|
||||
The script:
|
||||
- extracts the archive into a temporary directory,
|
||||
- validates `checksums.txt` using `sha256sum` (or `shasum -a 256`), and
|
||||
- prints the Merkle root hash from `bundle.json`.
|
||||
|
||||
3. Complete provenance verification:
|
||||
- Preferred: `stella evidence verify --bundle portable-bundle-v1.tgz`
|
||||
- Alternative: supply `manifest.json` and `signature.json` to the evidence verifier library.
|
||||
|
||||
4. Record the verification output (root hash, timestamp) with the receiving enclave’s evidence locker or incident ticket.
|
||||
|
||||
> **Note:** The DSSE payload is unchanged from the sealed bundle, so existing verification tooling does not need special handling for portable archives.
|
||||
|
||||
## 5. Importing into the receiving enclave
|
||||
|
||||
1. Upload the archive to the target Evidence Locker or attach it to the incident record.
|
||||
2. Store the checksum report generated by `verify-offline.sh` alongside the archive.
|
||||
3. If downstream automation needs enriched metadata, attach a private note referencing the original bundle’s tenant context—the portable archive intentionally omits it.
|
||||
|
||||
## 6. Troubleshooting
|
||||
|
||||
| Symptom | Likely cause | Remediation |
|
||||
|--------|--------------|-------------|
|
||||
| `verify-offline.sh` reports checksum failures | Transfer corruption | Re-transfer artefact; run `sha256sum portable-bundle-v1.tgz` on both sides and compare. |
|
||||
| `stella evidence verify` cannot reach TSA | Sealed environment lacks TSA connectivity | Verification still succeeds using DSSE signature; capture the missing TSA warning in the import log. |
|
||||
| `/portable` endpoint returns 400 | Bundle not yet sealed or signature missing | Wait for sealing to complete; ensure DSSE signing is enabled. |
|
||||
| `/portable` returns 404 | Bundle not found or tenant mismatch | Confirm DPoP scope and tenant claim; refresh bundle status via `GET /evidence/{id}`. |
|
||||
|
||||
## 7. Change management
|
||||
|
||||
- Portable bundle versioning is encoded in the filename (`portable-bundle-v1.tgz`). When content or script behaviour changes, bump the version and announce in release notes.
|
||||
- Any updates to `verify-offline.sh` must remain POSIX-sh compatible and avoid external dependencies beyond `tar`, `sha256sum`/`shasum`, and standard coreutils.
|
||||
- Remember to update this guide and the bundle packaging dossier (`docs/modules/evidence-locker/bundle-packaging.md`) when fields or workflows change.
|
||||
Reference in New Issue
Block a user