mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-06-03 20:12:58 +00:00
248 lines
12 KiB
PL/PgSQL
248 lines
12 KiB
PL/PgSQL
-- Security Features Migration
|
|
-- This migration adds tables for security scanning, compliance, and audit logging
|
|
|
|
-- Security scans table
|
|
CREATE TABLE IF NOT EXISTS security_scans (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
service_id UUID REFERENCES services(id) ON DELETE CASCADE,
|
|
scan_type VARCHAR(50) NOT NULL CHECK (scan_type IN ('dependency', 'configuration', 'comprehensive')),
|
|
status VARCHAR(50) NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
|
|
started_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
completed_at TIMESTAMP WITH TIME ZONE,
|
|
summary JSONB DEFAULT '{}',
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Vulnerabilities table
|
|
CREATE TABLE IF NOT EXISTS vulnerabilities (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
type VARCHAR(50) NOT NULL CHECK (type IN ('dependency', 'configuration', 'code')),
|
|
severity VARCHAR(20) NOT NULL CHECK (severity IN ('critical', 'high', 'medium', 'low')),
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
service_id UUID REFERENCES services(id) ON DELETE CASCADE,
|
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'ignored')),
|
|
found_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
resolved_at TIMESTAMP WITH TIME ZONE,
|
|
metadata JSONB DEFAULT '{}',
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Compliance frameworks table
|
|
CREATE TABLE IF NOT EXISTS compliance_frameworks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
description TEXT,
|
|
version VARCHAR(20) NOT NULL,
|
|
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Compliance controls table
|
|
CREATE TABLE IF NOT EXISTS compliance_controls (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
framework_id UUID NOT NULL REFERENCES compliance_frameworks(id) ON DELETE CASCADE,
|
|
code VARCHAR(50) NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
category VARCHAR(100),
|
|
requirement TEXT,
|
|
test_procedure TEXT,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('compliant', 'non_compliant', 'not_applicable', 'pending')),
|
|
last_assessed TIMESTAMP WITH TIME ZONE,
|
|
evidence TEXT,
|
|
metadata JSONB DEFAULT '{}',
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
UNIQUE(framework_id, code)
|
|
);
|
|
|
|
-- Compliance reports table
|
|
CREATE TABLE IF NOT EXISTS compliance_reports (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
framework_id UUID NOT NULL REFERENCES compliance_frameworks(id) ON DELETE CASCADE,
|
|
assessment_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
assessor VARCHAR(255),
|
|
overall_status VARCHAR(50) NOT NULL CHECK (overall_status IN ('compliant', 'partially_compliant', 'non_compliant', 'in_progress')),
|
|
score INTEGER NOT NULL DEFAULT 0 CHECK (score >= 0 AND score <= 100),
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Compliance risks table
|
|
CREATE TABLE IF NOT EXISTS compliance_risks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
report_id UUID NOT NULL REFERENCES compliance_reports(id) ON DELETE CASCADE,
|
|
control_id UUID NOT NULL REFERENCES compliance_controls(id) ON DELETE CASCADE,
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
impact VARCHAR(20) NOT NULL CHECK (impact IN ('high', 'medium', 'low')),
|
|
likelihood VARCHAR(20) NOT NULL CHECK (likelihood IN ('high', 'medium', 'low')),
|
|
mitigation TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Audit logs table
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
action VARCHAR(100) NOT NULL,
|
|
resource VARCHAR(100) NOT NULL,
|
|
resource_id UUID,
|
|
details JSONB DEFAULT '{}',
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
success BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Data retention policies table
|
|
CREATE TABLE IF NOT EXISTS data_retention_policies (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
data_type VARCHAR(100) NOT NULL,
|
|
retention_period INTERVAL NOT NULL,
|
|
action VARCHAR(50) NOT NULL CHECK (action IN ('delete', 'anonymize', 'archive')),
|
|
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Anonymized data table
|
|
CREATE TABLE IF NOT EXISTS anonymized_data (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
original_id UUID NOT NULL,
|
|
anonymized_id VARCHAR(255) NOT NULL UNIQUE,
|
|
data_type VARCHAR(100) NOT NULL,
|
|
anonymized_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
retained_data TEXT, -- Encrypted non-sensitive data
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes for performance
|
|
CREATE INDEX IF NOT EXISTS idx_security_scans_project_id ON security_scans(project_id);
|
|
CREATE INDEX IF NOT EXISTS idx_security_scans_status ON security_scans(status);
|
|
CREATE INDEX IF NOT EXISTS idx_security_scans_started_at ON security_scans(started_at);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_project_id ON vulnerabilities(project_id);
|
|
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_service_id ON vulnerabilities(service_id);
|
|
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_severity ON vulnerabilities(severity);
|
|
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_status ON vulnerabilities(status);
|
|
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_found_at ON vulnerabilities(found_at);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_controls_framework_id ON compliance_controls(framework_id);
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_controls_status ON compliance_controls(status);
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_controls_last_assessed ON compliance_controls(last_assessed);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_reports_project_id ON compliance_reports(project_id);
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_reports_framework_id ON compliance_reports(framework_id);
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_reports_assessment_date ON compliance_reports(assessment_date);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_risks_report_id ON compliance_risks(report_id);
|
|
CREATE INDEX IF NOT EXISTS idx_compliance_risks_control_id ON compliance_risks(control_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_timestamp ON audit_logs(timestamp);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_anonymized_data_original_id ON anonymized_data(original_id);
|
|
CREATE INDEX IF NOT EXISTS idx_anonymized_data_anonymized_id ON anonymized_data(anonymized_id);
|
|
CREATE INDEX IF NOT EXISTS idx_anonymized_data_data_type ON anonymized_data(data_type);
|
|
|
|
-- Insert default data retention policies
|
|
INSERT INTO data_retention_policies (name, data_type, retention_period, action, enabled) VALUES
|
|
('User Data Retention', 'user_data', INTERVAL '2 years', 'anonymize', true),
|
|
('Analytics Data Retention', 'analytics_data', INTERVAL '6 months', 'delete', true),
|
|
('Log Data Retention', 'log_data', INTERVAL '90 days', 'delete', true),
|
|
('Deleted User Data', 'deleted_user_data', INTERVAL '30 days', 'delete', true),
|
|
('Audit Log Retention', 'audit_log', INTERVAL '1 year', 'archive', true)
|
|
ON CONFLICT (name) DO NOTHING;
|
|
|
|
-- Insert default GDPR framework
|
|
INSERT INTO compliance_frameworks (id, name, description, version, enabled) VALUES
|
|
(gen_random_uuid(), 'GDPR', 'General Data Protection Regulation compliance framework', '1.0', true)
|
|
ON CONFLICT (name) DO UPDATE SET version = '1.0', enabled = true;
|
|
|
|
-- Update updated_at trigger function
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ language 'plpgsql';
|
|
|
|
-- Create triggers for updated_at (only if they don't exist)
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_security_scans_updated_at') THEN
|
|
CREATE TRIGGER update_security_scans_updated_at BEFORE UPDATE ON security_scans FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_vulnerabilities_updated_at') THEN
|
|
CREATE TRIGGER update_vulnerabilities_updated_at BEFORE UPDATE ON vulnerabilities FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_compliance_frameworks_updated_at') THEN
|
|
CREATE TRIGGER update_compliance_frameworks_updated_at BEFORE UPDATE ON compliance_frameworks FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_compliance_controls_updated_at') THEN
|
|
CREATE TRIGGER update_compliance_controls_updated_at BEFORE UPDATE ON compliance_controls FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_compliance_reports_updated_at') THEN
|
|
CREATE TRIGGER update_compliance_reports_updated_at BEFORE UPDATE ON compliance_reports FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_compliance_risks_updated_at') THEN
|
|
CREATE TRIGGER update_compliance_risks_updated_at BEFORE UPDATE ON compliance_risks FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_data_retention_policies_updated_at') THEN
|
|
CREATE TRIGGER update_data_retention_policies_updated_at BEFORE UPDATE ON data_retention_policies FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Row Level Security (RLS) for audit logs
|
|
ALTER TABLE audit_logs ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy for audit logs - users can only see their own audit logs
|
|
CREATE POLICY audit_logs_user_policy ON audit_logs
|
|
FOR SELECT USING (user_id = current_setting('app.current_user_id')::UUID);
|
|
|
|
-- Policy for audit logs - no direct inserts (only through application)
|
|
CREATE POLICY audit_logs_insert_policy ON audit_logs
|
|
FOR INSERT WITH CHECK (false);
|
|
|
|
-- Policy for audit logs - no direct updates (audit logs are immutable)
|
|
CREATE POLICY audit_logs_update_policy ON audit_logs
|
|
FOR UPDATE WITH CHECK (false);
|
|
|
|
-- Row Level Security for anonymized data
|
|
ALTER TABLE anonymized_data ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policy for anonymized data - restricted access
|
|
CREATE POLICY anonymized_data_policy ON anonymized_data
|
|
FOR SELECT USING (current_setting('app.is_admin', true)::BOOLEAN);
|
|
|
|
-- Add comments for documentation
|
|
COMMENT ON TABLE security_scans IS 'Security scan records for vulnerability assessment';
|
|
COMMENT ON TABLE vulnerabilities IS 'Security vulnerabilities found during scans';
|
|
COMMENT ON TABLE compliance_frameworks IS 'Compliance frameworks like GDPR, SOC2, etc.';
|
|
COMMENT ON TABLE compliance_controls IS 'Individual controls within compliance frameworks';
|
|
COMMENT ON TABLE compliance_reports IS 'Compliance assessment reports';
|
|
COMMENT ON TABLE compliance_risks IS 'Risk assessments for compliance gaps';
|
|
COMMENT ON TABLE audit_logs IS 'Security audit trail for all sensitive operations';
|
|
COMMENT ON TABLE data_retention_policies IS 'Data retention and deletion policies';
|
|
COMMENT ON TABLE anonymized_data IS 'Anonymized user data for privacy compliance';
|