feat(graph): introduce graph.inspect.v1 contract and schema for SBOM relationships
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Console CI / console-ci (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled

- Added graph.inspect.v1 documentation outlining payload structure and determinism rules.
- Created JSON schema for graph.inspect.v1 to enforce payload validation.
- Defined mapping rules for graph relationships, advisories, and VEX statements.

feat(notifications): establish remediation blueprint for gaps NR1-NR10

- Documented requirements, evidence, and tests for Notifier runtime.
- Specified deliverables and next steps for addressing identified gaps.

docs(notifications): organize operations and schemas documentation

- Created README files for operations, schemas, and security notes to clarify deliverables and policies.

feat(advisory): implement PostgreSQL caching for Link-Not-Merge linksets

- Created database schema for advisory linkset cache.
- Developed repository for managing advisory linkset cache operations.
- Added tests to ensure correct functionality of the AdvisoryLinksetCacheRepository.
This commit is contained in:
StellaOps Bot
2025-12-04 09:36:59 +02:00
parent 4dc7cf834a
commit 600f3a7a3c
37 changed files with 1326 additions and 272 deletions

View File

@@ -0,0 +1,86 @@
{
"schemaVersion": "graph.inspect.v1",
"tenant": "acme-dev",
"artifactDigest": "sha256:8f2c1f4c8f9d4c3bb2efc0a9d0a35d4492a0bba4f3c1a2b9d5c7e1f4a8c6b2d1",
"sbomDigest": "sha256:1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
"collectedAt": "2025-12-04T15:30:00Z",
"components": [
{
"purl": "pkg:maven/org.example/foo@1.2.3",
"version": "1.2.3",
"scopes": [
"runtime"
],
"relationships": [
{
"type": "contains",
"targetPurl": "pkg:docker/library/alpine@3.19.0",
"scope": "runtime",
"evidenceHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd",
"source": "scanner.sbom.v1"
},
{
"type": "depends_on",
"targetPurl": "pkg:npm/lodash@4.17.21",
"scope": "runtime",
"evidenceHash": "89abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345",
"source": "concelier.linkset.v1"
}
],
"advisories": [
{
"advisoryId": "CVE-2024-1111",
"source": "ghsa",
"status": "affected",
"severity": "HIGH",
"cvss": {
"vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"score": 9.8
},
"justification": "exploitable_in_default_config",
"justificationSummary": "Unauthenticated RCE in JSON parser; no mitigations applied.",
"linksetDigest": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"evidenceHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"modifiedAt": "2025-11-30T12:00:00Z",
"provenance": {
"source": "concelier.linkset.v1",
"collectedAt": "2025-11-30T11:55:00Z",
"eventOffset": 4421,
"linksetDigest": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"evidenceHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
}
}
],
"vexStatements": [
{
"statementId": "VEX-2025-0001",
"source": "excitor.vex.v1",
"status": "not_affected",
"justification": "component_not_present",
"impactStatement": "Library excluded from production image; only used in tests.",
"knownExploited": false,
"issuedAt": "2025-12-01T08:00:00Z",
"expiresAt": "2026-12-01T00:00:00Z",
"evidenceHash": "0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
"provenance": {
"source": "excitor.overlay.v1",
"collectedAt": "2025-12-01T08:00:00Z",
"eventOffset": 171,
"evidenceHash": "0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0"
}
}
],
"provenance": {
"source": "concelier.linkset.v1",
"collectedAt": "2025-12-04T15:29:00Z",
"eventOffset": 5123,
"linksetDigest": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"evidenceHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"
}
}
],
"links": {
"sbomObservationEventId": "obs-2025-11-22-001",
"linksetDigest": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd"
}
}

View File

@@ -0,0 +1,48 @@
# graph.inspect.v1 · Inspector Contract (2025-12-04)
Replaces the archived Cartographer handshake note and grounds the Concelier/Excititor → Graph Indexer/API interface. Use this shape for any inspector payloads that carry SBOM relationships plus advisory/VEX linkouts destined for graph ingestion or replay.
## Purpose
- Give Concelier (Link-Not-Merge v1) and Excititor a frozen, deterministic payload so graph nodes/edges can be regenerated or replayed offline.
- Encode the evidence hashes and provenance needed for append-only rebuilds and audit trails.
- Keep ordering stable to avoid hash churn between runs.
## Payload shape (summary)
- `schemaVersion` — must be `graph.inspect.v1`.
- `tenant`, `artifactDigest`, `sbomDigest`, `collectedAt`.
- `components[]` (ordered):
- `purl`, optional `version`, optional sorted `scopes[]`.
- `relationships[]``contains|depends_on|provides|runtime_observed` with `targetPurl`, `scope?`, `source`, `evidenceHash`.
- `advisories[]``advisoryId`, `source`, `status`, `severity?`, `cvss?`, `justification?`, `linksetDigest?`, `evidenceHash`, `modifiedAt`.
- `vexStatements[]``statementId`, `source`, `status`, `justification`, `impactStatement?`, `knownExploited?`, `issuedAt`, `expiresAt?`, `evidenceHash`.
- `provenance``source`, `collectedAt`, `eventOffset?`, `linksetDigest?`, `evidenceHash?`.
- Optional `links.sbomObservationEventId` and `links.linksetDigest` for traceability.
Full machine-readable schema: `docs/modules/graph/contracts/graph.inspect.v1.schema.json`.
## Determinism rules
- Preserve input order for `components[]` and `relationships[]`; sort `scopes`, `advisories`, and `vexStatements` by (`source`, `advisoryId`/`statementId`) before emitting.
- All timestamps UTC ISO-8601; hashes are SHA-256 hex.
- Keep arrays unique where noted (`scopes`), and avoid null/empty strings.
## Graph mapping
- `relationships` → graph edges: `contains/depends_on/provides/runtime_observed` align with `CONTAINS`, `DEPENDS_ON`, `PROVIDES`, `OBSERVED_RUNTIME`.
- `advisories``advisory` nodes + `AFFECTED_BY` edges (use `linksetDigest` and `evidenceHash` for provenance).
- `vexStatements``vex_statement` nodes + `VEX_EXEMPTS` edges (propagate `justification`, `knownExploited`, `impactStatement`).
- `provenance` fields flow into graph `provenance` objects and `eventOffset` becomes the replay cursor.
## Validation
```sh
# Example (offline-friendly if ajv is already installed locally)
ajv validate -s docs/modules/graph/contracts/graph.inspect.v1.schema.json \
-d docs/modules/graph/contracts/examples/graph.inspect.v1.sample.json
```
Use any JSON Schema Draft 2020-12 validator if `ajv` is unavailable.
## Sample
See `docs/modules/graph/contracts/examples/graph.inspect.v1.sample.json` for a one-component payload that exercises relationships, advisory observations, VEX statements, and provenance fields.
## Upstream alignment
- Link-Not-Merge fixtures: `docs/modules/concelier/link-not-merge-schema.md`.
- Excititor overlay prep: `docs/modules/excititor/prep/2025-11-20-graph-21-001-prep.md` and `...graph-21-002-prep.md`.
- Graph canonical model: `docs/modules/graph/schema.md` (node/edge taxonomy the inspector feeds).

View File

@@ -0,0 +1,217 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.org/schemas/graph.inspect.v1.schema.json",
"title": "graph.inspect.v1",
"description": "Inspector payload consumed by the Graph Indexer/API. Carries SBOM relationships plus advisory/VEX linkouts with deterministic ordering so downstream graph nodes/edges can be rebuilt or replayed offline.",
"type": "object",
"additionalProperties": false,
"properties": {
"schemaVersion": {
"const": "graph.inspect.v1",
"description": "Version marker; reject if not exactly graph.inspect.v1."
},
"tenant": {
"type": "string",
"minLength": 1,
"description": "Tenant identifier applied to all nested records."
},
"artifactDigest": {
"type": "string",
"pattern": "^[A-Za-z0-9_.:+-]{8,}$",
"description": "Primary artifact digest (e.g., image digest) that the inspector payload refers to."
},
"sbomDigest": {
"type": "string",
"pattern": "^[A-Za-z0-9_.:+-]{8,}$",
"description": "SBOM digest or manifest hash that produced the relationships below."
},
"collectedAt": {
"type": "string",
"format": "date-time",
"description": "UTC ISO-8601 timestamp when the inspector snapshot was produced."
},
"components": {
"type": "array",
"minItems": 1,
"description": "Component records kept in the same order as the upstream feed (stable ordering expected).",
"items": { "$ref": "#/$defs/component" }
},
"links": {
"type": "object",
"additionalProperties": false,
"description": "Optional pointers for traceability (event ids, evidence URLs).",
"properties": {
"sbomObservationEventId": { "type": "string" },
"linksetDigest": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$",
"description": "Optional Concelier linkset digest that seeded this payload."
}
}
}
},
"required": [
"schemaVersion",
"tenant",
"artifactDigest",
"sbomDigest",
"collectedAt",
"components"
],
"$defs": {
"component": {
"type": "object",
"additionalProperties": false,
"required": ["purl", "relationships", "provenance"],
"properties": {
"purl": {
"type": "string",
"description": "Normalized PURL for the component."
},
"version": {
"type": "string",
"description": "Optional explicit version if not derivable from the PURL."
},
"scopes": {
"type": "array",
"description": "Sorted, unique scopes such as runtime/build/dev.",
"items": { "type": "string" },
"uniqueItems": true
},
"relationships": {
"type": "array",
"minItems": 1,
"description": "SBOM relationships that map to graph edges.",
"items": { "$ref": "#/$defs/relationship" }
},
"advisories": {
"type": "array",
"description": "Advisory observations used to emit AFFECTED_BY edges.",
"items": { "$ref": "#/$defs/advisoryObservation" }
},
"vexStatements": {
"type": "array",
"description": "VEX statements used to emit VEX_EXEMPTS edges.",
"items": { "$ref": "#/$defs/vexStatement" }
},
"provenance": { "$ref": "#/$defs/provenance" }
}
},
"relationship": {
"type": "object",
"additionalProperties": false,
"required": ["type", "targetPurl", "source", "evidenceHash"],
"properties": {
"type": {
"type": "string",
"enum": ["contains", "depends_on", "provides", "runtime_observed"],
"description": "Relationship kind; aligns to graph edge taxonomy."
},
"targetPurl": {
"type": "string",
"description": "Normalized PURL of the target component."
},
"scope": {
"type": "string",
"description": "Scope label propagated to graph edges (e.g., runtime, build)."
},
"evidenceHash": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$",
"description": "SHA-256 of the evidence payload referenced by this relationship."
},
"source": {
"type": "string",
"description": "Provenance source for the relationship (e.g., concelier.linkset.v1, scanner.sbom.v1)."
}
}
},
"advisoryObservation": {
"type": "object",
"additionalProperties": false,
"required": ["advisoryId", "source", "status", "modifiedAt", "evidenceHash"],
"properties": {
"advisoryId": { "type": "string" },
"source": {
"type": "string",
"description": "Advisory source/provider (nvd, ghsa, distro id, etc.)."
},
"status": {
"type": "string",
"enum": ["affected", "not_affected", "fixed", "under_investigation", "unknown"]
},
"severity": { "type": "string" },
"cvss": {
"type": "object",
"additionalProperties": false,
"properties": {
"vector": { "type": "string" },
"score": { "type": "number" }
}
},
"justification": { "type": "string" },
"justificationSummary": { "type": "string" },
"linksetDigest": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$"
},
"evidenceHash": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$"
},
"modifiedAt": {
"type": "string",
"format": "date-time"
},
"provenance": { "$ref": "#/$defs/provenance" }
}
},
"vexStatement": {
"type": "object",
"additionalProperties": false,
"required": ["statementId", "status", "justification", "issuedAt", "evidenceHash", "source"],
"properties": {
"statementId": { "type": "string" },
"source": { "type": "string" },
"status": {
"type": "string",
"enum": ["not_affected", "affected", "under_investigation", "fixed"]
},
"justification": { "type": "string" },
"impactStatement": { "type": "string" },
"knownExploited": { "type": "boolean" },
"issuedAt": {
"type": "string",
"format": "date-time"
},
"expiresAt": {
"type": "string",
"format": "date-time"
},
"evidenceHash": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$"
},
"provenance": { "$ref": "#/$defs/provenance" }
}
},
"provenance": {
"type": "object",
"additionalProperties": false,
"required": ["source", "collectedAt"],
"properties": {
"source": { "type": "string" },
"collectedAt": { "type": "string", "format": "date-time" },
"eventOffset": { "type": "integer", "minimum": 0 },
"linksetDigest": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$"
},
"evidenceHash": {
"type": "string",
"pattern": "^[A-Fa-f0-9]{64}$"
}
}
}
}
}