mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-06-04 04:22:57 +00:00
overhaul
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
-- 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';
|
||||
Reference in New Issue
Block a user