up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
This commit is contained in:
6
docs/modules/attestor/keys-and-issuers.md
Normal file
6
docs/modules/attestor/keys-and-issuers.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Keys and Issuers (DOCS-ATTEST-74-001)
|
||||
|
||||
- Maintain issuer registry (KMS IDs, key IDs, allowed predicates).
|
||||
- Rotate keys with overlap; publish fingerprints and validity in registry file.
|
||||
- Offline operation: bundle registry with bootstrap; no remote fetch.
|
||||
- Each attestation must include issuer ID and key ID; verify against registry.
|
||||
9
docs/modules/attestor/overview.md
Normal file
9
docs/modules/attestor/overview.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Attestor Overview (DOCS-ATTEST-73-001)
|
||||
|
||||
High-level description of the Attestor service and its contracts.
|
||||
|
||||
- Purpose: verify DSSE/attestations, supply transparency info, and expose attestation APIs without deriving verdicts.
|
||||
- Components: WebService, Worker, KMS integration, Transparency log (optional), Evidence links.
|
||||
- Rule banner: aggregation-only; no policy decisions.
|
||||
- Tenancy: all attestations scoped per tenant; cross-tenant reads forbidden.
|
||||
- Offline posture: allow offline verification using bundled trust roots and Rekor checkpoints when available.
|
||||
@@ -1,48 +1,29 @@
|
||||
# Attestor Payload Reference
|
||||
# Attestor Payloads (DOCS-ATTEST-73-002)
|
||||
|
||||
StellaOps evidence predicates must remain reproducible, explainable, and portable across online and fully air-gapped deployments. This guide lists each predicate type, indicates where the canonical JSON Schema lives, highlights the producing service, and links to the matching golden samples.
|
||||
Schemas/examples for attestations handled by Attestor.
|
||||
|
||||
## Quick Reference
|
||||
## DSSE payload
|
||||
```json
|
||||
{
|
||||
"_type": "https://in-toto.io/Statement/v1",
|
||||
"subject": [{"name": "sha256:...", "digest": {"sha256": "..."}}],
|
||||
"predicateType": "stella.ops/vexObservation@v1",
|
||||
"predicate": {
|
||||
"observationId": "vex:obs:sha256:...",
|
||||
"tenant": "default",
|
||||
"providerId": "ubuntu-csaf",
|
||||
"createdAt": "2025-11-23T23:10:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Type ID | Predicate URI | Schema file | Produced by | Primary consumers |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| StellaOps.BuildProvenance@1 | https://schemas.stella-ops.org/attestations/build-provenance@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-build-provenance.v1.schema.json | Build pipelines, Scanner SBOM bake stage | Attestor, Export Center, Policy Engine |
|
||||
| StellaOps.SBOMAttestation@1 | https://schemas.stella-ops.org/attestations/sbom-attestation@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-sbom-attestation.v1.schema.json | Scanner.Worker SBOM composer | Policy Engine, CLI, Export Center |
|
||||
| StellaOps.ScanResults@1 | https://schemas.stella-ops.org/attestations/scan-results@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-scan-results.v1.schema.json | Scanner.Worker analyzers | Policy Engine, CLI, Orchestrator |
|
||||
| StellaOps.PolicyEvaluation@1 | https://schemas.stella-ops.org/attestations/policy-evaluation@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-policy-evaluation.v1.schema.json | Policy Engine explain pipeline | CLI, Notify, Export Center |
|
||||
| StellaOps.VEXAttestation@1 | https://schemas.stella-ops.org/attestations/vex-attestation@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-vex-attestation.v1.schema.json | Excititor consensus service | Policy Engine, CLI, Console |
|
||||
| StellaOps.RiskProfileEvidence@1 | https://schemas.stella-ops.org/attestations/risk-profile@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-risk-profile.v1.schema.json | Policy Engine risk pipeline | Console, Notify, Export Center |
|
||||
| StellaOps.CustomEvidence@1 | https://schemas.stella-ops.org/attestations/custom-evidence@1 | src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-custom-evidence.v1.schema.json | CLI custom evidence workflows and partner integrations | Policy Engine (policy hooks), Export Center |
|
||||
## Evidence links
|
||||
- Each payload references evidence hashes (VEX observations/linksets) and optional timeline event IDs.
|
||||
- Keep payloads aggregation-only; no verdict fields.
|
||||
|
||||
Golden JSON fixtures that double as contract tests live under `src/Attestor/StellaOps.Attestor.Types/fixtures/v1/<predicate>.sample.json`. TypeScript and Go clients consume the generated sources in `src/Attestor/StellaOps.Attestor.Types/generated/ts` and `src/Attestor/StellaOps.Attestor.Types/generated/go`.
|
||||
## Hashing/signing
|
||||
- Canonicalize JSON (RFC 8785) before signing.
|
||||
- Use SHA-256 digests; include in envelope metadata.
|
||||
|
||||
## Envelope Conventions
|
||||
|
||||
- DSSE envelopes are signed over canonical JSON (sorted keys, UTF-8, no insignificant whitespace).
|
||||
- The `subject` array must include at least one SHA-256 digest and may attach annotations such as `oci.reference` or `stellaops.asset`.
|
||||
- `predicateType` uses the URI shown in the table; `predicate.typeId` mirrors the short identifier.
|
||||
- `predicate.schemaVersion` follows semantic versioning. Consumers reject mismatched major versions.
|
||||
- Optional `metadata` and `materials` sections follow the in-toto Statement format to maximise provenance portability.
|
||||
|
||||
## Predicate Highlights
|
||||
|
||||
- **StellaOps.BuildProvenance@1** records builder identity, config source, materials, reproducibility flags, and the resulting artifact digests. Outputs must match the DSSE subject.
|
||||
- **StellaOps.SBOMAttestation@1** links an artifact digest to a CycloneDX 1.6 or SBOM 3.0.0 document, tracking inventory counts and the generator metadata. Component graph hashes reference CAS entries emitted by Scanner.Worker.
|
||||
- **StellaOps.ScanResults@1** captures deterministic findings from OS, language, and native analyzers. It reports summary counts, per-finding metadata (PURL, severity, exploitability), and the layer digests inspected.
|
||||
- **StellaOps.PolicyEvaluation@1** documents lattice-based policy outcomes, including decision traces and evidence digests consumed during evaluation.
|
||||
- **StellaOps.VEXAttestation@1** mirrors OpenVEX-aligned statements with justification, scope narrowing (package coordinates or component IDs), and issue timestamps.
|
||||
- **StellaOps.RiskProfileEvidence@1** summarises exploitability, ticketing load, runtime coverage, and maturity for downstream dashboards.
|
||||
- **StellaOps.CustomEvidence@1** allows regulated tenants to attach organisation-specific payloads referenced by a CAS-hosted schema while preserving provenance and retention controls.
|
||||
|
||||
## Validation and Tooling
|
||||
|
||||
- Run `npm install` once, then `npm run docs:attestor:validate` to validate JSON fixtures against their schemas, execute the generated TypeScript tests (`npm test`), and run `go test ./...` for the Go SDK. The command fails fast when any schema, fixture, or generated SDK drifts.
|
||||
- Regenerate schemas and SDKs after edits with `dotnet run --project src/Attestor/StellaOps.Attestor.Types/Tools/StellaOps.Attestor.Types.Generator`.
|
||||
- Offline Kit builds (`ops/devops/offline-kit/`) mirror schemas, fixtures, and SDK bundles so air-gapped operators can run the same validation stack.
|
||||
|
||||
## Related Material
|
||||
|
||||
- `docs/modules/attestor/architecture.md` — service topology, Rekor integration, caching model.
|
||||
- `docs/modules/platform/architecture-overview.md` — cross-module data flows and tenant boundaries.
|
||||
- `docs/ingestion/aggregation-only-contract.md` — guardrails for advisory feeds consumed by policy evaluation.
|
||||
- `src/Attestor/StellaOps.Attestor.Types/samples/README.md` — directory map for the golden evidence set referenced here.
|
||||
## Examples
|
||||
- Place sample payloads in `docs/samples/attestor/payloads/` (add when available).
|
||||
|
||||
12
docs/modules/attestor/policies.md
Normal file
12
docs/modules/attestor/policies.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Attestor Policies (DOCS-ATTEST-73-003)
|
||||
|
||||
Guidance on verification policies applied by Attestor.
|
||||
|
||||
- Scope: DSSE envelope validation, subject hash matching, optional transparency checks.
|
||||
- Policy fields:
|
||||
- allowed issuers / key IDs
|
||||
- required predicates (e.g., `stella.ops/vexObservation@v1`)
|
||||
- transparency requirements (allow/require/skip)
|
||||
- freshness window for attestations
|
||||
- Determinism: policies must be pure; no external lookups in sealed mode.
|
||||
- Versioning: include `policyVersion` and hash; store alongside attestation records.
|
||||
6
docs/modules/attestor/transparency.md
Normal file
6
docs/modules/attestor/transparency.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Transparency (DOCS-ATTEST-74-002)
|
||||
|
||||
- Optional Rekor/witness integration.
|
||||
- In sealed mode, use bundled checkpoints and disable live witness fetch.
|
||||
- Verification: compare embedded checkpoint with bundled; log discrepancies.
|
||||
- Record transparency fields on verification result: `{uuid, logIndex, checkpointHash}`.
|
||||
@@ -1,247 +1,9 @@
|
||||
# Attestor Verification Workflows
|
||||
# Attestor Workflows (DOCS-ATTEST-73-004)
|
||||
|
||||
> How StellaOps turns DSSE bundles into verifiable evidence, how the verification API reports outcomes, and how explainability signals surface in UI/CLI flows.
|
||||
Sequence of ingest, verify, and bulk operations.
|
||||
|
||||
> ⚠️ **2025-11-01 coordination note:** `StellaOps.Attestor.WebService` is failing to compile until downstream fixes land (`Contracts/AttestationBundleContracts.cs` null-coalescing update and scope/token variables restored in `Program.cs`). Verification flows ship in infrastructure/tests, but the WebService hand-off stays blocked — track via `ATTESTOR-73-002` (see Attestor task board).
|
||||
|
||||
## 1. Verification flow (API and service contract)
|
||||
|
||||
- **Entry point.** `POST /api/v1/rekor/verify` deserialises to `AttestorVerificationRequest`.
|
||||
- **Resolution order.** The service tries `uuid`, then canonicalised `bundle`, then `artifactSha256`. At least one selector must be present (`invalid_query` otherwise).
|
||||
- **Optional proof refresh.** `refreshProof=true` forces a Rekor lookup before returning. Proofs are cached in Mongo.
|
||||
- **Signature replay.** Supplying `bundle` lets the service recompute the canonical hash and re-run signature checks; omitting the bundle skips those steps but still validates Merkle proofs and cached policy decisions.
|
||||
- **Auth scopes.** Endpoints demand `attestor.verify` (write scope is also accepted); read-only detail/list APIs require `attestor.read` at minimum.
|
||||
|
||||
### 1.1 Request properties
|
||||
|
||||
| Field | Type | Required | Purpose |
|
||||
|-------|------|----------|---------|
|
||||
| `uuid` | string | optional | Rekor V2 UUID to verify and (optionally) refresh. |
|
||||
| `bundle` | object | optional | DSSE envelope (same shape as submission) for signature re-verification. |
|
||||
| `artifactSha256` | string | optional | Resolve the most recent entry for an attestable artefact digest. |
|
||||
| `subject` | string | optional | Logical subject identifier used for cache/telemetry tagging; defaults to the stored artifact digest. |
|
||||
| `envelopeId` | string | optional | Stable identifier for the DSSE bundle (typically the canonical hash); enables cache lookups. |
|
||||
| `policyVersion` | string | optional | Policy digest/version driving verification; feeds cache keys and observability dimensions. |
|
||||
| `refreshProof` | bool | optional (default `false`) | Pull the current inclusion proof and checkpoint from Rekor before evaluating. |
|
||||
|
||||
All selectors are mutually compatible; if more than one is set the service uses the first match (`uuid` → `bundle` → `artifactSha256`).
|
||||
|
||||
### 1.2 Response schema (`AttestorVerificationResult`)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `ok` | bool | `true` when the entry status is `included` **and** no issues were recorded. |
|
||||
| `uuid` | string | Rekor UUID that satisfied the query. Useful for follow-up fetches. |
|
||||
| `index` | number (int64) | Rekor log index, when supplied by the backend. |
|
||||
| `logUrl` | string | Fully-qualified Rekor entry URL for operators and auditors. |
|
||||
| `status` | string | Transparency-log status seen in Mongo (`included`, `pending`, `failed`, …). |
|
||||
| `checkedAt` | string (ISO-8601 UTC) | Timestamp emitted when the response is created. |
|
||||
| `issues` | array[string] | Machine-readable explainability codes. Empty when `ok=true`. |
|
||||
|
||||
> **Note:** `checkedAt` is recomputed each call; cache hits do not recycle previous timestamps.
|
||||
|
||||
### 1.3 Success criteria
|
||||
|
||||
`ok=true` requires:
|
||||
|
||||
1. Entry exists and status equals `included`.
|
||||
2. Canonical DSSE hash matches the stored bundle hash.
|
||||
3. Signature re-verification (when a bundle is supplied) succeeds.
|
||||
4. Inclusion proof validates against the cached or refreshed checkpoint.
|
||||
|
||||
Any deviation records at least one issue and flips `ok` to `false`. Consumers **must** inspect `issues` rather than inferring from `status` alone.
|
||||
|
||||
## 2. Verification report schema
|
||||
|
||||
`AttestorVerificationResult` carries the flattened summary shown above. When callers request the detailed report (`GET /api/v1/rekor/entries/{uuid}?refresh=true` or via SDK) they receive a `VerificationReport` shaped as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"overallStatus": "pass",
|
||||
"succeeded": true,
|
||||
"policy": { ... },
|
||||
"issuer": { ... },
|
||||
"freshness": { ... },
|
||||
"signatures": { ... },
|
||||
"transparency": { ... },
|
||||
"issues": [ "bundle_hash_mismatch" ]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `overallStatus` | string (`pass`, `warn`, `fail`, `skipped`) | Aggregated verdict derived from the individual section statuses. |
|
||||
| `succeeded` | bool | Convenience flag; `true` when `overallStatus ∈ {pass, warn}`. |
|
||||
| `policy` | object | Results from policy evaluation (see below). |
|
||||
| `issuer` | object | Identity/result of the signing entity. |
|
||||
| `freshness` | object | Age analysis relative to policy settings. |
|
||||
| `signatures` | object | Signature validation summary. |
|
||||
| `transparency` | object | Inclusion proof / checkpoint evaluation summary. |
|
||||
| `issues` | array[string] | De-duplicated set drawn from the sections; order is deterministic and stable. |
|
||||
|
||||
### 2.1 `policy`
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `status` | Section verdict (`pass`, `warn`, `fail`, `skipped`). |
|
||||
| `policyId` / `policyVersion` | DSL identifier and revision used for evaluation. |
|
||||
| `verdict` | Policy outcome (`allow`, `challenge`, `deny`, etc.). |
|
||||
| `issues` | Policy-specific explainability codes (e.g., `policy_rule_blocked`). |
|
||||
| `attributes` | Key/value map emitted by the policy for downstream observability (e.g., applicable rules, matched waivers). |
|
||||
|
||||
### 2.2 `issuer`
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `status` | Result of issuer validation. |
|
||||
| `mode` | Signing mode detected (`keyless`, `kms`, `unknown`). |
|
||||
| `issuer` | Distinguished name / issuer URI recorded during signing. |
|
||||
| `subjectAlternativeName` | SAN pulled from the Fulcio certificate (keyless) or recorded KMS identity. |
|
||||
| `keyId` | Logical key identifier associated with the signature. |
|
||||
| `issues` | Issuer-specific issues (e.g., `issuer_trust_root_mismatch`, `signer_mode_unsupported:kid`). |
|
||||
|
||||
### 2.3 `freshness`
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `status` | `fail` when the attestation exceeds `verification.freshnessMaxAgeMinutes`; `warn` when only the warning threshold is hit. |
|
||||
| `createdAt` | Timestamp embedded in the attestation metadata. |
|
||||
| `evaluatedAt` | Server-side timestamp used for age calculations. |
|
||||
| `age` | ISO8601 duration of `evaluatedAt - createdAt`. |
|
||||
| `maxAge` | Policy-driven ceiling (null when unchecked). |
|
||||
| `issues` | `freshness_max_age_exceeded`, `freshness_warning`, etc. |
|
||||
|
||||
### 2.4 `signatures`
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `status` | Signature validation verdict. |
|
||||
| `bundleProvided` | `true` when canonical DSSE bytes were supplied. |
|
||||
| `totalSignatures` | Count observed in the DSSE envelope. |
|
||||
| `verifiedSignatures` | Number of signatures that validated against trusted keys. |
|
||||
| `requiredSignatures` | Policy / configuration minimum enforced. |
|
||||
| `issues` | Signature codes such as `bundle_payload_invalid_base64`, `signature_invalid`, `signer_mode_unknown`. |
|
||||
|
||||
### 2.5 `transparency`
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `status` | Inclusion proof / checkpoint verdict. |
|
||||
| `proofPresent` | Whether a proof document was available. |
|
||||
| `checkpointPresent` | Indicates the Rekor checkpoint existed and parsed. |
|
||||
| `inclusionPathPresent` | `true` when the Merkle path array contained nodes. |
|
||||
| `issues` | Merkle/rekor codes (`proof_missing`, `proof_leafhash_mismatch`, `checkpoint_missing`, `proof_root_mismatch`). |
|
||||
|
||||
### 2.6 Issue catalogue (non-exhaustive)
|
||||
|
||||
| Code | Trigger | Notes |
|
||||
|------|---------|-------|
|
||||
| `bundle_hash_mismatch` | Canonical DSSE hash differs from stored value. | Often indicates tampering or inconsistent canonicalisation. |
|
||||
| `bundle_payload_invalid_base64` | DSSE payload cannot be base64-decoded. | Validate producer pipeline; the attestation is unusable. |
|
||||
| `signature_invalid` | At least one signature failed cryptographic verification. | Consider checking key rotation / revocation status. |
|
||||
| `signer_mode_unknown` / `signer_mode_unsupported:<mode>` | Signing mode not configured for this installation. | Update `attestorOptions.security.signerIdentity.mode`. |
|
||||
| `issuer_trust_root_mismatch` | Certificate chain does not terminate in configured Fulcio/KMS roots. | Check Fulcio bundle / KMS configuration. |
|
||||
| `freshness_max_age_exceeded` | Attestation older than permitted maximum. | Regenerate attestation or extend policy window. |
|
||||
| `proof_missing` | No inclusion proof stored or supplied. | When running offline, import bundles with proofs or allow warn-level policies. |
|
||||
| `proof_root_mismatch` | Rebuilt Merkle root differs from checkpoint. | Proof may be stale or log compromised; escalate. |
|
||||
| `checkpoint_missing` | No Rekor checkpoint available. | Configure `RequireCheckpoint=false` to downgrade severity. |
|
||||
|
||||
Downstream consumers (UI, CLI, policy studio) should render human-readable messages but must retain the exact issue codes for automation and audit replay.
|
||||
|
||||
## 3. Explainability signals
|
||||
|
||||
1. **Canonicalisation.** The service replays DSSE canonicalisation to derive `bundleSha256`. Failures surface as `bundle_hash_mismatch` or decoding errors.
|
||||
2. **Signature checks.** Mode-aware handling:
|
||||
- `kms` (HMAC) compares against configured shared secrets.
|
||||
- `keyless` rebuilds the certificate chain, enforces Fulcio roots, SAN allow-lists, and verifies with the leaf certificate.
|
||||
- Unknown modes emit `signer_mode_unknown` / `signer_mode_unsupported:<mode>`.
|
||||
3. **Proof acquisition.** When `refreshProof` is requested the Rekor backend may contribute a textual issue (`Proof refresh failed: …`) without stopping evaluation.
|
||||
4. **Merkle validation.** Structured helper ensures leaf hash, path orientation, and checkpoint root are consistent; each validation failure has a discrete issue code.
|
||||
5. **Observability.** The meter `attestor.verify_total` increments with `result=ok|failed`; structured logs and traces carry the same `issues` vector for UI/CLI drill-down.
|
||||
|
||||
All issues are appended in detection order to simplify chronological replay in the Console’s chain-of-custody view.
|
||||
|
||||
## 3. Issue catalogue
|
||||
|
||||
| Code | Trigger | Operator guidance |
|
||||
|------|---------|-------------------|
|
||||
| `bundle_hash_mismatch` | Canonicalised DSSE hash differs from stored bundle hash. | Re-download artefact; investigate tampering or submission races. |
|
||||
| `bundle_payload_invalid_base64` | Payload could not be base64-decoded. | Ensure bundle transport preserved payload; capture original DSSE for forensics. |
|
||||
| `signature_invalid_kms` | HMAC verification failed for `mode=kms`. | Confirm shared secret alignment with Signer; rotate keys if drift detected. |
|
||||
| `signer_mode_unknown` | Entry lacks signer mode metadata and bundle omitted it. | Re-ingest bundle or inspect submission pipeline metadata. |
|
||||
| `signer_mode_unsupported:<mode>` | Signer mode is unsupported by the verifier. | Add support or block unsupported issuers in policy. |
|
||||
| `kms_key_missing` | No configured KMS secrets to verify `mode=kms`. | Populate `security:signerIdentity:kmsKeys` in Attestor config before retry. |
|
||||
| `signature_invalid_base64` | One or more signatures were not valid base64. | Bundle corruption; capture raw payload and re-submit. |
|
||||
| `certificate_chain_missing` | `mode=keyless` bundle lacked any certificates. | Ensure Signer attaches Fulcio chain; review submission pipeline. |
|
||||
| `certificate_chain_invalid` | Certificates could not be parsed. | Fetch original DSSE bundle for repair; confirm certificate encoding. |
|
||||
| `certificate_chain_untrusted[:detail]` | Chain failed custom-root validation. | Import correct Fulcio roots or investigate potential impersonation. |
|
||||
| `certificate_san_untrusted` | Leaf SAN not in configured allow-list. | Update allow-list or revoke offending issuer. |
|
||||
| `signature_invalid` | No signature validated with supplied public keys. | Treat as tampering; trigger incident response. |
|
||||
| `proof_missing` | No Merkle proof stored for the entry. | Re-run with `refreshProof=true`; check Rekor availability. |
|
||||
| `bundle_hash_decode_failed` | Stored bundle hash could not be decoded. | Verify Mongo record integrity; re-enqueue submission if necessary. |
|
||||
| `proof_inclusion_missing` | Inclusion section absent from proof. | Retry proof refresh; inspect Rekor health. |
|
||||
| `proof_leafhash_decode_failed` | Leaf hash malformed. | Replay submission; inspect Rekor data corruption. |
|
||||
| `proof_leafhash_mismatch` | Leaf hash differs from canonical bundle hash. | Raises tamper alert; reconcile Rekor entry vs stored bundle. |
|
||||
| `proof_path_decode_failed` | Inclusion path entry malformed. | Same action as above; likely Rekor data corruption. |
|
||||
| `proof_path_orientation_missing` | Inclusion path lacks left/right marker. | File Rekor bug; fallback to mirror log if configured. |
|
||||
| `checkpoint_missing` | Proof lacks checkpoint metadata. | Retry refresh; ensure Rekor is configured to return checkpoints. |
|
||||
| `checkpoint_root_decode_failed` | Checkpoint root hash malformed. | Investigate Rekor/mirror integrity before trusting log. |
|
||||
| `proof_root_mismatch` | Computed root hash != checkpoint root. | Critical alert; assume inclusion proof compromised. |
|
||||
| `Proof refresh failed: …` | Rekor fetch threw an exception. | Message includes upstream error; surface alongside telemetry for debugging. |
|
||||
|
||||
Future explainability flags must follow the same pattern: short, lowercase codes with optional suffix payload (`code:detail`).
|
||||
|
||||
## 4. Worked examples
|
||||
|
||||
### 4.1 Successful verification
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"uuid": "0192fdb4-a82b-7f90-b894-6fd1dd918b85",
|
||||
"index": 73421,
|
||||
"logUrl": "https://rekor.stellaops.test/api/v2/log/entries/0192fdb4a82b7f90b8946fd1dd918b85",
|
||||
"status": "included",
|
||||
"checkedAt": "2025-11-01T17:06:52.182394Z",
|
||||
"issues": []
|
||||
}
|
||||
```
|
||||
|
||||
This mirrors the happy-path asserted in `AttestorVerificationServiceTests.VerifyAsync_ReturnsOk_ForExistingUuid`, which replays the entire submission→verification loop.
|
||||
|
||||
### 4.2 Tampered bundle
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"uuid": "0192fdb4-a82b-7f90-b894-6fd1dd918b85",
|
||||
"index": 73421,
|
||||
"logUrl": "https://rekor.stellaops.test/api/v2/log/entries/0192fdb4a82b7f90b8946fd1dd918b85",
|
||||
"status": "included",
|
||||
"checkedAt": "2025-11-01T17:09:05.443218Z",
|
||||
"issues": [
|
||||
"bundle_hash_mismatch",
|
||||
"signature_invalid"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Derived from `AttestorVerificationServiceTests.VerifyAsync_FlagsTamperedBundle`, which flips the DSSE payload and expects both issues to surface. CLI and Console consumers should display these codes verbatim and provide remediation tips from the table above.
|
||||
|
||||
## 5. Validating the documentation
|
||||
|
||||
- Run `dotnet test src/Attestor/StellaOps.Attestor/StellaOps.Attestor.Tests` to exercise the scenarios behind the examples.
|
||||
- API integrators can `curl` the verify endpoint and compare responses with the JSON above.
|
||||
- UI/CLI teams should ensure explainability tooltips and runbooks reference the same issue catalogue.
|
||||
|
||||
Keeping the documentation aligned with the test suite guarantees explainability remains deterministic and audit-friendly.
|
||||
|
||||
## 6. Offline bundles & air-gapped verification
|
||||
|
||||
Stella Ops Attestor now supports packaging attestations for sealed environments and rehydrating them without calling Rekor:
|
||||
|
||||
- **Export bundles.** `POST /api/v1/attestations:export` accepts either a list of Rekor UUIDs or filter criteria (`subject`, `type`, `issuer`, `scope`, `createdAfter|Before`, `limit`, `continuationToken`) and returns an `attestor.bundle.v1` document. Each item contains the attestation entry, canonical DSSE payload (base64), optional proof payload, and metadata. Responses include a `continuationToken` so callers can page through large result sets (limits default to 100 and are capped at 200). JSON content is required and requests are gated by the `attestor.read` scope.
|
||||
- **Import bundles.** `POST /api/v1/attestations:import` ingests the bundle document, upserts attestation metadata, and restores the canonical DSSE/proof into the configured archive store. The S3 archive integration must be enabled; the response reports how many entries were imported versus updated, any skipped items, and issue codes (`bundle_payload_invalid_base64`, `bundle_hash_mismatch`, `archive_disabled`, …).
|
||||
- **Offline verification.** When replaying verification without log connectivity, submit the DSSE bundle and set `offline=true` on `POST /api/v1/rekor/verify`. The service reuses imported proofs when present and surfaces deterministic explainability codes (`proof_missing`, `proof_inclusion_missing`, …) instead of attempting Rekor fetches.
|
||||
|
||||
Tests `AttestorBundleServiceTests.ExportAsync_AppliesFiltersAndContinuation`, `AttestationBundleEndpointsTests`, `AttestorVerificationServiceTests.VerifyAsync_OfflineSkipsProofRefreshWhenMissing`, and `AttestorVerificationServiceTests.VerifyAsync_OfflineUsesImportedProof` exercise the exporter/importer, API contracts, and the offline verification path with and without witness data.
|
||||
1. **Ingest**: receive DSSE, validate schema, hash subjects, store envelope + metadata.
|
||||
2. **Verify**: run policy checks (issuer, predicate, transparency optional), compute verification record.
|
||||
3. **Persist**: store verification result with `verificationId`, `attestationId`, `policyVersion`, timestamps.
|
||||
4. **Bulk ops**: batch verify envelopes; export results to timeline/audit logs.
|
||||
5. **Audit**: expose read API for verification records; include determinism hash of inputs.
|
||||
|
||||
49
docs/modules/cli/guides/airgap.md
Normal file
49
docs/modules/cli/guides/airgap.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# CLI Airgap Guide (DOCS-AIRGAP-57-003)
|
||||
|
||||
Offline/air-gapped usage patterns for the Stella CLI.
|
||||
|
||||
## Prerequisites
|
||||
- CLI installed from offline bundle; `local-nugets/` and cached plugins available.
|
||||
- Mirror/Bootstrap bundles staged locally; no external network required.
|
||||
- Set `STELLA_OFFLINE=true` to prevent outbound fetches.
|
||||
|
||||
## Common commands
|
||||
- Validate mirror bundle
|
||||
```bash
|
||||
stella airgap verify-bundle /mnt/media/mirror.tar \
|
||||
--manifest /mnt/media/manifest.json \
|
||||
--trust-root /opt/stella/trust/mirror-root.pem
|
||||
```
|
||||
- Import bundle into local registry
|
||||
```bash
|
||||
stella airgap import --bundle /mnt/media/mirror.tar --generation 12
|
||||
```
|
||||
- Check sealed mode status
|
||||
```bash
|
||||
stella airgap status
|
||||
```
|
||||
- List bundles and staleness
|
||||
```bash
|
||||
stella airgap list --format table
|
||||
```
|
||||
|
||||
## Determinism & offline rules
|
||||
- Commands must succeed without egress; any outbound attempt is a bug—report with logs.
|
||||
- Hashes and signatures are verified locally using bundled trust roots; no OCSP/CRL.
|
||||
- Outputs are stable JSON/NDJSON; timestamps use UTC.
|
||||
|
||||
## Exit codes
|
||||
- `0` success
|
||||
- `2` validation failed (hash/signature mismatch)
|
||||
- `3` sealed-mode violation (unexpected egress attempted)
|
||||
- `4` input/argument error
|
||||
- `>4` unexpected error (inspect logs)
|
||||
|
||||
## Logs
|
||||
- Default stderr structured JSON: includes `tenant`, `bundleId`, `mirrorGeneration`, `sealed` flag.
|
||||
- For audits, use `--log-file /var/log/stella/airgap.log --log-format json`.
|
||||
|
||||
## Tips
|
||||
- Keep bundles on read-only media to avoid hash drift.
|
||||
- Use `--dry-run` to validate without writing to registries.
|
||||
- Pair with `docs/airgap/overview.md` and `docs/airgap/sealing-and-egress.md` for policy context.
|
||||
25
docs/modules/cli/guides/attest.md
Normal file
25
docs/modules/cli/guides/attest.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# CLI Attest Guide (DOCS-ATTEST-74-004)
|
||||
|
||||
How to verify and inspect attestations via CLI.
|
||||
|
||||
## Verify DSSE
|
||||
```bash
|
||||
stella attest verify --envelope bundle.dsse.json --policy policy.json \
|
||||
--root keys/root.pem --transparency-checkpoint checkpoints/rekor.json
|
||||
```
|
||||
- Offline verification uses bundled roots and checkpoints; transparency optional.
|
||||
|
||||
## List attestations
|
||||
```bash
|
||||
stella attest list --tenant default --issuer dev-kms --format table
|
||||
```
|
||||
|
||||
## Show attestation
|
||||
```bash
|
||||
stella attest show --id a1b2c3 --output json
|
||||
```
|
||||
|
||||
## Notes
|
||||
- No network access required in sealed mode.
|
||||
- All commands emit deterministic JSON; timestamps in UTC.
|
||||
- Exit codes: 0 success, 2 verification failed, 4 input error.
|
||||
39
docs/modules/excititor/observability/locker-manifest.md
Normal file
39
docs/modules/excititor/observability/locker-manifest.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Excititor Locker Manifest (OBS-53-001)
|
||||
|
||||
Defines the manifest for evidence snapshots stored in Evidence Locker / sealed-mode bundles.
|
||||
|
||||
## Manifest structure
|
||||
```json
|
||||
{
|
||||
"tenant": "default",
|
||||
"manifestId": "locker:excititor:2025-11-23:0001",
|
||||
"createdAt": "2025-11-23T23:10:00Z",
|
||||
"items": [
|
||||
{
|
||||
"observationId": "vex:obs:sha256:...",
|
||||
"providerId": "ubuntu-csaf",
|
||||
"contentHash": "sha256:...",
|
||||
"linksetId": "CVE-2024-0001:pkg:maven/org.demo/app@1.2.3",
|
||||
"dsseEnvelopeHash": "sha256:...",
|
||||
"provenance": {
|
||||
"source": "mirror|ingest",
|
||||
"mirrorGeneration": 12,
|
||||
"exportCenterManifest": "sha256:..."
|
||||
}
|
||||
}
|
||||
],
|
||||
"merkleRoot": "sha256:...", // over `items[*].contentHash`
|
||||
"signature": null, // populated in OBS-54-001 (DSSE)
|
||||
"metadata": {"sealed": true}
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
- `items` sorted by `observationId`, then `providerId`.
|
||||
- `merkleRoot` uses SHA-256 over concatenated item hashes (stable order above).
|
||||
- `signature` is a DSSE envelope (hash recorded in `dsseEnvelopeHash`) when OBS-54-001 is enabled; otherwise `null`.
|
||||
- Manifests are immutable; version using `manifestId` suffix.
|
||||
|
||||
## Storage and replay
|
||||
- Store manifests alongside payloads in object storage; key prefix: `locker/excititor/<tenant>/<manifestId>`.
|
||||
- Replay tools must verify `merkleRoot` before loading payloads; reject if mismatched.
|
||||
43
docs/modules/excititor/observability/timeline-events.md
Normal file
43
docs/modules/excititor/observability/timeline-events.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Excititor Timeline Events (OBS-52-001)
|
||||
|
||||
Defines the event envelope for evidence timelines emitted by Excititor. All fields are aggregation-only; no consensus/merge logic.
|
||||
|
||||
## Envelope
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "excititor.timeline.v1",
|
||||
"tenant": "default",
|
||||
"eventId": "urn:uuid:...",
|
||||
"timestamp": "2025-11-23T23:10:00Z",
|
||||
"traceId": "beefcafe...",
|
||||
"spanId": "deadb33f...",
|
||||
"source": "excititor.web",
|
||||
"kind": "observation|linkset",
|
||||
"action": "ingest|update|backfill|replay",
|
||||
"observationId": "vex:obs:sha256:...",
|
||||
"linksetId": "CVE-2024-0001:pkg:maven/org.demo/app@1.2.3",
|
||||
"justifications": ["component_not_present"],
|
||||
"conflicts": [
|
||||
{"providerId": "suse-csaf", "status": "fixed", "justification": null}
|
||||
],
|
||||
"evidenceHash": "sha256:...", // content-addressed payload hash
|
||||
"dsseEnvelopeHash": "sha256:...", // if attested (see OBS-54-001)
|
||||
"metadata": {"connector": "ubuntu-csaf", "mirrorGeneration": 12}
|
||||
}
|
||||
```
|
||||
|
||||
## Semantics
|
||||
- `eventId` is stable per write; retries reuse the same ID.
|
||||
- `timestamp` must be UTC; derive from TimeProvider.
|
||||
- `traceId`/`spanId` propagate ingestion traces; if tracing is disabled, set both to `null`.
|
||||
- `kind` + `action` drive downstream storage and alerting.
|
||||
- `evidenceHash` is the raw document hash; `dsseEnvelopeHash` appears only when OBS-54-001 is enabled.
|
||||
|
||||
## Determinism
|
||||
- Sort `justifications` and `conflicts` ascending by providerId/status before emit.
|
||||
- Emit at-most-once per storage write; idempotent consumers rely on `(eventId, tenant)`.
|
||||
|
||||
## Transport
|
||||
- Default topic: `excititor.timeline.v1` (NATS/Redis). Subject includes tenant: `excititor.timeline.v1.<tenant>`.
|
||||
- Payload size should stay <32 KiB; truncate conflict arrays with `truncated=true` flag if needed (keep hash counts deterministic).
|
||||
@@ -37,6 +37,24 @@ Excititor’s evidence APIs now emit first-class OpenTelemetry metrics so Lens,
|
||||
3. **Alerting**: add rules for high guard violation rates, missing signatures, and abnormal chunk bytes/record counts. Tie alerts back to connectors via tenant metadata.
|
||||
4. **Post-deploy checks**: after each release, verify metrics emit by curling `/v1/vex/observations/...` and `/v1/vex/evidence/chunks`, watching the console exporter (dev) or OTLP (prod).
|
||||
|
||||
## SLOs (Sprint 119 – OBS-51-001)
|
||||
|
||||
The following SLOs apply to Excititor evidence read paths when telemetry is enabled. Record them in the shared SLO registry and alert via the platform alertmanager.
|
||||
|
||||
| Surface | SLI | Target | Window | Burn alert | Notes |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `/v1/vex/observations` | p95 latency | ≤ 450 ms | 7d | 2 % over 1h | Measured on successful responses only; tenant scoped. |
|
||||
| `/v1/vex/observations` | freshness | ≥ 99 % within 5 min of upstream ingest | 7d | 5 % over 4h | Derived from arrival minus `createdAt`; requires ingest clocks in UTC. |
|
||||
| `/v1/vex/observations` | signature presence | ≥ 98 % statements with signature present | 7d | 3 % over 24h | Use `excititor.vex.signature.status{status="missing"}`. |
|
||||
| `/v1/vex/evidence/chunks` | p95 stream duration | ≤ 600 ms | 7d | 2 % over 1h | From request start to last NDJSON write; excludes client disconnects. |
|
||||
| `/v1/vex/evidence/chunks` | truncation rate | ≤ 1 % truncated streams | 7d | 1 % over 1h | `excititor.vex.chunks.records` with `truncated=true`. |
|
||||
| AOC guardrail | zero hard violations | 0 | continuous | immediate | Any `excititor.vex.aoc.guard_violations` with severity `error` pages ops. |
|
||||
|
||||
Implementation notes:
|
||||
- Emit latency/freshness SLOs via OTEL views that pre-aggregate by tenant and route to the platform SLO backend; keep bucket boundaries aligned with 50/100/250/450/650/1000 ms.
|
||||
- Freshness SLI derived from ingest timestamps; ensure clocks are synchronized (NTP) and stored in UTC.
|
||||
- For air-gapped deployments without OTEL sinks, scrape console exporter and push to offline Prometheus; same thresholds apply.
|
||||
|
||||
## Related documents
|
||||
|
||||
- `docs/modules/excititor/architecture.md` – API contract, AOC guardrails, connector responsibilities.
|
||||
|
||||
@@ -101,4 +101,29 @@ Response 200:
|
||||
- Determinism: responses sorted by `vulnerabilityId`, then `productKey`; arrays sorted lexicographically.
|
||||
|
||||
## SDK generation
|
||||
- Use this file plus `vex_observations.md` as the source of truth for SDK examples in EXCITITOR-LNM-21-203.
|
||||
- Source of truth for EXCITITOR-LNM-21-203 SDK samples (TypeScript/Go/Python) and OpenAPI snippets.
|
||||
- Suggested generation inputs:
|
||||
- Schema: this doc + `docs/modules/excititor/vex_observations.md` for field semantics.
|
||||
- Auth: bearer token + `X-Stella-Tenant` header (required).
|
||||
- Pagination: `cursor` (opaque) + `limit` (default 200, max 500).
|
||||
- Minimal client example (TypeScript, fetch):
|
||||
```ts
|
||||
const resp = await fetch(
|
||||
`${baseUrl}/v1/vex/observations?` + new URLSearchParams({
|
||||
vulnerabilityId: "CVE-2024-0001",
|
||||
productKey: "pkg:maven/org.demo/app@1.2.3",
|
||||
limit: "100"
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"X-Stella-Tenant": "default"
|
||||
}
|
||||
}
|
||||
);
|
||||
const body = await resp.json();
|
||||
```
|
||||
- Determinism requirements for SDKs:
|
||||
- Preserve server ordering; do not resort items client-side.
|
||||
- Treat `cursor` as opaque; echo it back for next page.
|
||||
- Keep enums case-sensitive as returned by API.
|
||||
|
||||
@@ -31,10 +31,10 @@ OCI=1 scripts/mirror/ci-sign.sh
|
||||
```
|
||||
|
||||
## Temporary dev key (to unblock CI until production key is issued)
|
||||
Use this throwaway Ed25519 key only for non-production runs. Generated 2025-11-23 to replace the previous placeholder; rotate TUF metadata immediately after swapping in the production key.
|
||||
Use this throwaway Ed25519 key only for non-production runs. Generated 2025-11-24 to replace the previous placeholder; rotate TUF metadata immediately after swapping in the production key.
|
||||
|
||||
```
|
||||
MIRROR_SIGN_KEY_B64=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSURqb3pDRVdKVVFUdW1xZ2gyRmZXcVBaemlQbkdaSzRvOFZRTThGYkZCSEcKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
|
||||
MIRROR_SIGN_KEY_B64=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUxGdFMwbjBpMVVueE1maGt0cDNlY1N4WHVxYmcrVFJuaENhS05jaGtTbFIKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
|
||||
```
|
||||
|
||||
**Do not ship with this key.** Set `REQUIRE_PROD_SIGNING=1` for release/tag builds so they fail without the real key. Add the production key as a Gitea secret (`MIRROR_SIGN_KEY_B64`) and rerun the workflow; remove this temporary key block once rotated.
|
||||
|
||||
@@ -208,8 +208,17 @@ All payloads are immutable and include analyzer fingerprints (`scanner.native@sh
|
||||
- **Scopes:** Mutations require `policy:*` scopes corresponding to action; `effective:write` restricted to service identity.
|
||||
- **Tenancy:** All queries filter by `tenant`. Service identity uses `tenant-global` for shared policies; cross-tenant reads prohibited unless `policy:tenant-admin` scope present.
|
||||
- **Secrets:** Configuration loaded via environment variables or sealed secrets; runtime avoids writing secrets to logs.
|
||||
- **Determinism guard:** Static analyzer prevents referencing forbidden namespaces; runtime guard intercepts `DateTime.Now`, `Random`, `Guid`, HTTP clients beyond allow-list.
|
||||
- **Sealed mode:** Global flag disables outbound network except allow-listed internal hosts; watchers fail fast if unexpected egress attempted.
|
||||
- **Determinism guard:** Static analyzer prevents referencing forbidden namespaces; runtime guard intercepts `DateTime.Now`, `Random`, `Guid`, HTTP clients beyond allow-list.
|
||||
- **Sealed mode:** Global flag disables outbound network except allow-listed internal hosts; watchers fail fast if unexpected egress attempted.
|
||||
|
||||
### Determinism enforcement (DOCS-POLICY-DET-01)
|
||||
|
||||
- **Inputs are ordered and frozen:** Selector emits batches sorted deterministically by `(tenant, policyId, vulnerabilityId, productKey, source)` with stable cursors; workers must not resort.
|
||||
- **No ambient randomness or wall clocks:** Policy code relies on injected `TimeProvider`/`IRandom` shims; guards block `DateTime.Now`, `Guid.NewGuid`, `Random` when not injected.
|
||||
- **Immutable evidence:** SBOM/VEX inputs carry content hashes; evaluator treats payloads as read-only and surfaces hashes in logs for replay.
|
||||
- **Side effects prohibited:** Evaluator cannot call external HTTP except allow-listed internal services (Authority, Storage) and must not write files outside temp workspace.
|
||||
- **Replay hash:** Each batch computes `determinismHash = SHA256(policyVersion + batchCursor + inputsHash)`; included in logs and run exports.
|
||||
- **Testing:** Determinism tests run the same batch twice with seeded clock/GUID providers and assert identical outputs + determinismHash; add a test per policy package.
|
||||
|
||||
---
|
||||
|
||||
|
||||
29
docs/modules/policy/samples/advisory-ai-knobs@draft.json
Normal file
29
docs/modules/policy/samples/advisory-ai-knobs@draft.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"knobs": [
|
||||
{
|
||||
"name": "ai_signal_weight",
|
||||
"default_value": 1.2,
|
||||
"min": 0.0,
|
||||
"max": 2.0,
|
||||
"step": 0.1,
|
||||
"description": "Weight applied to Advisory AI signal scores"
|
||||
},
|
||||
{
|
||||
"name": "reachability_boost",
|
||||
"default_value": 0.25,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.05,
|
||||
"description": "Boost when asset is reachable"
|
||||
},
|
||||
{
|
||||
"name": "time_decay_half_life_days",
|
||||
"default_value": 45,
|
||||
"min": 1,
|
||||
"max": 365,
|
||||
"step": 1,
|
||||
"description": "Half-life in days for signal decay"
|
||||
}
|
||||
],
|
||||
"profile_hash": "ADVISORYAIHASH"
|
||||
}
|
||||
16
docs/modules/policy/samples/orchestrator-job@draft.json
Normal file
16
docs/modules/policy/samples/orchestrator-job@draft.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"job_id": "01HZX1QJP6Z3MNA0Q2T3VCPV5K",
|
||||
"tenant_id": "acme",
|
||||
"context_id": "ctx-2025-11-24T10:00:00Z",
|
||||
"policy_profile_hash": "overlay-hash-123",
|
||||
"priority": "high",
|
||||
"requested_at": "2025-11-24T10:00:00Z",
|
||||
"status": "queued",
|
||||
"trace_ref": "4E5C2B5E22F928E846B0EFBC58AA53FC3218C8C172199FF52C7C09244E0C0D30",
|
||||
"determinism_hash": "2C855E80F66D30D5E51C4D9A0441A63C5BB8F04DC1EC537D0ADB7B9357A4C713",
|
||||
"batch_items": [
|
||||
{ "component_purl": "pkg:npm/alpha@1.0.0", "advisory_id": "ADV-1" },
|
||||
{ "component_purl": "pkg:npm/zeta@1.0.0", "advisory_id": "ADV-2" }
|
||||
],
|
||||
"callbacks": { "sse": "sse://events", "nats": "policy.jobs" }
|
||||
}
|
||||
11
docs/modules/policy/samples/policy-batch-context@draft.json
Normal file
11
docs/modules/policy/samples/policy-batch-context@draft.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"policy_profile_hash": "overlay-hash-123",
|
||||
"knobs_version": "knobs-v1",
|
||||
"overlay_hash": "overlay-hash-123",
|
||||
"items": [
|
||||
{ "component_purl": "pkg:npm/lodash@4.17.21", "advisory_id": "ADV-2025-0001" },
|
||||
{ "component_purl": "pkg:npm/left-pad@1.3.0", "advisory_id": "ADV-2025-0002" }
|
||||
],
|
||||
"options": { "include_reachability": true }
|
||||
}
|
||||
32
docs/modules/policy/samples/policy-conflict@draft.json
Normal file
32
docs/modules/policy/samples/policy-conflict@draft.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"conflicts": [
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"snapshot_id": "01HZX3GN4V6KBW1PXJ0K3VXEGT",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"severity_fused": "high",
|
||||
"score": 0.900,
|
||||
"sources": [
|
||||
{ "source": "policy-engine", "weight": 1.050, "severity": "high", "score": 0.945 }
|
||||
],
|
||||
"reason_codes": ["weights-applied", "deterministic-fusion"]
|
||||
},
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"snapshot_id": "01HZX3GN4V6KBW1PXJ0K3VXEGT",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"severity_fused": "medium",
|
||||
"score": 0.600,
|
||||
"sources": [
|
||||
{ "source": "policy-engine", "weight": 1.050, "severity": "medium", "score": 0.630 }
|
||||
],
|
||||
"reason_codes": ["weights-applied", "deterministic-fusion"]
|
||||
}
|
||||
],
|
||||
"resolved_status": null
|
||||
}
|
||||
36
docs/modules/policy/samples/policy-ledger-export@draft.json
Normal file
36
docs/modules/policy/samples/policy-ledger-export@draft.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"manifest": {
|
||||
"export_id": "01HZX2KDRT9Q9K5AZXWPRH62VE",
|
||||
"schema_version": "policy-ledger-export-v1",
|
||||
"generated_at": "2025-11-24T15:00:00Z",
|
||||
"record_count": 2,
|
||||
"sha256": "D4B8C98A2F946D93AFBDE6C4DE6535853A223E108A4A2C389E2C2623D3761C1E"
|
||||
},
|
||||
"records": [
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"job_id": "job-1",
|
||||
"context_id": "ctx",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"status": "violation",
|
||||
"trace_ref": "trace-a",
|
||||
"occurred_at": "2025-11-24T15:00:00Z"
|
||||
},
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"job_id": "job-1",
|
||||
"context_id": "ctx",
|
||||
"component_purl": "pkg:npm/zeta@1.0.0",
|
||||
"advisory_id": "ADV-2",
|
||||
"status": "ok",
|
||||
"trace_ref": "trace-b",
|
||||
"occurred_at": "2025-11-24T15:00:00Z"
|
||||
}
|
||||
],
|
||||
"lines": [
|
||||
"{\"export_id\":\"01HZX2KDRT9Q9K5AZXWPRH62VE\",\"schema_version\":\"policy-ledger-export-v1\",\"generated_at\":\"2025-11-24T15:00:00Z\",\"record_count\":2,\"sha256\":\"D4B8C98A2F946D93AFBDE6C4DE6535853A223E108A4A2C389E2C2623D3761C1E\"}",
|
||||
"{\"tenant_id\":\"acme\",\"job_id\":\"job-1\",\"context_id\":\"ctx\",\"component_purl\":\"pkg:npm/alpha@1.0.0\",\"advisory_id\":\"ADV-1\",\"status\":\"violation\",\"trace_ref\":\"trace-a\",\"occurred_at\":\"2025-11-24T15:00:00Z\"}",
|
||||
"{\"tenant_id\":\"acme\",\"job_id\":\"job-1\",\"context_id\":\"ctx\",\"component_purl\":\"pkg:npm/zeta@1.0.0\",\"advisory_id\":\"ADV-2\",\"status\":\"ok\",\"trace_ref\":\"trace-b\",\"occurred_at\":\"2025-11-24T15:00:00Z\"}"
|
||||
]
|
||||
}
|
||||
30
docs/modules/policy/samples/policy-snapshot@draft.json
Normal file
30
docs/modules/policy/samples/policy-snapshot@draft.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"snapshot_id": "01HZX3GN4V6KBW1PXJ0K3VXEGT",
|
||||
"tenant_id": "acme",
|
||||
"ledger_export_id": "01HZX2KDRT9Q9K5AZXWPRH62VE",
|
||||
"generated_at": "2025-11-24T16:00:00Z",
|
||||
"overlay_hash": "overlay-1",
|
||||
"status_counts": { "violation": 1, "ok": 1 },
|
||||
"records": [
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"job_id": "job-1",
|
||||
"context_id": "ctx",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"status": "violation",
|
||||
"trace_ref": "trace-a",
|
||||
"occurred_at": "2025-11-24T15:00:00Z"
|
||||
},
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"job_id": "job-1",
|
||||
"context_id": "ctx",
|
||||
"component_purl": "pkg:npm/zeta@1.0.0",
|
||||
"advisory_id": "ADV-2",
|
||||
"status": "ok",
|
||||
"trace_ref": "trace-b",
|
||||
"occurred_at": "2025-11-24T15:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event_id": "E7A1F3B0D6F243B4868A6D4B3E7B2AB9",
|
||||
"tenant_id": "acme",
|
||||
"snapshot_id": "01HZX3GN4V6KBW1PXJ0K3VXEGT",
|
||||
"policy_profile_hash": "overlay-hash-123",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"violation_code": "policy.violation.detected",
|
||||
"severity": "high",
|
||||
"status": "violation",
|
||||
"trace_ref": "trace-a",
|
||||
"occurred_at": "2025-11-24T16:00:00Z"
|
||||
}
|
||||
11
docs/modules/policy/samples/policy-worker-result@draft.json
Normal file
11
docs/modules/policy/samples/policy-worker-result@draft.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"job_id": "01HZX1QJP6Z3MNA0Q2T3VCPV5K",
|
||||
"worker_id": "worker-stub",
|
||||
"started_at": "2025-11-24T13:00:00Z",
|
||||
"completed_at": "2025-11-24T13:00:01Z",
|
||||
"result_hash": "5E5A4EFA8C7E9952E4E5E5D9E2B9F3A5D46B13E44CB6E0D7292F7D5CB40CF182",
|
||||
"results": [
|
||||
{ "component_purl": "pkg:npm/alpha@1.0.0", "advisory_id": "ADV-1", "status": "violation", "trace_ref": "F5D9B8717EAB4B0252BE22325771C4F9F8ABAE4E7728F3221E15C5F24A8E8D9F" },
|
||||
{ "component_purl": "pkg:npm/zeta@1.0.0", "advisory_id": "ADV-2", "status": "ok", "trace_ref": "3C75CC86A30B6E230D1DE2D5F08F9B0F5CF75AB1931E47372DC7AC2175BE3F6C" }
|
||||
]
|
||||
}
|
||||
12
docs/modules/policy/samples/severity-fusion@draft.json
Normal file
12
docs/modules/policy/samples/severity-fusion@draft.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"tenant_id": "acme",
|
||||
"snapshot_id": "01HZX3GN4V6KBW1PXJ0K3VXEGT",
|
||||
"component_purl": "pkg:npm/alpha@1.0.0",
|
||||
"advisory_id": "ADV-1",
|
||||
"severity_fused": "high",
|
||||
"score": 0.900,
|
||||
"sources": [
|
||||
{ "source": "policy-engine", "weight": 1.050, "severity": "high", "score": 0.945 }
|
||||
],
|
||||
"reason_codes": ["weights-applied", "deterministic-fusion"]
|
||||
}
|
||||
23
docs/modules/policy/samples/trust-weighting@draft.json
Normal file
23
docs/modules/policy/samples/trust-weighting@draft.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"weights": [
|
||||
{
|
||||
"source": "cartographer",
|
||||
"weight": 1.000,
|
||||
"justification": "default baseline",
|
||||
"updated_at": "2025-11-23T12:00:00Z"
|
||||
},
|
||||
{
|
||||
"source": "scanner",
|
||||
"weight": 0.950,
|
||||
"justification": "prefer curated SBOM sources",
|
||||
"updated_at": "2025-11-23T12:00:00Z"
|
||||
},
|
||||
{
|
||||
"source": "concelier",
|
||||
"weight": 1.050,
|
||||
"justification": "policy engine override",
|
||||
"updated_at": "2025-11-23T12:00:00Z"
|
||||
}
|
||||
],
|
||||
"profile_hash": "D1A5F0A0DEFAULTHASH"
|
||||
}
|
||||
27
docs/modules/policy/schemas/advisory-ai-knobs@draft.json
Normal file
27
docs/modules/policy/schemas/advisory-ai-knobs@draft.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "advisory-ai-knobs@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"knobs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string", "minLength": 1 },
|
||||
"default_value": { "type": "number" },
|
||||
"min": { "type": "number" },
|
||||
"max": { "type": "number" },
|
||||
"step": { "type": "number" },
|
||||
"description": { "type": "string" }
|
||||
},
|
||||
"required": ["name", "default_value", "min", "max", "step", "description"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"profile_hash": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["knobs", "profile_hash"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
51
docs/modules/policy/schemas/orchestrator-job@draft.json
Normal file
51
docs/modules/policy/schemas/orchestrator-job@draft.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "orchestrator-job@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_id": { "type": "string", "minLength": 1 },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"context_id": { "type": "string", "minLength": 1 },
|
||||
"policy_profile_hash": { "type": "string", "minLength": 1 },
|
||||
"priority": { "type": "string", "enum": ["normal", "high", "emergency", "preview"] },
|
||||
"requested_at": { "type": "string", "format": "date-time" },
|
||||
"status": { "type": "string", "minLength": 1 },
|
||||
"trace_ref": { "type": "string", "minLength": 1 },
|
||||
"determinism_hash": { "type": "string", "minLength": 1 },
|
||||
"completed_at": { "type": ["string", "null"], "format": "date-time" },
|
||||
"result_hash": { "type": ["string", "null"] },
|
||||
"batch_items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["component_purl", "advisory_id"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"callbacks": {
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"sse": { "type": ["string", "null"] },
|
||||
"nats": { "type": ["string", "null"] }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"job_id",
|
||||
"tenant_id",
|
||||
"context_id",
|
||||
"policy_profile_hash",
|
||||
"priority",
|
||||
"requested_at",
|
||||
"status",
|
||||
"determinism_hash",
|
||||
"batch_items"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
41
docs/modules/policy/schemas/policy-batch-context@draft.json
Normal file
41
docs/modules/policy/schemas/policy-batch-context@draft.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "policy-batch-context@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"policy_profile_hash": { "type": "string", "minLength": 1 },
|
||||
"knobs_version": { "type": "string", "minLength": 1 },
|
||||
"overlay_hash": { "type": "string", "minLength": 1 },
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["component_purl", "advisory_id"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"include_reachability": { "type": "boolean" }
|
||||
},
|
||||
"required": ["include_reachability"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tenant_id",
|
||||
"policy_profile_hash",
|
||||
"knobs_version",
|
||||
"overlay_hash",
|
||||
"items",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
17
docs/modules/policy/schemas/policy-conflict@draft.json
Normal file
17
docs/modules/policy/schemas/policy-conflict@draft.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "policy-conflict@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 },
|
||||
"conflicts": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "severity-fusion@draft.json" }
|
||||
},
|
||||
"resolved_status": { "type": ["string", "null"] }
|
||||
},
|
||||
"required": ["tenant_id", "component_purl", "advisory_id", "conflicts"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
40
docs/modules/policy/schemas/policy-ledger-export@draft.json
Normal file
40
docs/modules/policy/schemas/policy-ledger-export@draft.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "policy-ledger-export@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"export_id": { "type": "string", "minLength": 1 },
|
||||
"schema_version": { "type": "string", "minLength": 1 },
|
||||
"generated_at": { "type": "string", "format": "date-time" },
|
||||
"record_count": { "type": "integer", "minimum": 0 },
|
||||
"sha256": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["export_id", "schema_version", "generated_at", "record_count", "sha256"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"records": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"job_id": { "type": "string", "minLength": 1 },
|
||||
"context_id": { "type": "string", "minLength": 1 },
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 },
|
||||
"status": { "type": "string", "minLength": 1 },
|
||||
"trace_ref": { "type": "string", "minLength": 1 },
|
||||
"occurred_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"required": ["tenant_id", "job_id", "context_id", "component_purl", "advisory_id", "status", "trace_ref", "occurred_at"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"lines": { "type": "array", "items": { "type": "string" } }
|
||||
},
|
||||
"required": ["manifest", "records", "lines"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
33
docs/modules/policy/schemas/policy-snapshot@draft.json
Normal file
33
docs/modules/policy/schemas/policy-snapshot@draft.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "policy-snapshot@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"snapshot_id": { "type": "string", "minLength": 1 },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"ledger_export_id": { "type": "string", "minLength": 1 },
|
||||
"generated_at": { "type": "string", "format": "date-time" },
|
||||
"overlay_hash": { "type": "string", "minLength": 1 },
|
||||
"status_counts": { "type": "object", "additionalProperties": { "type": "integer" } },
|
||||
"records": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tenant_id": { "type": "string" },
|
||||
"job_id": { "type": "string" },
|
||||
"context_id": { "type": "string" },
|
||||
"component_purl": { "type": "string" },
|
||||
"advisory_id": { "type": "string" },
|
||||
"status": { "type": "string" },
|
||||
"trace_ref": { "type": "string" },
|
||||
"occurred_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"required": ["tenant_id", "job_id", "context_id", "component_purl", "advisory_id", "status", "trace_ref", "occurred_at"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["snapshot_id", "tenant_id", "ledger_export_id", "generated_at", "overlay_hash", "status_counts", "records"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "policy-violation-event@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"event_id": { "type": "string", "minLength": 1 },
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"snapshot_id": { "type": "string", "minLength": 1 },
|
||||
"policy_profile_hash": { "type": "string", "minLength": 1 },
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 },
|
||||
"violation_code": { "type": "string", "minLength": 1 },
|
||||
"severity": { "type": "string", "minLength": 1 },
|
||||
"status": { "type": "string", "minLength": 1 },
|
||||
"trace_ref": { "type": "string", "minLength": 1 },
|
||||
"occurred_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"required": [
|
||||
"event_id",
|
||||
"tenant_id",
|
||||
"snapshot_id",
|
||||
"policy_profile_hash",
|
||||
"component_purl",
|
||||
"advisory_id",
|
||||
"violation_code",
|
||||
"severity",
|
||||
"status",
|
||||
"trace_ref",
|
||||
"occurred_at"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
29
docs/modules/policy/schemas/policy-worker-result@draft.json
Normal file
29
docs/modules/policy/schemas/policy-worker-result@draft.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "policy-worker-result@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_id": { "type": "string", "minLength": 1 },
|
||||
"worker_id": { "type": "string", "minLength": 1 },
|
||||
"started_at": { "type": "string", "format": "date-time" },
|
||||
"completed_at": { "type": "string", "format": "date-time" },
|
||||
"result_hash": { "type": "string", "minLength": 1 },
|
||||
"results": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 },
|
||||
"status": { "type": "string", "minLength": 1 },
|
||||
"trace_ref": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["component_purl", "advisory_id", "status", "trace_ref"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"required": ["job_id", "worker_id", "started_at", "completed_at", "result_hash", "results"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
30
docs/modules/policy/schemas/severity-fusion@draft.json
Normal file
30
docs/modules/policy/schemas/severity-fusion@draft.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "severity-fusion@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tenant_id": { "type": "string", "minLength": 1 },
|
||||
"snapshot_id": { "type": "string", "minLength": 1 },
|
||||
"component_purl": { "type": "string", "minLength": 1 },
|
||||
"advisory_id": { "type": "string", "minLength": 1 },
|
||||
"severity_fused": { "type": "string", "minLength": 1 },
|
||||
"score": { "type": "number" },
|
||||
"sources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": { "type": "string", "minLength": 1 },
|
||||
"weight": { "type": "number" },
|
||||
"severity": { "type": "string", "minLength": 1 },
|
||||
"score": { "type": "number" }
|
||||
},
|
||||
"required": ["source", "weight", "severity", "score"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"reason_codes": { "type": "array", "items": { "type": "string" } }
|
||||
},
|
||||
"required": ["tenant_id", "snapshot_id", "component_purl", "advisory_id", "severity_fused", "score", "sources", "reason_codes"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
25
docs/modules/policy/schemas/trust-weighting@draft.json
Normal file
25
docs/modules/policy/schemas/trust-weighting@draft.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "trust-weighting@draft",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"weights": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": { "type": "string", "minLength": 1 },
|
||||
"weight": { "type": "number" },
|
||||
"justification": { "type": ["string", "null"] },
|
||||
"updated_at": { "type": "string", "format": "date-time" }
|
||||
},
|
||||
"required": ["source", "weight", "updated_at"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"profile_hash": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["weights", "profile_hash"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -44,8 +44,16 @@ Operational rules:
|
||||
- `GET /console/sboms` — Console catalog with filters (artifact, license, scope, asset tags), cursor pagination, evaluation metadata, immutable JSON projection for drawer views.
|
||||
- `GET /components/lookup?purl=...` — component neighborhood for global search/Graph overlays; returns caches hints + tenant enforcement.
|
||||
- `POST /entrypoints` / `GET /entrypoints` — manage entrypoint/service node overrides feeding Cartographer relevance; deterministic defaults when unset.
|
||||
- `GET /sboms/{snapshotId}/projection` — Link-Not-Merge v1 projection returning hashes plus asset metadata (criticality, owner, environment, exposure flags, tags) alongside package/component graph.
|
||||
- `GET /internal/sbom/events` — internal diagnostics endpoint returning the in-memory event outbox for validation.
|
||||
- `POST /internal/sbom/events/backfill` — replays existing projections into the event stream; deterministic ordering, clock abstraction for tests.
|
||||
- `GET /internal/sbom/asset-events` — diagnostics endpoint returning emitted `sbom.asset.updated` envelopes for validation and air-gap parity checks.
|
||||
- `GET/POST /internal/orchestrator/sources` — list/register orchestrator ingest/index sources (deterministic seeds; idempotent on artifactDigest+sourceType).
|
||||
- `GET/POST /internal/orchestrator/control` — manage pause/throttle/backpressure signals per tenant; metrics emitted for control updates.
|
||||
- `GET/POST /internal/orchestrator/watermarks` — fetch/set backfill watermarks for reconciliation and deterministic replays.
|
||||
- `GET /internal/sbom/resolver-feed` — list resolver candidates (artifact, purl, version, paths, scope, runtime_flag, nearest_safe_version).
|
||||
- `POST /internal/sbom/resolver-feed/backfill` — clear and repopulate resolver feed from current projections.
|
||||
- `GET /internal/sbom/resolver-feed/export` — NDJSON export of resolver candidates for air-gap delivery.
|
||||
|
||||
## 4) Ingestion & orchestrator integration
|
||||
- Ingest sources: Scanner pipeline (preferred) or uploaded SPDX 3.0.1/CycloneDX 1.6 bundles.
|
||||
@@ -70,7 +78,8 @@ Operational rules:
|
||||
- Input validation: schema-validate incoming SBOMs; reject oversized/unsupported media types early.
|
||||
|
||||
## 8) Observability
|
||||
- Metrics: `sbom_projection_seconds`, `sbom_projection_size_bytes`, `sbom_paths_latency_seconds`, `sbom_paths_cache_hit_ratio`, `sbom_events_backlog`.
|
||||
- Metrics: `sbom_projection_seconds`, `sbom_projection_size_bytes`, `sbom_projection_queries_total`, `sbom_paths_latency_seconds`, `sbom_paths_cache_hit_ratio`, `sbom_events_backlog`.
|
||||
- Tracing: ActivitySource `StellaOps.SbomService` (entrypoints, component lookup, console catalog, projections, events).
|
||||
- Traces: wrap ingest, projection build, and API handlers; propagate orchestrator job IDs.
|
||||
- Logs: structured, include tenant + artifact digest + sbomVersion; classify ingest failures (schema, storage, orchestrator, validation).
|
||||
- Alerts: backlog thresholds for outbox/event delivery; high latency on path/timeline endpoints.
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"snapshotId":"snap-001","tenantId":"tenant-a","projection":{"purl":"pkg:npm/lodash@4.17.21","paths":[],"metadata":{"schemaVersion":"1.0.0"}}}]
|
||||
[{"snapshotId":"snap-001","tenantId":"tenant-a","projection":{"purl":"pkg:npm/lodash@4.17.21","paths":[],"metadata":{"schemaVersion":"1.0.0","asset":{"criticality":"high","owner":"team-console","environment":"prod","exposure":["internet","pci"],"tags":{"tier":"1","service":"sample-api"}}}}}]
|
||||
|
||||
Reference in New Issue
Block a user