- Implemented InjectionTests.cs to cover various injection vulnerabilities including SQL, NoSQL, Command, LDAP, and XPath injections. - Created SsrfTests.cs to test for Server-Side Request Forgery (SSRF) vulnerabilities, including internal URL access, cloud metadata access, and URL allowlist bypass attempts. - Introduced MaliciousPayloads.cs to store a collection of malicious payloads for testing various security vulnerabilities. - Added SecurityAssertions.cs for common security-specific assertion helpers. - Established SecurityTestBase.cs as a base class for security tests, providing common infrastructure and mocking utilities. - Configured the test project StellaOps.Security.Tests.csproj with necessary dependencies for testing.
256 lines
11 KiB
SQL
256 lines
11 KiB
SQL
-- Generated from docs/db/SPECIFICATION.md §5.4 (2025-11-28)
|
|
|
|
CREATE SCHEMA IF NOT EXISTS scheduler;
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.schedules (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
cron_expression TEXT,
|
|
timezone TEXT NOT NULL DEFAULT 'UTC',
|
|
mode TEXT NOT NULL CHECK (mode IN ('scheduled', 'manual', 'on_event', 'continuous')),
|
|
selection JSONB NOT NULL DEFAULT '{}',
|
|
only_if JSONB DEFAULT '{}',
|
|
notify JSONB DEFAULT '{}',
|
|
limits JSONB DEFAULT '{}',
|
|
subscribers TEXT[] DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by TEXT,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_by TEXT,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by TEXT,
|
|
UNIQUE (tenant_id, name) WHERE deleted_at IS NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.triggers (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
schedule_id UUID NOT NULL REFERENCES scheduler.schedules(id) ON DELETE CASCADE,
|
|
trigger_type TEXT NOT NULL CHECK (trigger_type IN ('cron', 'fixed_delay', 'manual', 'on_event', 'webhook')),
|
|
cron_expression TEXT,
|
|
fixed_delay_seconds INT,
|
|
event_filter JSONB,
|
|
timezone TEXT DEFAULT 'UTC',
|
|
next_fire_time TIMESTAMPTZ,
|
|
last_fire_time TIMESTAMPTZ,
|
|
misfire_policy TEXT DEFAULT 'skip' CHECK (misfire_policy IN ('skip', 'fire_now', 'queue')),
|
|
enabled BOOLEAN NOT NULL DEFAULT TRUE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.runs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
schedule_id UUID REFERENCES scheduler.schedules(id),
|
|
trigger_id UUID REFERENCES scheduler.triggers(id),
|
|
state TEXT NOT NULL CHECK (state IN ('pending', 'queued', 'running', 'completed', 'failed', 'cancelled', 'stale', 'timeout')),
|
|
reason JSONB DEFAULT '{}',
|
|
stats JSONB DEFAULT '{}',
|
|
deltas JSONB DEFAULT '[]',
|
|
worker_id UUID,
|
|
retry_of UUID REFERENCES scheduler.runs(id),
|
|
retry_count INT NOT NULL DEFAULT 0,
|
|
error TEXT,
|
|
error_details JSONB,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
started_at TIMESTAMPTZ,
|
|
finished_at TIMESTAMPTZ,
|
|
timeout_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.graph_jobs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
sbom_id TEXT NOT NULL,
|
|
sbom_version_id TEXT,
|
|
sbom_digest TEXT NOT NULL,
|
|
graph_snapshot_id TEXT,
|
|
status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
|
|
trigger TEXT NOT NULL CHECK (trigger IN ('manual', 'scheduled', 'on_sbom_change', 'on_feed_update')),
|
|
priority INT NOT NULL DEFAULT 100,
|
|
attempts INT NOT NULL DEFAULT 0,
|
|
max_attempts INT NOT NULL DEFAULT 3,
|
|
cartographer_job_id TEXT,
|
|
correlation_id TEXT,
|
|
metadata JSONB DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
started_at TIMESTAMPTZ,
|
|
completed_at TIMESTAMPTZ,
|
|
error TEXT,
|
|
error_details JSONB
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.policy_jobs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
policy_pack_id TEXT NOT NULL,
|
|
policy_version INT,
|
|
target_type TEXT NOT NULL CHECK (target_type IN ('image', 'sbom', 'project', 'artifact')),
|
|
target_id TEXT NOT NULL,
|
|
status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'failed')),
|
|
priority INT NOT NULL DEFAULT 100,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
started_at TIMESTAMPTZ,
|
|
completed_at TIMESTAMPTZ,
|
|
result JSONB DEFAULT '{}',
|
|
error TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.impact_snapshots (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
run_id UUID NOT NULL REFERENCES scheduler.runs(id),
|
|
image_digest TEXT NOT NULL,
|
|
image_reference TEXT,
|
|
new_findings INT NOT NULL DEFAULT 0,
|
|
new_criticals INT NOT NULL DEFAULT 0,
|
|
new_high INT NOT NULL DEFAULT 0,
|
|
new_medium INT NOT NULL DEFAULT 0,
|
|
new_low INT NOT NULL DEFAULT 0,
|
|
total_findings INT NOT NULL DEFAULT 0,
|
|
kev_hits TEXT[] DEFAULT '{}',
|
|
top_findings JSONB DEFAULT '[]',
|
|
report_url TEXT,
|
|
attestation JSONB DEFAULT '{}',
|
|
detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.workers (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
node_id TEXT NOT NULL UNIQUE,
|
|
hostname TEXT,
|
|
capabilities TEXT[] DEFAULT '{}',
|
|
max_concurrent_jobs INT NOT NULL DEFAULT 1,
|
|
current_jobs INT NOT NULL DEFAULT 0,
|
|
version TEXT,
|
|
last_heartbeat_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'draining', 'paused', 'dead'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.execution_logs (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
run_id UUID NOT NULL REFERENCES scheduler.runs(id) ON DELETE CASCADE,
|
|
logged_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
level TEXT NOT NULL CHECK (level IN ('trace', 'debug', 'info', 'warn', 'error', 'fatal')),
|
|
message TEXT NOT NULL,
|
|
logger TEXT,
|
|
data JSONB DEFAULT '{}'
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.locks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
lock_key TEXT NOT NULL UNIQUE,
|
|
lock_type TEXT NOT NULL DEFAULT 'exclusive' CHECK (lock_type IN ('exclusive', 'shared')),
|
|
holder_id TEXT NOT NULL,
|
|
holder_info JSONB DEFAULT '{}',
|
|
acquired_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
renewed_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.run_summaries (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
schedule_id UUID REFERENCES scheduler.schedules(id),
|
|
period_start TIMESTAMPTZ NOT NULL,
|
|
period_end TIMESTAMPTZ NOT NULL,
|
|
total_runs INT NOT NULL DEFAULT 0,
|
|
successful_runs INT NOT NULL DEFAULT 0,
|
|
failed_runs INT NOT NULL DEFAULT 0,
|
|
cancelled_runs INT NOT NULL DEFAULT 0,
|
|
avg_duration_seconds NUMERIC(10,2),
|
|
max_duration_seconds INT,
|
|
min_duration_seconds INT,
|
|
total_findings_detected INT NOT NULL DEFAULT 0,
|
|
new_criticals INT NOT NULL DEFAULT 0,
|
|
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (tenant_id, schedule_id, period_start)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS scheduler.audit (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
action TEXT NOT NULL,
|
|
entity_type TEXT NOT NULL,
|
|
entity_id UUID NOT NULL,
|
|
actor TEXT,
|
|
actor_type TEXT CHECK (actor_type IN ('user', 'service', 'system')),
|
|
old_value JSONB,
|
|
new_value JSONB,
|
|
details JSONB DEFAULT '{}',
|
|
ip_address INET,
|
|
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes
|
|
CREATE INDEX IF NOT EXISTS idx_schedules_tenant ON scheduler.schedules(tenant_id) WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON scheduler.schedules(tenant_id, enabled) WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_triggers_schedule ON scheduler.triggers(schedule_id);
|
|
CREATE INDEX IF NOT EXISTS idx_triggers_next_fire ON scheduler.triggers(next_fire_time) WHERE enabled = TRUE;
|
|
CREATE INDEX IF NOT EXISTS idx_runs_tenant_state ON scheduler.runs(tenant_id, state);
|
|
CREATE INDEX IF NOT EXISTS idx_runs_schedule ON scheduler.runs(schedule_id);
|
|
CREATE INDEX IF NOT EXISTS idx_runs_created ON scheduler.runs(created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_runs_state_created ON scheduler.runs(state, created_at) WHERE state IN ('pending', 'queued', 'running');
|
|
CREATE INDEX IF NOT EXISTS idx_graph_jobs_tenant_status ON scheduler.graph_jobs(tenant_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_graph_jobs_sbom ON scheduler.graph_jobs(sbom_digest);
|
|
CREATE INDEX IF NOT EXISTS idx_policy_jobs_tenant_status ON scheduler.policy_jobs(tenant_id, status);
|
|
CREATE INDEX IF NOT EXISTS idx_impact_snapshots_run ON scheduler.impact_snapshots(run_id);
|
|
CREATE INDEX IF NOT EXISTS idx_impact_snapshots_tenant ON scheduler.impact_snapshots(tenant_id, detected_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_workers_status ON scheduler.workers(status);
|
|
CREATE INDEX IF NOT EXISTS idx_workers_heartbeat ON scheduler.workers(last_heartbeat_at);
|
|
CREATE INDEX IF NOT EXISTS idx_execution_logs_run ON scheduler.execution_logs(run_id);
|
|
CREATE INDEX IF NOT EXISTS idx_locks_expires ON scheduler.locks(expires_at);
|
|
CREATE INDEX IF NOT EXISTS idx_run_summaries_tenant ON scheduler.run_summaries(tenant_id, period_start DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_tenant_time ON scheduler.audit(tenant_id, occurred_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_entity ON scheduler.audit(entity_type, entity_id);
|
|
|
|
-- =============================================================================
|
|
-- Failure Signatures table for predictive TTFS signal hints
|
|
-- Tracks common failure patterns by scope, toolchain, and error code
|
|
-- Added: Sprint 0341
|
|
-- =============================================================================
|
|
CREATE TABLE IF NOT EXISTS scheduler.failure_signatures (
|
|
signature_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Scope: what artifact/repo/image this signature applies to
|
|
scope_type TEXT NOT NULL CHECK (scope_type IN ('repo', 'image', 'artifact', 'global')),
|
|
scope_id TEXT NOT NULL,
|
|
|
|
-- Toolchain: build environment fingerprint
|
|
toolchain_hash TEXT NOT NULL,
|
|
|
|
-- Error classification
|
|
error_code TEXT NULL,
|
|
error_category TEXT NULL CHECK (error_category IN ('network', 'auth', 'validation', 'resource', 'timeout', 'config', 'unknown')),
|
|
|
|
-- Signature statistics
|
|
occurrence_count INT NOT NULL DEFAULT 1,
|
|
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Resolution status
|
|
resolution_status TEXT NOT NULL DEFAULT 'unresolved' CHECK (resolution_status IN ('unresolved', 'investigating', 'resolved', 'wont_fix')),
|
|
resolution_notes TEXT NULL,
|
|
resolved_at TIMESTAMPTZ NULL,
|
|
resolved_by TEXT NULL,
|
|
|
|
-- Predictive hints
|
|
predicted_outcome TEXT NULL CHECK (predicted_outcome IN ('pass', 'fail', 'flaky', 'unknown')),
|
|
confidence_score DECIMAL(5, 4) NULL CHECK (confidence_score >= 0 AND confidence_score <= 1),
|
|
|
|
-- Composite unique constraint
|
|
UNIQUE (tenant_id, scope_type, scope_id, toolchain_hash, error_code)
|
|
);
|
|
|
|
-- Indexes for failure_signatures
|
|
CREATE INDEX IF NOT EXISTS idx_failure_sig_tenant ON scheduler.failure_signatures(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_sig_scope ON scheduler.failure_signatures(scope_type, scope_id);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_sig_error ON scheduler.failure_signatures(error_code) WHERE error_code IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_failure_sig_last_seen ON scheduler.failure_signatures(last_seen_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_failure_sig_unresolved ON scheduler.failure_signatures(tenant_id, resolution_status) WHERE resolution_status = 'unresolved';
|