Files
StellaOps Bot 6a299d231f
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Add unit tests for Router configuration and transport layers
- Implemented tests for RouterConfig, RoutingOptions, StaticInstanceConfig, and RouterConfigOptions to ensure default values are set correctly.
- Added tests for RouterConfigProvider to validate configurations and ensure defaults are returned when no file is specified.
- Created tests for ConfigValidationResult to check success and error scenarios.
- Developed tests for ServiceCollectionExtensions to verify service registration for RouterConfig.
- Introduced UdpTransportTests to validate serialization, connection, request-response, and error handling in UDP transport.
- Added scripts for signing authority gaps and hashing DevPortal SDK snippets.
2025-12-05 08:01:47 +02:00

246 lines
10 KiB
SQL

-- Generated from docs/db/SPECIFICATION.md §5.3 (2025-11-28)
CREATE SCHEMA IF NOT EXISTS vex;
CREATE TABLE IF NOT EXISTS vex.projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
key TEXT NOT NULL,
display_name TEXT NOT NULL,
description TEXT,
settings JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (tenant_id, key)
);
CREATE TABLE IF NOT EXISTS vex.graph_revisions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES vex.projects(id),
revision_id TEXT NOT NULL UNIQUE,
parent_revision_id TEXT,
sbom_hash TEXT NOT NULL,
sbom_format TEXT NOT NULL CHECK (sbom_format IN ('cyclonedx', 'spdx', 'syft', 'other')),
sbom_location TEXT,
feed_snapshot_id UUID,
lattice_policy_version TEXT,
unknowns_snapshot_id UUID,
node_count INT NOT NULL DEFAULT 0,
edge_count INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT,
notes TEXT
);
CREATE TABLE IF NOT EXISTS vex.graph_nodes (
id BIGSERIAL PRIMARY KEY,
graph_revision_id UUID NOT NULL REFERENCES vex.graph_revisions(id) ON DELETE CASCADE,
node_key TEXT NOT NULL,
node_type TEXT NOT NULL CHECK (node_type IN ('component', 'vulnerability', 'runtime_entity', 'file', 'package', 'service')),
purl TEXT,
name TEXT,
version TEXT,
attributes JSONB DEFAULT '{}',
UNIQUE (graph_revision_id, node_key)
);
CREATE TABLE IF NOT EXISTS vex.graph_edges (
id BIGSERIAL PRIMARY KEY,
graph_revision_id UUID NOT NULL REFERENCES vex.graph_revisions(id) ON DELETE CASCADE,
from_node_id BIGINT NOT NULL REFERENCES vex.graph_nodes(id) ON DELETE CASCADE,
to_node_id BIGINT NOT NULL REFERENCES vex.graph_nodes(id) ON DELETE CASCADE,
edge_type TEXT NOT NULL CHECK (edge_type IN (
'depends_on', 'dev_depends_on', 'optional_depends_on',
'contains', 'introduces', 'mitigates', 'affects',
'build_tool', 'test_dependency'
)),
attributes JSONB DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS vex.statements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
project_id UUID REFERENCES vex.projects(id),
graph_revision_id UUID REFERENCES vex.graph_revisions(id),
advisory_id UUID,
vulnerability_id TEXT NOT NULL,
subject_node_id BIGINT REFERENCES vex.graph_nodes(id),
product_key TEXT,
status TEXT NOT NULL CHECK (status IN ('affected', 'not_affected', 'under_investigation', 'fixed')),
status_justification TEXT CHECK (status_justification IN (
'component_not_present', 'vulnerable_code_not_present',
'vulnerable_code_not_in_execute_path', 'vulnerable_code_cannot_be_controlled_by_adversary',
'inline_mitigations_already_exist', NULL
)),
impact_statement TEXT,
action_statement TEXT,
action_statement_timestamp TIMESTAMPTZ,
evidence JSONB DEFAULT '{}',
provenance JSONB DEFAULT '{}',
evaluated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
evaluated_by TEXT,
superseded_by UUID REFERENCES vex.statements(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS vex.observations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
provider_id TEXT NOT NULL,
vulnerability_id TEXT NOT NULL,
product_key TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('affected', 'not_affected', 'under_investigation', 'fixed')),
status_justification TEXT,
content_hash TEXT NOT NULL,
linkset_id UUID,
dsse_envelope_hash TEXT,
provenance JSONB DEFAULT '{}',
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ,
UNIQUE (tenant_id, provider_id, vulnerability_id, product_key, content_hash)
);
CREATE TABLE IF NOT EXISTS vex.linksets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
linkset_id TEXT NOT NULL,
provider_id TEXT NOT NULL,
sbom_digest TEXT,
vex_digest TEXT,
sbom_location TEXT,
vex_location TEXT,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'superseded', 'revoked')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
metadata JSONB DEFAULT '{}',
UNIQUE (tenant_id, linkset_id)
);
CREATE TABLE IF NOT EXISTS vex.linkset_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
linkset_id UUID NOT NULL REFERENCES vex.linksets(id),
event_type TEXT NOT NULL CHECK (event_type IN ('created', 'updated', 'superseded', 'revoked')),
details JSONB DEFAULT '{}',
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS vex.consensus (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
vulnerability_id TEXT NOT NULL,
product_key TEXT NOT NULL,
computed_status TEXT NOT NULL CHECK (computed_status IN ('affected', 'not_affected', 'under_investigation', 'fixed', 'conflict')),
confidence_score NUMERIC(3,2) CHECK (confidence_score >= 0 AND confidence_score <= 1),
contributing_observations UUID[] DEFAULT '{}',
conflict_details JSONB,
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (tenant_id, vulnerability_id, product_key)
);
CREATE TABLE IF NOT EXISTS vex.consensus_holds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
consensus_id UUID NOT NULL REFERENCES vex.consensus(id),
hold_type TEXT NOT NULL CHECK (hold_type IN ('manual_review', 'conflict_resolution', 'policy_override')),
reason TEXT NOT NULL,
placed_by TEXT NOT NULL,
placed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
released_at TIMESTAMPTZ,
released_by TEXT
);
CREATE TABLE IF NOT EXISTS vex.unknowns_snapshots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES vex.projects(id),
graph_revision_id UUID REFERENCES vex.graph_revisions(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT,
rationale TEXT,
item_count INT NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS vex.unknown_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
snapshot_id UUID NOT NULL REFERENCES vex.unknowns_snapshots(id) ON DELETE CASCADE,
item_key TEXT NOT NULL,
item_type TEXT NOT NULL CHECK (item_type IN (
'missing_sbom', 'ambiguous_package', 'missing_feed',
'unresolved_edge', 'no_version_info', 'unknown_ecosystem'
)),
severity TEXT CHECK (severity IN ('critical', 'high', 'medium', 'low', 'info')),
details JSONB DEFAULT '{}',
resolved_at TIMESTAMPTZ,
resolution TEXT
);
CREATE TABLE IF NOT EXISTS vex.evidence_manifests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
manifest_id TEXT NOT NULL UNIQUE,
merkle_root TEXT NOT NULL,
signature TEXT,
signer_id TEXT,
sealed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
item_count INT NOT NULL DEFAULT 0,
items JSONB NOT NULL DEFAULT '[]',
metadata JSONB DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS vex.cvss_receipts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
statement_id UUID NOT NULL REFERENCES vex.statements(id),
cvss_metric_id UUID,
cvss_version TEXT NOT NULL,
vector TEXT NOT NULL,
score_used NUMERIC(3,1) NOT NULL,
context JSONB DEFAULT '{}',
scored_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS vex.attestations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
statement_id UUID REFERENCES vex.statements(id),
graph_revision_id UUID REFERENCES vex.graph_revisions(id),
attestation_type TEXT NOT NULL CHECK (attestation_type IN ('in-toto', 'dsse', 'sigstore')),
envelope_hash TEXT NOT NULL,
rekor_log_id TEXT,
rekor_log_index BIGINT,
signer_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
metadata JSONB DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS vex.timeline_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
project_id UUID REFERENCES vex.projects(id),
event_type TEXT NOT NULL,
entity_type TEXT NOT NULL,
entity_id UUID NOT NULL,
actor TEXT,
details JSONB DEFAULT '{}',
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_projects_tenant ON vex.projects(tenant_id);
CREATE INDEX IF NOT EXISTS idx_graph_revisions_project ON vex.graph_revisions(project_id);
CREATE INDEX IF NOT EXISTS idx_graph_revisions_sbom ON vex.graph_revisions(sbom_hash);
CREATE INDEX IF NOT EXISTS idx_graph_nodes_revision ON vex.graph_nodes(graph_revision_id);
CREATE INDEX IF NOT EXISTS idx_graph_nodes_purl ON vex.graph_nodes(purl) WHERE purl IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_graph_edges_revision ON vex.graph_edges(graph_revision_id);
CREATE INDEX IF NOT EXISTS idx_graph_edges_from ON vex.graph_edges(from_node_id);
CREATE INDEX IF NOT EXISTS idx_graph_edges_to ON vex.graph_edges(to_node_id);
CREATE INDEX IF NOT EXISTS idx_statements_tenant_vuln ON vex.statements(tenant_id, vulnerability_id);
CREATE INDEX IF NOT EXISTS idx_statements_project ON vex.statements(project_id);
CREATE INDEX IF NOT EXISTS idx_statements_graph ON vex.statements(graph_revision_id);
CREATE INDEX IF NOT EXISTS idx_observations_tenant_vuln ON vex.observations(tenant_id, vulnerability_id);
CREATE INDEX IF NOT EXISTS idx_observations_provider ON vex.observations(provider_id);
CREATE INDEX IF NOT EXISTS idx_linksets_tenant ON vex.linksets(tenant_id);
CREATE INDEX IF NOT EXISTS idx_consensus_tenant_vuln ON vex.consensus(tenant_id, vulnerability_id);
CREATE INDEX IF NOT EXISTS idx_unknowns_project ON vex.unknowns_snapshots(project_id);
CREATE INDEX IF NOT EXISTS idx_attestations_tenant ON vex.attestations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_attestations_rekor ON vex.attestations(rekor_log_id) WHERE rekor_log_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_timeline_tenant_time ON vex.timeline_events(tenant_id, occurred_at DESC);
CREATE INDEX IF NOT EXISTS idx_timeline_entity ON vex.timeline_events(entity_type, entity_id);