Skip to content

Latest commit

 

History

History
864 lines (685 loc) · 27.8 KB

File metadata and controls

864 lines (685 loc) · 27.8 KB

GNN Security Framework

📋 Document Metadata
Type: Security Framework | Audience: Security Teams & System Administrators | Complexity: Advanced
Cross-References: Deployment Guide | API Reference | Troubleshooting

This document outlines comprehensive security considerations, threat models, and mitigation strategies for deploying and integrating GNN systems in production environments.

🔒 Security Overview

🎯 Security Objectives

  1. 🛡️ Data Protection: Secure handling of model definitions and simulation data
  2. 🔐 Access Control: Authenticated and authorized system access
  3. 🚫 Input Validation: Prevention of malicious model injection
  4. 📡 Communication Security: Encrypted data transmission
  5. 🔍 Audit Logging: Comprehensive security event tracking
  6. ⚡ Availability: Protection against denial of service attacks

🚨 Threat Model

Primary Threat Vectors

  1. Malicious Model Injection: Crafted GNN files with harmful payloads
  2. Privilege Escalation: Unauthorized access to system resources
  3. Data Exfiltration: Unauthorized access to sensitive model data
  4. Supply Chain Attacks: Compromised dependencies or frameworks
  5. Resource Exhaustion: DoS through computational overload
  6. API Exploitation: Abuse of programmatic interfaces

🛡️ Input Validation and Sanitization

📝 GNN Model Validation

from gnn.security import SecureGNNParser, SecurityConfig

class SecureGNNParser:
    """Security-hardened GNN parser with input validation."""
    
    def __init__(self, security_config: SecurityConfig):
        self.security_config = security_config
        self.validator = ModelSecurityValidator()
        self.sanitizer = InputSanitizer()
    
    def parse_file_secure(self, filepath: str, 
                         user_context: UserContext) -> GNNModel:
        """
        Parse GNN file with comprehensive security checks.
        
        Security checks performed:
        - File size limits
        - Content sanitization
        - Complexity bounds
        - Resource estimation
        - Malicious pattern detection
        """
        
        # 1. File-level security checks
        if not self.validate_file_safety(filepath):
            raise SecurityException("File failed security validation")
        
        # 2. Size and resource limits
        file_size = os.path.getsize(filepath)
        if file_size > self.security_config.max_file_size:
            raise SecurityException(f"File exceeds size limit: {file_size}")
        
        # 3. Content sanitization
        with open(filepath, 'r') as f:
            content = f.read()
        
        sanitized_content = self.sanitizer.sanitize_gnn_content(content)
        
        # 4. Parse with resource monitoring
        with ResourceMonitor() as monitor:
            model = super().parse_string(sanitized_content)
        
        # 5. Post-parse security validation
        security_report = self.validator.validate_model_security(
            model, user_context
        )
        
        if not security_report.is_safe:
            raise SecurityException(f"Model validation failed: {security_report.threats}")
        
        return model

# Security configuration
security_config = SecurityConfig(
    max_file_size=10 * 1024 * 1024,  # 10MB limit
    max_state_dimensions=1000,        # Prevent complexity attacks
    max_matrix_size=1000000,          # Matrix size limits
    allowed_file_types=['.md', '.gnn'],
    enable_sandboxing=True,
    audit_logging=True
)

# Usage with security context
parser = SecureGNNParser(security_config)
user_context = UserContext(user_id="researcher_001", role="standard")
model = parser.parse_file_secure("untrusted_model.md", user_context)

🧹 Input Sanitization

class InputSanitizer:
    """Sanitize GNN model content to prevent injection attacks."""
    
    def __init__(self):
        self.dangerous_patterns = self.load_dangerous_patterns()
        self.allowed_sections = {
            'GNNVersionAndFlags', 'ModelName', 'ModelAnnotation',
            'StateSpaceBlock', 'Connections', 'InitialParameterization',
            'Equations', 'Time', 'ActInfOntologyAnnotation',
            'Footer', 'Signature'
        }
    
    def sanitize_gnn_content(self, content: str) -> str:
        """
        Sanitize GNN content to remove potential security threats.
        
        Sanitization steps:
        1. Remove dangerous patterns
        2. Validate section headers
        3. Sanitize mathematical expressions
        4. Remove embedded code blocks
        5. Validate variable names
        """
        
        # 1. Detect and block dangerous patterns
        for pattern in self.dangerous_patterns:
            if re.search(pattern, content, re.IGNORECASE):
                raise SecurityException(f"Dangerous pattern detected: {pattern}")
        
        # 2. Extract and validate sections
        sections = self.extract_sections(content)
        validated_sections = {}
        
        for section_name, section_content in sections.items():
            if section_name not in self.allowed_sections:
                continue  # Skip unknown sections
            
            validated_sections[section_name] = self.sanitize_section(
                section_name, section_content
            )
        
        return self.reconstruct_gnn_content(validated_sections)
    
    def sanitize_section(self, section_name: str, content: str) -> str:
        """Sanitize individual section content."""
        
        if section_name == 'StateSpaceBlock':
            return self.sanitize_state_space(content)
        elif section_name == 'InitialParameterization':
            return self.sanitize_parameters(content)
        elif section_name == 'Equations':
            return self.sanitize_equations(content)
        else:
            return self.sanitize_generic_text(content)
    
    def load_dangerous_patterns(self) -> List[str]:
        """Load patterns that indicate potential security threats."""
        return [
            r'__import__',           # Python imports
            r'eval\s*\(',           # Code evaluation
            r'exec\s*\(',           # Code execution
            r'os\.system',          # System commands
            r'subprocess',          # Process spawning
            r'open\s*\(',          # File operations
            r'file\s*\(',          # File operations
            r'<script',             # Script injection
            r'javascript:',         # JavaScript URLs
            r'data:.*base64',       # Base64 data URLs
            r'\.\./',              # Directory traversal
            r'\\x[0-9a-fA-F]{2}',  # Hex encoding
            r'%[0-9a-fA-F]{2}',    # URL encoding
        ]

# Usage
sanitizer = InputSanitizer()
safe_content = sanitizer.sanitize_gnn_content(untrusted_content)

🔐 Access Control and Authentication

👤 User Authentication

from gnn.auth import AuthenticationManager, Role, Permission

class AuthenticationManager:
    """Manage user authentication and authorization."""
    
    def __init__(self, auth_config: AuthConfig):
        self.auth_config = auth_config
        self.session_manager = SessionManager()
    
    def authenticate_user(self, username: str, password: str) -> UserSession:
        """Authenticate user credentials."""
        
        # Support multiple authentication methods
        if self.auth_config.auth_method == 'ldap':
            return self.authenticate_ldap(username, password)
        elif self.auth_config.auth_method == 'oauth':
            return self.authenticate_oauth(username, password)
        elif self.auth_config.auth_method == 'local':
            return self.authenticate_local(username, password)
        else:
            raise AuthenticationException("Invalid authentication method")
    
    def authorize_action(self, 
                        user_session: UserSession,
                        action: str,
                        resource: str = None) -> bool:
        """Check if user is authorized for specific action."""
        
        user_role = user_session.role
        required_permission = self.get_required_permission(action, resource)
        
        return user_role.has_permission(required_permission)

# Role-based access control
class Role:
    """User role with associated permissions."""
    
    VIEWER = Role("viewer", [
        Permission.READ_MODELS,
        Permission.VIEW_VISUALIZATIONS
    ])
    
    RESEARCHER = Role("researcher", [
        Permission.READ_MODELS,
        Permission.WRITE_MODELS,
        Permission.RUN_SIMULATIONS,
        Permission.VIEW_VISUALIZATIONS,
        Permission.EXPORT_DATA
    ])
    
    ADMIN = Role("admin", [
        Permission.ALL  # All permissions
    ])

# Usage
auth_manager = AuthenticationManager(auth_config)
user_session = auth_manager.authenticate_user("researcher", "password")

if auth_manager.authorize_action(user_session, "parse_model"):
    model = parser.parse_file_secure(filepath, user_session.context)
else:
    raise AuthorizationException("Insufficient permissions")

🔑 API Key Management

class APIKeyManager:
    """Manage API keys for programmatic access."""
    
    def __init__(self):
        self.key_store = SecureKeyStore()
        self.rate_limiter = RateLimiter()
    
    def generate_api_key(self, 
                        user_id: str,
                        permissions: List[Permission],
                        expiry_days: int = 90) -> APIKey:
        """Generate new API key with specified permissions."""
        
        key = APIKey(
            key_id=generate_secure_id(),
            user_id=user_id,
            permissions=permissions,
            created_at=datetime.utcnow(),
            expires_at=datetime.utcnow() + timedelta(days=expiry_days),
            rate_limit=self.get_default_rate_limit(permissions)
        )
        
        self.key_store.store_key(key)
        return key
    
    def validate_api_key(self, key_string: str) -> APIKeyContext:
        """Validate API key and return context."""
        
        # 1. Check key format and existence
        key = self.key_store.get_key(key_string)
        if not key:
            raise InvalidAPIKeyException("Invalid API key")
        
        # 2. Check expiration
        if key.is_expired():
            raise ExpiredAPIKeyException("API key has expired")
        
        # 3. Check rate limits
        if not self.rate_limiter.check_rate_limit(key):
            raise RateLimitExceededException("Rate limit exceeded")
        
        # 4. Update usage statistics
        self.key_store.record_usage(key)
        
        return APIKeyContext(
            user_id=key.user_id,
            permissions=key.permissions,
            rate_limit=key.rate_limit
        )

# API key usage
api_manager = APIKeyManager()

# Generate key for researcher
researcher_key = api_manager.generate_api_key(
    user_id="researcher_001",
    permissions=[Permission.READ_MODELS, Permission.RUN_SIMULATIONS],
    expiry_days=30
)

# Validate incoming API request
@require_api_key
def api_parse_model(key_string: str, model_file: str):
    context = api_manager.validate_api_key(key_string)
    
    if Permission.READ_MODELS not in context.permissions:
        raise InsufficientPermissionsException()
    
    return secure_parser.parse_file_secure(model_file, context)

🔒 Secure Communication

🔐 Encryption and TLS

class SecureCommunicationManager:
    """Manage secure communication channels."""
    
    def __init__(self, tls_config: TLSConfig):
        self.tls_config = tls_config
        self.certificate_manager = CertificateManager()
    
    def setup_tls_server(self, port: int) -> SecureServer:
        """Setup TLS-encrypted server."""
        
        # Load certificates
        cert_chain = self.certificate_manager.get_certificate_chain()
        private_key = self.certificate_manager.get_private_key()
        
        # Configure TLS settings
        tls_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        tls_context.load_cert_chain(cert_chain, private_key)
        tls_context.minimum_version = ssl.TLSVersion.TLSv1_2
        tls_context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
        
        return SecureServer(port, tls_context)
    
    def encrypt_model_data(self, model: GNNModel, 
                          recipient_key: bytes) -> EncryptedData:
        """Encrypt model data for secure transmission."""
        
        # Serialize model
        model_data = model.to_json().encode('utf-8')
        
        # Generate ephemeral encryption key
        ephemeral_key = Fernet.generate_key()
        fernet = Fernet(ephemeral_key)
        
        # Encrypt model data
        encrypted_data = fernet.encrypt(model_data)
        
        # Encrypt ephemeral key with recipient's public key
        encrypted_key = self.encrypt_with_public_key(ephemeral_key, recipient_key)
        
        return EncryptedData(
            encrypted_data=encrypted_data,
            encrypted_key=encrypted_key,
            algorithm='Fernet+RSA'
        )

# TLS configuration
tls_config = TLSConfig(
    certificate_path='/etc/ssl/certs/gnn.crt',
    private_key_path='/etc/ssl/private/gnn.key',
    ca_bundle_path='/etc/ssl/certs/ca-bundle.crt',
    require_client_cert=True,
    min_tls_version='1.2'
)

# Setup secure server
comm_manager = SecureCommunicationManager(tls_config)
secure_server = comm_manager.setup_tls_server(8443)

🔍 Audit Logging and Monitoring

📊 Security Event Logging

class SecurityAuditLogger:
    """Comprehensive security event logging."""
    
    def __init__(self, config: AuditConfig):
        self.config = config
        self.logger = self.setup_secure_logger()
        self.event_correlator = EventCorrelator()
    
    def log_authentication_event(self, 
                                event_type: str,
                                user_id: str,
                                ip_address: str,
                                success: bool,
                                details: dict = None):
        """Log authentication-related events."""
        
        event = SecurityEvent(
            event_type=f'AUTH_{event_type}',
            timestamp=datetime.utcnow(),
            user_id=user_id,
            ip_address=ip_address,
            success=success,
            details=details or {},
            severity=self.calculate_severity(event_type, success)
        )
        
        self.log_event(event)
        
        # Check for suspicious patterns
        if not success:
            self.check_failed_login_patterns(user_id, ip_address)
    
    def log_model_access_event(self,
                              action: str,
                              model_path: str,
                              user_id: str,
                              success: bool,
                              details: dict = None):
        """Log model access and modification events."""
        
        event = SecurityEvent(
            event_type=f'MODEL_{action}',
            timestamp=datetime.utcnow(),
            user_id=user_id,
            resource=model_path,
            success=success,
            details=details or {},
            severity='INFO' if success else 'WARN'
        )
        
        self.log_event(event)
    
    def log_security_violation(self,
                              violation_type: str,
                              details: dict,
                              user_id: str = None,
                              ip_address: str = None):
        """Log security violations and potential attacks."""
        
        event = SecurityEvent(
            event_type=f'VIOLATION_{violation_type}',
            timestamp=datetime.utcnow(),
            user_id=user_id,
            ip_address=ip_address,
            success=False,
            details=details,
            severity='HIGH'
        )
        
        self.log_event(event)
        
        # Trigger immediate alerts for high-severity events
        if violation_type in ['INJECTION_ATTEMPT', 'PRIVILEGE_ESCALATION']:
            self.trigger_security_alert(event)

# Usage
audit_logger = SecurityAuditLogger(audit_config)

# Log authentication
audit_logger.log_authentication_event(
    'LOGIN', 'researcher_001', '192.168.1.100', True
)

# Log model access
audit_logger.log_model_access_event(
    'PARSE', '/models/sensitive_model.md', 'researcher_001', True,
    {'parser_version': '1.0', 'execution_time': 2.3}
)

# Log security violation
audit_logger.log_security_violation(
    'INJECTION_ATTEMPT',
    {'pattern': '__import__', 'file': 'malicious_model.md'},
    user_id='unknown_user',
    ip_address='192.168.1.255'
)

🚨 Real-time Security Monitoring

class SecurityMonitor:
    """Real-time security monitoring and alerting."""
    
    def __init__(self, config: MonitoringConfig):
        self.config = config
        self.alert_manager = AlertManager()
        self.threat_detector = ThreatDetector()
    
    def monitor_system_resources(self):
        """Monitor system resources for abuse."""
        
        while True:
            # Check CPU usage
            cpu_usage = psutil.cpu_percent(interval=1)
            if cpu_usage > self.config.cpu_threshold:
                self.alert_manager.send_alert(
                    'HIGH_CPU_USAGE',
                    f'CPU usage: {cpu_usage}%'
                )
            
            # Check memory usage
            memory = psutil.virtual_memory()
            if memory.percent > self.config.memory_threshold:
                self.alert_manager.send_alert(
                    'HIGH_MEMORY_USAGE',
                    f'Memory usage: {memory.percent}%'
                )
            
            # Check for suspicious processes
            suspicious_processes = self.detect_suspicious_processes()
            for process in suspicious_processes:
                self.alert_manager.send_alert(
                    'SUSPICIOUS_PROCESS',
                    f'Process: {process.name()}, PID: {process.pid}'
                )
            
            time.sleep(self.config.monitoring_interval)
    
    def detect_anomalies(self, events: List[SecurityEvent]) -> List[Anomaly]:
        """Detect anomalous patterns in security events."""
        
        anomalies = []
        
        # Detect brute force attacks
        failed_logins = self.group_failed_logins(events)
        for ip, failures in failed_logins.items():
            if len(failures) > self.config.brute_force_threshold:
                anomalies.append(Anomaly(
                    type='BRUTE_FORCE_ATTACK',
                    source_ip=ip,
                    event_count=len(failures),
                    severity='HIGH'
                ))
        
        # Detect unusual access patterns
        access_patterns = self.analyze_access_patterns(events)
        for pattern in access_patterns:
            if pattern.is_unusual():
                anomalies.append(Anomaly(
                    type='UNUSUAL_ACCESS_PATTERN',
                    details=pattern.details,
                    severity='MEDIUM'
                ))
        
        return anomalies

# Start security monitoring
monitor = SecurityMonitor(monitoring_config)
monitor_thread = threading.Thread(target=monitor.monitor_system_resources)
monitor_thread.start()

🛡️ Sandboxing and Isolation

📦 Container Security

# Secure Docker container for GNN processing
FROM python:3.10-slim

# Create non-root user
RUN groupadd -r gnn && useradd -r -g gnn gnn

# Install security updates
RUN apt-get update && apt-get upgrade -y && \
    apt-get install -y --no-install-recommends \
    ca-certificates && \
    rm -rf /var/lib/apt/lists/*

# Set up application directory
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen

# Copy application code
COPY --chown=gnn:gnn . .

# Remove unnecessary packages and files
RUN apt-get purge -y --auto-remove \
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /root/.cache

# Security hardening
RUN chmod -R o-rwx /app
RUN find /app -type f -name "*.py" -exec chmod 644 {} \;

# Switch to non-root user
USER gnn

# Set security-focused environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PYTHONPATH=/app

# Expose only necessary port
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD python -c "import requests; requests.get('http://localhost:8080/health')"

# Run application
CMD ["python", "src/main.py", "--secure-mode"]

🔒 Process Isolation

class SecureExecutionEnvironment:
    """Secure environment for executing GNN operations."""
    
    def __init__(self, config: SecurityConfig):
        self.config = config
        self.sandbox = ProcessSandbox()
    
    def execute_in_sandbox(self, 
                          operation: Callable,
                          *args,
                          timeout: int = 30,
                          memory_limit: int = 512,  # MB
                          **kwargs) -> Any:
        """Execute operation in isolated sandbox."""
        
        # Set up sandbox constraints
        constraints = SandboxConstraints(
            max_memory=memory_limit * 1024 * 1024,  # Convert to bytes
            max_cpu_time=timeout,
            max_file_descriptors=100,
            allowed_syscalls=self.get_allowed_syscalls(),
            blocked_networks=True,
            readonly_filesystem=True
        )
        
        try:
            # Execute in sandbox
            with self.sandbox.create_environment(constraints) as env:
                result = env.execute(operation, *args, **kwargs)
                return result
                
        except SandboxTimeoutException:
            raise SecurityException("Operation timed out in sandbox")
        except SandboxMemoryException:
            raise SecurityException("Operation exceeded memory limit")
        except SandboxViolationException as e:
            raise SecurityException(f"Sandbox violation: {e}")
    
    def get_allowed_syscalls(self) -> List[str]:
        """Get list of allowed system calls for sandbox."""
        return [
            'read', 'write', 'open', 'close', 'stat', 'fstat',
            'lstat', 'mmap', 'munmap', 'brk', 'rt_sigaction',
            'ioctl', 'access', 'pipe', 'dup2', 'getpid',
            'socket', 'connect', 'sendto', 'recvfrom'
        ]

# Usage
secure_env = SecureExecutionEnvironment(security_config)

# Execute parsing in sandbox
try:
    model = secure_env.execute_in_sandbox(
        parser.parse_file,
        "untrusted_model.md",
        timeout=30,
        memory_limit=256
    )
except SecurityException as e:
    logger.error(f"Sandboxed execution failed: {e}")

🚨 Incident Response

📋 Incident Response Plan

class IncidentResponseManager:
    """Manage security incident response procedures."""
    
    def __init__(self, config: IncidentConfig):
        self.config = config
        self.notification_manager = NotificationManager()
        self.forensics_logger = ForensicsLogger()
    
    def handle_security_incident(self, 
                                incident_type: str,
                                severity: str,
                                details: dict):
        """Execute incident response procedures."""
        
        # 1. Create incident record
        incident = SecurityIncident(
            incident_id=generate_incident_id(),
            type=incident_type,
            severity=severity,
            detected_at=datetime.utcnow(),
            details=details
        )
        
        # 2. Immediate containment
        if severity in ['HIGH', 'CRITICAL']:
            self.execute_containment_procedures(incident)
        
        # 3. Notification
        self.notification_manager.notify_security_team(incident)
        
        # 4. Forensics collection
        self.forensics_logger.collect_evidence(incident)
        
        # 5. Response coordination
        return self.coordinate_response(incident)
    
    def execute_containment_procedures(self, incident: SecurityIncident):
        """Execute immediate containment procedures."""
        
        if incident.type == 'BRUTE_FORCE_ATTACK':
            # Block source IP
            self.firewall_manager.block_ip(incident.details['source_ip'])
            
        elif incident.type == 'MALICIOUS_MODEL_DETECTED':
            # Quarantine model file
            self.quarantine_manager.quarantine_file(
                incident.details['model_file']
            )
            
        elif incident.type == 'PRIVILEGE_ESCALATION':
            # Disable user account
            self.user_manager.disable_account(
                incident.details['user_id']
            )
            
        elif incident.type == 'DATA_EXFILTRATION':
            # Block network access
            self.network_manager.block_user_network_access(
                incident.details['user_id']
            )

# Incident response configuration
incident_config = IncidentConfig(
    notification_channels=['email', 'slack', 'pagerduty'],
    containment_timeout=300,  # 5 minutes
    forensics_retention_days=90,
    auto_containment_severity=['HIGH', 'CRITICAL']
)

# Set up incident response
incident_manager = IncidentResponseManager(incident_config)

# Example incident handling
incident_manager.handle_security_incident(
    incident_type='MALICIOUS_MODEL_DETECTED',
    severity='HIGH',
    details={
        'model_file': '/tmp/suspicious_model.md',
        'detection_method': 'pattern_analysis',
        'malicious_patterns': ['__import__', 'os.system'],
        'user_id': 'unknown_user',
        'timestamp': datetime.utcnow().isoformat()
    }
)

🔧 Security Configuration

⚙️ Security Hardening Checklist

# security_config.yaml - Production security configuration

authentication:
  method: "ldap"  # ldap, oauth, local
  session_timeout: 3600  # 1 hour
  max_concurrent_sessions: 3
  password_policy:
    min_length: 12
    require_uppercase: true
    require_lowercase: true
    require_numbers: true
    require_symbols: true
    history_length: 12

authorization:
  default_role: "viewer"
  role_inheritance: true
  permission_caching: true
  cache_ttl: 300  # 5 minutes

input_validation:
  max_file_size: 10485760  # 10MB
  max_model_complexity: 10000
  max_matrix_dimensions: 1000
  allowed_file_extensions: [".md", ".gnn"]
  content_scanning: true
  malware_scanning: true

communication:
  enforce_tls: true
  min_tls_version: "1.2"
  cipher_suites: "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM"
  hsts_enabled: true
  certificate_pinning: true

logging:
  audit_enabled: true
  log_level: "INFO"
  log_retention_days: 365
  sensitive_data_masking: true
  real_time_analysis: true

monitoring:
  intrusion_detection: true
  anomaly_detection: true
  resource_monitoring: true
  performance_monitoring: true
  alert_thresholds:
    cpu_usage: 80
    memory_usage: 85
    disk_usage: 90
    failed_logins: 5

sandboxing:
  enabled: true
  execution_timeout: 300  # 5 minutes
  memory_limit: 1073741824  # 1GB
  network_isolation: true
  filesystem_readonly: true

incident_response:
  auto_containment: true
  notification_enabled: true
  forensics_collection: true
  escalation_timeouts:
    low: 3600      # 1 hour
    medium: 1800   # 30 minutes
    high: 300      # 5 minutes
    critical: 60   # 1 minute

🔒 Security Summary: This comprehensive security framework provides defense-in-depth protection for GNN deployments, addressing all major threat vectors and providing robust incident response capabilities.

🔄 Continuous Security: Security is an ongoing process requiring regular updates, monitoring, and adaptation to emerging threats in the Active Inference and AI research community.


Status: Production-Ready Security Framework
Next Steps: Security Assessment | Compliance Guide