Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly. - Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps. - Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges. - Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges. - Set up project file for the test project with necessary dependencies and configurations. - Include JSON fixture files for testing purposes.
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
-- 001_initial.sql
|
||||
-- Findings Ledger bootstrap schema (LEDGER-29-001)
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TYPE ledger_event_type AS ENUM (
|
||||
'finding.created',
|
||||
'finding.status_changed',
|
||||
'finding.severity_changed',
|
||||
'finding.tag_updated',
|
||||
'finding.comment_added',
|
||||
'finding.assignment_changed',
|
||||
'finding.accepted_risk',
|
||||
'finding.remediation_plan_added',
|
||||
'finding.attachment_added',
|
||||
'finding.closed'
|
||||
);
|
||||
|
||||
CREATE TYPE ledger_action_type AS ENUM (
|
||||
'assign',
|
||||
'comment',
|
||||
'attach_evidence',
|
||||
'link_ticket',
|
||||
'remediation_plan',
|
||||
'status_change',
|
||||
'accept_risk',
|
||||
'reopen',
|
||||
'close'
|
||||
);
|
||||
|
||||
CREATE TABLE ledger_events (
|
||||
tenant_id TEXT NOT NULL,
|
||||
chain_id UUID NOT NULL,
|
||||
sequence_no BIGINT NOT NULL,
|
||||
event_id UUID NOT NULL,
|
||||
event_type ledger_event_type NOT NULL,
|
||||
policy_version TEXT NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
artifact_id TEXT NOT NULL,
|
||||
source_run_id UUID,
|
||||
actor_id TEXT NOT NULL,
|
||||
actor_type TEXT NOT NULL,
|
||||
occurred_at TIMESTAMPTZ NOT NULL,
|
||||
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
event_body JSONB NOT NULL,
|
||||
event_hash CHAR(64) NOT NULL,
|
||||
previous_hash CHAR(64) NOT NULL,
|
||||
merkle_leaf_hash CHAR(64) NOT NULL,
|
||||
CONSTRAINT pk_ledger_events PRIMARY KEY (tenant_id, chain_id, sequence_no),
|
||||
CONSTRAINT uq_ledger_events_event_id UNIQUE (tenant_id, event_id),
|
||||
CONSTRAINT uq_ledger_events_chain_hash UNIQUE (tenant_id, chain_id, event_hash),
|
||||
CONSTRAINT ck_ledger_events_event_hash_hex CHECK (event_hash ~ '^[0-9a-f]{64}$'),
|
||||
CONSTRAINT ck_ledger_events_previous_hash_hex CHECK (previous_hash ~ '^[0-9a-f]{64}$'),
|
||||
CONSTRAINT ck_ledger_events_leaf_hash_hex CHECK (merkle_leaf_hash ~ '^[0-9a-f]{64}$'),
|
||||
CONSTRAINT ck_ledger_events_actor_type CHECK (actor_type IN ('system', 'operator', 'integration'))
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE ledger_events_default PARTITION OF ledger_events DEFAULT;
|
||||
|
||||
CREATE INDEX ix_ledger_events_finding ON ledger_events (tenant_id, finding_id, policy_version);
|
||||
CREATE INDEX ix_ledger_events_type ON ledger_events (tenant_id, event_type, recorded_at DESC);
|
||||
CREATE INDEX ix_ledger_events_recorded_at ON ledger_events (tenant_id, recorded_at DESC);
|
||||
|
||||
CREATE TABLE ledger_merkle_roots (
|
||||
tenant_id TEXT NOT NULL,
|
||||
anchor_id UUID NOT NULL,
|
||||
window_start TIMESTAMPTZ NOT NULL,
|
||||
window_end TIMESTAMPTZ NOT NULL,
|
||||
sequence_start BIGINT NOT NULL,
|
||||
sequence_end BIGINT NOT NULL,
|
||||
root_hash CHAR(64) NOT NULL,
|
||||
leaf_count INTEGER NOT NULL,
|
||||
anchored_at TIMESTAMPTZ NOT NULL,
|
||||
anchor_reference TEXT,
|
||||
CONSTRAINT pk_ledger_merkle_roots PRIMARY KEY (tenant_id, anchor_id),
|
||||
CONSTRAINT uq_ledger_merkle_root_hash UNIQUE (tenant_id, root_hash),
|
||||
CONSTRAINT ck_ledger_merkle_root_hash_hex CHECK (root_hash ~ '^[0-9a-f]{64}$')
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE ledger_merkle_roots_default PARTITION OF ledger_merkle_roots DEFAULT;
|
||||
|
||||
CREATE INDEX ix_merkle_sequences ON ledger_merkle_roots (tenant_id, sequence_end DESC);
|
||||
|
||||
CREATE TABLE findings_projection (
|
||||
tenant_id TEXT NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
policy_version TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
severity NUMERIC(6,3),
|
||||
labels JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
current_event_id UUID NOT NULL,
|
||||
explain_ref TEXT,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
cycle_hash CHAR(64) NOT NULL,
|
||||
CONSTRAINT pk_findings_projection PRIMARY KEY (tenant_id, finding_id, policy_version),
|
||||
CONSTRAINT ck_findings_projection_cycle_hash_hex CHECK (cycle_hash ~ '^[0-9a-f]{64}$')
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE findings_projection_default PARTITION OF findings_projection DEFAULT;
|
||||
|
||||
CREATE INDEX ix_projection_status ON findings_projection (tenant_id, status, severity DESC);
|
||||
CREATE INDEX ix_projection_labels_gin ON findings_projection USING GIN (labels JSONB_PATH_OPS);
|
||||
|
||||
CREATE TABLE finding_history (
|
||||
tenant_id TEXT NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
policy_version TEXT NOT NULL,
|
||||
event_id UUID NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
severity NUMERIC(6,3),
|
||||
actor_id TEXT NOT NULL,
|
||||
comment TEXT,
|
||||
occurred_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT pk_finding_history PRIMARY KEY (tenant_id, finding_id, event_id)
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE finding_history_default PARTITION OF finding_history DEFAULT;
|
||||
|
||||
CREATE INDEX ix_finding_history_timeline ON finding_history (tenant_id, finding_id, occurred_at DESC);
|
||||
|
||||
CREATE TABLE triage_actions (
|
||||
tenant_id TEXT NOT NULL,
|
||||
action_id UUID NOT NULL,
|
||||
event_id UUID NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
action_type ledger_action_type NOT NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}'::JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT NOT NULL,
|
||||
CONSTRAINT pk_triage_actions PRIMARY KEY (tenant_id, action_id)
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE triage_actions_default PARTITION OF triage_actions DEFAULT;
|
||||
|
||||
CREATE INDEX ix_triage_actions_event ON triage_actions (tenant_id, event_id);
|
||||
CREATE INDEX ix_triage_actions_created_at ON triage_actions (tenant_id, created_at DESC);
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,21 @@
|
||||
-- 002_projection_offsets.sql
|
||||
-- Projection worker checkpoint storage (LEDGER-29-003)
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ledger_projection_offsets (
|
||||
worker_id TEXT NOT NULL PRIMARY KEY,
|
||||
last_recorded_at TIMESTAMPTZ NOT NULL,
|
||||
last_event_id UUID NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO ledger_projection_offsets (worker_id, last_recorded_at, last_event_id, updated_at)
|
||||
VALUES (
|
||||
'default',
|
||||
'1970-01-01T00:00:00Z',
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
NOW())
|
||||
ON CONFLICT (worker_id) DO NOTHING;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,16 @@
|
||||
-- 003_policy_rationale.sql
|
||||
-- Add policy rationale column to findings_projection (LEDGER-29-004)
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE findings_projection
|
||||
ADD COLUMN IF NOT EXISTS policy_rationale JSONB NOT NULL DEFAULT '[]'::JSONB;
|
||||
|
||||
ALTER TABLE findings_projection
|
||||
ALTER COLUMN policy_rationale SET DEFAULT '[]'::JSONB;
|
||||
|
||||
UPDATE findings_projection
|
||||
SET policy_rationale = '[]'::JSONB
|
||||
WHERE policy_rationale IS NULL;
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user