Standards & Reference

Security

This document defines the security standards, practices, and controls for the Flowstate application.

Table of Contents

  1. Overview
  2. Security Principles
  3. Authentication
  4. Authorization
  5. Data Security
  6. Input Validation
  7. Output Encoding
  8. Session Management
  9. API Security
  10. Frontend Security
  11. Dependency Security
  12. Security Testing
  13. Incident Response
  14. Security Checklist

Overview

Security is a foundational requirement for the Flowstate application. All development must adhere to these security standards to protect sensitive data and maintain system integrity.

Security Objectives

ObjectiveDescription
ConfidentialityProtect sensitive data from unauthorized access
IntegrityEnsure data accuracy and prevent tampering
AvailabilityMaintain system uptime and resilience
Non-repudiationEnsure actions can be attributed to users
ComplianceMeet regulatory and organizational requirements

Threat Model

┌─────────────────────────────────────────────────────────────────────────────┐
│                              Threat Landscape                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│  External Threats                                                             │
│  ├── Unauthorized Access                                                      │
│  │   ├── Credential Theft                                                    │
│  │   ├── Session Hijacking                                                   │
│  │   └── Brute Force Attacks                                                 │
│  ├── Data Breaches                                                           │
│  │   ├── SQL Injection                                                       │
│  │   ├── XSS Attacks                                                         │
│  │   └── API Exploitation                                                    │
│  └── Service Disruption                                                      │
│      ├── DDoS Attacks                                                        │
│      └── Resource Exhaustion                                                 │
│                                                                               │
│  Internal Threats                                                             │
│  ├── Privilege Escalation                                                    │
│  ├── Data Exfiltration                                                       │
│  └── Insider Misuse                                                          │
│                                                                               │
└─────────────────────────────────────────────────────────────────────────────┘

Security Principles

Defense in Depth

Apply multiple layers of security controls:

┌─────────────────────────────────────────────────────────────────────────────┐
│                           Security Layers                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   ┌────────────────────────────────────────────────────────────────────┐    │
│   │  Layer 1: Perimeter Security                                        │    │
│   │  WAF, DDoS Protection, Rate Limiting                               │    │
│   │  ┌────────────────────────────────────────────────────────────┐   │    │
│   │  │  Layer 2: Network Security                                  │   │    │
│   │  │  TLS, VPC, Security Groups, Network ACLs                   │   │    │
│   │  │  ┌────────────────────────────────────────────────────┐   │   │    │
│   │  │  │  Layer 3: Application Security                      │   │   │    │
│   │  │  │  Authentication, Authorization, Input Validation    │   │   │    │
│   │  │  │  ┌────────────────────────────────────────────┐   │   │   │    │
│   │  │  │  │  Layer 4: Data Security                    │   │   │   │    │
│   │  │  │  │  Encryption, Access Controls, Masking      │   │   │   │    │
│   │  │  │  └────────────────────────────────────────────┘   │   │   │    │
│   │  │  └────────────────────────────────────────────────────┘   │   │    │
│   │  └────────────────────────────────────────────────────────────┘   │    │
│   └────────────────────────────────────────────────────────────────────┘    │
│                                                                               │
└─────────────────────────────────────────────────────────────────────────────┘

Principle of Least Privilege

Grant minimum permissions required for each role:

// ✅ Good: Minimal permissions
const userPermissions = {
  viewer: ['events:read', 'deliverables:read'],
  user: ['events:read', 'deliverables:read', 'deliverables:update'],
  manager: ['events:*', 'deliverables:*', 'reports:read'],
  admin: ['*'],
};

// ❌ Bad: Overly permissive
const userPermissions = {
  user: ['*'], // Too broad
};

Fail Secure

When errors occur, default to secure state:

// ✅ Good: Fail secure
async function checkPermission(userId: string, resource: string): Promise<boolean> {
  try {
    const permissions = await fetchUserPermissions(userId);
    return permissions.includes(resource);
  } catch (error) {
    // Default to denied on error
    console.error('Permission check failed:', error);
    return false;
  }
}

// ❌ Bad: Fail open
async function checkPermission(userId: string, resource: string): Promise<boolean> {
  try {
    const permissions = await fetchUserPermissions(userId);
    return permissions.includes(resource);
  } catch (error) {
    // Dangerous: grants access on error
    return true;
  }
}

Authentication

Azure AD Integration

Use MSAL (Microsoft Authentication Library) for authentication:

// src/lib/auth/msalConfig.ts
import { Configuration, LogLevel } from '@azure/msal-browser';

export const msalConfig: Configuration = {
  auth: {
    clientId: import.meta.env.VITE_AZURE_CLIENT_ID,
    authority: `https://login.microsoftonline.com/${import.meta.env.VITE_AZURE_TENANT_ID}`,
    redirectUri: window.location.origin,
    postLogoutRedirectUri: window.location.origin,
  },
  cache: {
    cacheLocation: 'sessionStorage', // More secure than localStorage
    storeAuthStateInCookie: false,
  },
  system: {
    loggerOptions: {
      logLevel: import.meta.env.PROD ? LogLevel.Error : LogLevel.Warning,
      loggerCallback: (level, message) => {
        console.log(message);
      },
    },
  },
};

export const loginRequest = {
  scopes: ['User.Read', 'api://flowstate/access'],
};

Token Management

// src/lib/auth/tokenManager.ts
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig, loginRequest } from './msalConfig';

const msalInstance = new PublicClientApplication(msalConfig);

export async function getAccessToken(): Promise<string | null> {
  const account = msalInstance.getActiveAccount();
  if (!account) return null;

  try {
    // Try silent token acquisition first
    const response = await msalInstance.acquireTokenSilent({
      ...loginRequest,
      account,
    });
    return response.accessToken;
  } catch (error) {
    // Fall back to interactive if silent fails
    try {
      const response = await msalInstance.acquireTokenPopup(loginRequest);
      return response.accessToken;
    } catch (interactiveError) {
      console.error('Authentication failed:', interactiveError);
      return null;
    }
  }
}

// Token refresh handling
export function setupTokenRefresh(): void {
  // Refresh token 5 minutes before expiry
  const REFRESH_BUFFER = 5 * 60 * 1000;

  setInterval(async () => {
    const account = msalInstance.getActiveAccount();
    if (!account) return;

    const tokenExpiresAt = account.idTokenClaims?.exp;
    if (!tokenExpiresAt) return;

    const expiryTime = tokenExpiresAt * 1000;
    const now = Date.now();

    if (expiryTime - now < REFRESH_BUFFER) {
      await getAccessToken(); // This will refresh the token
    }
  }, 60 * 1000); // Check every minute
}

Multi-Factor Authentication

MFA is enforced at the Azure AD level. Conditional access policies should require MFA for:

  • All administrative actions
  • Access from new devices
  • Access from unusual locations
  • Sensitive data access

Authorization

Role-Based Access Control (RBAC)

// src/types/auth.ts
export type Role = 'admin' | 'manager' | 'user' | 'viewer';

export interface User {
  id: string;
  email: string;
  name: string;
  role: Role;
  permissions: string[];
  groups: string[];
}

// Permission definitions
export const PERMISSIONS = {
  // Events
  EVENTS_CREATE: 'events:create',
  EVENTS_READ: 'events:read',
  EVENTS_UPDATE: 'events:update',
  EVENTS_DELETE: 'events:delete',

  // Deliverables
  DELIVERABLES_CREATE: 'deliverables:create',
  DELIVERABLES_READ: 'deliverables:read',
  DELIVERABLES_UPDATE: 'deliverables:update',
  DELIVERABLES_DELETE: 'deliverables:delete',
  DELIVERABLES_APPROVE: 'deliverables:approve',

  // Reports
  REPORTS_READ: 'reports:read',
  REPORTS_EXPORT: 'reports:export',

  // Admin
  ADMIN_USERS: 'admin:users',
  ADMIN_SETTINGS: 'admin:settings',
  ADMIN_AUDIT: 'admin:audit',
} as const;

// Role to permissions mapping
export const ROLE_PERMISSIONS: Record<Role, string[]> = {
  admin: Object.values(PERMISSIONS),
  manager: [
    PERMISSIONS.EVENTS_CREATE,
    PERMISSIONS.EVENTS_READ,
    PERMISSIONS.EVENTS_UPDATE,
    PERMISSIONS.DELIVERABLES_CREATE,
    PERMISSIONS.DELIVERABLES_READ,
    PERMISSIONS.DELIVERABLES_UPDATE,
    PERMISSIONS.DELIVERABLES_APPROVE,
    PERMISSIONS.REPORTS_READ,
    PERMISSIONS.REPORTS_EXPORT,
  ],
  user: [
    PERMISSIONS.EVENTS_READ,
    PERMISSIONS.DELIVERABLES_READ,
    PERMISSIONS.DELIVERABLES_UPDATE,
    PERMISSIONS.REPORTS_READ,
  ],
  viewer: [
    PERMISSIONS.EVENTS_READ,
    PERMISSIONS.DELIVERABLES_READ,
  ],
};

Permission Hooks

// src/hooks/usePermissions.ts
import { useAuth } from './useAuth';
import { ROLE_PERMISSIONS } from '@/types/auth';

export function usePermission(permission: string): boolean {
  const { user } = useAuth();

  if (!user) return false;

  // Check role-based permissions
  const rolePermissions = ROLE_PERMISSIONS[user.role] || [];
  if (rolePermissions.includes(permission)) return true;

  // Check user-specific permissions
  return user.permissions.includes(permission);
}

export function usePermissions(permissions: string[]): boolean[] {
  const { user } = useAuth();

  if (!user) return permissions.map(() => false);

  return permissions.map((permission) => {
    const rolePermissions = ROLE_PERMISSIONS[user.role] || [];
    return rolePermissions.includes(permission) || user.permissions.includes(permission);
  });
}

// Permission guard component
export function RequirePermission({
  permission,
  children,
  fallback = null,
}: {
  permission: string;
  children: React.ReactNode;
  fallback?: React.ReactNode;
}) {
  const hasPermission = usePermission(permission);

  if (!hasPermission) return <>{fallback}</>;

  return <>{children}</>;
}

Route Protection

// src/components/guards/AuthGuard.tsx
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';

interface AuthGuardProps {
  children: React.ReactNode;
  requiredPermission?: string;
  requiredRole?: Role;
}

export function AuthGuard({
  children,
  requiredPermission,
  requiredRole,
}: AuthGuardProps) {
  const { user, loading } = useAuth();
  const location = useLocation();

  if (loading) {
    return <LoadingScreen />;
  }

  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  if (requiredRole && user.role !== requiredRole && user.role !== 'admin') {
    return <Navigate to="/unauthorized" replace />;
  }

  if (requiredPermission && !usePermission(requiredPermission)) {
    return <Navigate to="/unauthorized" replace />;
  }

  return <>{children}</>;
}

Data Security

Data Classification

ClassificationExamplesProtection
PublicMarketing contentStandard protection
InternalEvent names, locationsAuthenticated access
ConfidentialDeliverable detailsRole-based access
RestrictedFinancial data, PIIEncryption + audit

Encryption Standards

// Data encryption utilities
import CryptoJS from 'crypto-js';

// Client-side encryption for sensitive fields (when needed)
export function encryptSensitiveData(data: string, key: string): string {
  return CryptoJS.AES.encrypt(data, key).toString();
}

export function decryptSensitiveData(encryptedData: string, key: string): string {
  const bytes = CryptoJS.AES.decrypt(encryptedData, key);
  return bytes.toString(CryptoJS.enc.Utf8);
}

// Hash sensitive data for logging
export function hashForLogging(sensitiveValue: string): string {
  return CryptoJS.SHA256(sensitiveValue).toString().substring(0, 8) + '...';
}

Data Handling Rules

// ❌ Never log sensitive data
console.log('User password:', password); // DANGEROUS

// ✅ Log sanitized data
console.log('Login attempt for user:', hashForLogging(email));

// ❌ Never store credentials in code
const API_KEY = 'your-secret-key-here'; // DANGEROUS - never hardcode!

// ✅ Use environment variables
const API_KEY = import.meta.env.VITE_API_KEY;

// ❌ Never expose tokens in URLs
const url = `/api/data?token=${token}`; // DANGEROUS

// ✅ Use headers for authentication
const response = await fetch('/api/data', {
  headers: { Authorization: `Bearer ${token}` },
});

Input Validation

Validation with Zod

// src/lib/validation/schemas.ts
import { z } from 'zod';

// Event validation schema
export const eventSchema = z.object({
  name: z
    .string()
    .min(1, 'Name is required')
    .max(200, 'Name must be 200 characters or less')
    .regex(/^[\w\s\-.,!?()]+$/, 'Name contains invalid characters'),

  description: z
    .string()
    .max(2000, 'Description must be 2000 characters or less')
    .optional(),

  startDate: z
    .date()
    .min(new Date('2020-01-01'), 'Start date cannot be before 2020'),

  endDate: z.date(),

  status: z.enum(['on-track', 'at-risk', 'delayed']),

  location: z
    .string()
    .min(1, 'Location is required')
    .max(200, 'Location must be 200 characters or less'),
}).refine((data) => data.endDate >= data.startDate, {
  message: 'End date must be after start date',
  path: ['endDate'],
});

// User input sanitization
export const sanitizeInput = (input: string): string => {
  return input
    .trim()
    .replace(/[<>]/g, '') // Remove potential HTML tags
    .slice(0, 10000); // Limit length
};

// Email validation
export const emailSchema = z
  .string()
  .email('Invalid email address')
  .toLowerCase()
  .max(254, 'Email too long');

// File upload validation
export const fileUploadSchema = z.object({
  name: z.string().max(255),
  size: z.number().max(10 * 1024 * 1024, 'File must be less than 10MB'),
  type: z.enum([
    'application/pdf',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'image/png',
    'image/jpeg',
  ], { errorMap: () => ({ message: 'Invalid file type' }) }),
});

Form Validation

// src/components/forms/EventForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { eventSchema } from '@/lib/validation/schemas';

type EventFormData = z.infer<typeof eventSchema>;

export function EventForm({ onSubmit }: { onSubmit: (data: EventFormData) => void }) {
  const form = useForm<EventFormData>({
    resolver: zodResolver(eventSchema),
    defaultValues: {
      name: '',
      description: '',
      status: 'on-track',
      location: '',
    },
  });

  return (
    <form onSubmit={form.handleSubmit(onSubmit)}>
      {/* Form fields with validation */}
      <input
        {...form.register('name')}
        aria-invalid={!!form.formState.errors.name}
      />
      {form.formState.errors.name && (
        <span role="alert">{form.formState.errors.name.message}</span>
      )}
    </form>
  );
}

SQL Injection Prevention

// Backend: Use parameterized queries
// ✅ Safe: Parameterized query
const result = await db.query(
  'SELECT * FROM events WHERE id = $1 AND status = $2',
  [eventId, status]
);

// ❌ Dangerous: String concatenation
const result = await db.query(
  `SELECT * FROM events WHERE id = '${eventId}'` // SQL INJECTION RISK!
);

// Parse Server: Use query constraints
// ✅ Safe: Parse Query
const query = new Parse.Query('Event');
query.equalTo('id', eventId);
query.equalTo('status', status);
const results = await query.find();

Output Encoding

XSS Prevention

React automatically escapes content, but be careful with:

// ✅ Safe: React auto-escapes
<div>{userContent}</div>

// ⚠️ Dangerous: dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userContent }} /> // XSS RISK!

// ✅ If HTML is required, sanitize first
import DOMPurify from 'dompurify';

const sanitizedHtml = DOMPurify.sanitize(userContent, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
  ALLOWED_ATTR: ['href'],
});

<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />

URL Handling

// ✅ Safe: Validate URLs
const isValidUrl = (url: string): boolean => {
  try {
    const parsed = new URL(url);
    return ['http:', 'https:'].includes(parsed.protocol);
  } catch {
    return false;
  }
};

// ✅ Safe: Use href only after validation
{isValidUrl(link.url) && (
  <a href={link.url} rel="noopener noreferrer" target="_blank">
    {link.text}
  </a>
)}

// ❌ Dangerous: Unvalidated URL
<a href={userProvidedUrl}>Click here</a> // Could be javascript: protocol

Session Management

Session Security

// Session configuration
const SESSION_CONFIG = {
  // Session timeout: 8 hours for interactive sessions
  INTERACTIVE_TIMEOUT: 8 * 60 * 60 * 1000,

  // Idle timeout: 30 minutes of inactivity
  IDLE_TIMEOUT: 30 * 60 * 1000,

  // Absolute timeout: 24 hours maximum
  ABSOLUTE_TIMEOUT: 24 * 60 * 60 * 1000,
};

// Session management hook
export function useSessionManagement() {
  const { logout } = useAuth();
  const lastActivityRef = useRef(Date.now());

  useEffect(() => {
    // Track user activity
    const updateActivity = () => {
      lastActivityRef.current = Date.now();
    };

    const events = ['mousedown', 'keydown', 'scroll', 'touchstart'];
    events.forEach((event) => window.addEventListener(event, updateActivity));

    // Check for idle timeout
    const interval = setInterval(() => {
      const idleTime = Date.now() - lastActivityRef.current;
      if (idleTime > SESSION_CONFIG.IDLE_TIMEOUT) {
        logout();
        toast.warning('Session expired due to inactivity');
      }
    }, 60 * 1000);

    return () => {
      events.forEach((event) => window.removeEventListener(event, updateActivity));
      clearInterval(interval);
    };
  }, [logout]);
}

Secure Storage

// Use sessionStorage over localStorage for sensitive data
// sessionStorage is cleared when tab closes

// ✅ Safe: Session-scoped storage
sessionStorage.setItem('authToken', token);

// ⚠️ Caution: Persistent storage
localStorage.setItem('authToken', token); // Survives browser close

// For tokens, prefer HttpOnly cookies (set by backend)
// For non-sensitive preferences, localStorage is acceptable
localStorage.setItem('theme', 'dark'); // OK - not sensitive

API Security

Request Security

// src/lib/api.ts
import axios from 'axios';

export const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
  timeout: 30000,
  withCredentials: true, // Include cookies for CSRF
  headers: {
    'Content-Type': 'application/json',
  },
});

// Add CSRF token to requests
apiClient.interceptors.request.use((config) => {
  const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
  if (csrfToken) {
    config.headers['X-CSRF-Token'] = csrfToken;
  }
  return config;
});

// Add correlation ID for request tracing
apiClient.interceptors.request.use((config) => {
  config.headers['X-Correlation-ID'] = crypto.randomUUID();
  return config;
});

Rate Limiting

// Backend rate limiting configuration
const rateLimitConfig = {
  // General API requests
  general: {
    windowMs: 60 * 1000, // 1 minute
    max: 100, // 100 requests per minute
  },

  // Authentication endpoints
  auth: {
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 5, // 5 attempts per 15 minutes
  },

  // File uploads
  uploads: {
    windowMs: 60 * 1000,
    max: 10, // 10 uploads per minute
  },
};

Response Security Headers

// Backend: Security headers middleware
app.use((req, res, next) => {
  // Prevent MIME sniffing
  res.setHeader('X-Content-Type-Options', 'nosniff');

  // Prevent clickjacking
  res.setHeader('X-Frame-Options', 'DENY');

  // XSS protection (legacy browsers)
  res.setHeader('X-XSS-Protection', '1; mode=block');

  // Content Security Policy
  res.setHeader('Content-Security-Policy', [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline'",
    "style-src 'self' 'unsafe-inline'",
    "img-src 'self' data: https:",
    "font-src 'self'",
    "connect-src 'self' https://api.example.com",
    "frame-ancestors 'none'",
  ].join('; '));

  // Strict Transport Security
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

  // Referrer Policy
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');

  next();
});

Frontend Security

Content Security Policy

<!-- index.html -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self' https://api.example.com https://login.microsoftonline.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
">

Secure Component Patterns

// Avoid exposing sensitive data in component state
// ❌ Bad: Storing sensitive data in component
const [creditCard, setCreditCard] = useState(user.creditCard);

// ✅ Good: Only store what's needed
const [last4Digits] = useState(user.cardLast4);

// Prevent sensitive data in console/devtools
if (import.meta.env.PROD) {
  console.log = () => {};
  console.debug = () => {};
}

// Secure event handlers
const handleSubmit = async (data: FormData) => {
  try {
    await submitForm(data);
  } catch (error) {
    // Don't expose internal errors to users
    if (error instanceof ValidationError) {
      setError(error.message);
    } else {
      setError('An unexpected error occurred. Please try again.');
      // Log full error server-side
      logError(error);
    }
  }
};

Dependency Security

Dependency Management

// package.json
{
  "scripts": {
    "audit": "npm audit",
    "audit:fix": "npm audit fix",
    "audit:ci": "npm audit --audit-level=high",
    "deps:check": "npm-check-updates",
    "deps:update": "npm-check-updates -u && npm install"
  }
}

Automated Security Scanning

# .github/workflows/security.yml
name: Security Scan

on:
  push:
    branches: [main, dev]
  pull_request:
  schedule:
    - cron: '0 0 * * *' # Daily

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Run npm audit
        run: npm audit --audit-level=high

      - name: Run Snyk security scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

  codeql:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: javascript, typescript

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3

Known Vulnerability Policy

SeverityResponse TimeAction
Critical24 hoursImmediate patch or mitigation
High7 daysPlanned patch
Medium30 daysNext release
Low90 daysBacklog

Security Testing

Security Test Types

Test TypeFrequencyTools
Static AnalysisEvery commitESLint security plugins, CodeQL
Dependency ScanDailynpm audit, Snyk
Dynamic AnalysisWeeklyOWASP ZAP
Penetration TestQuarterlyExternal security firm
Code ReviewEvery PRManual review

Security Testing Scripts

// e2e/tests/security.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Security Tests', () => {
  test('should not expose sensitive headers', async ({ page }) => {
    const response = await page.goto('/');
    const headers = response?.headers();

    expect(headers?.['x-powered-by']).toBeUndefined();
    expect(headers?.['server']).toBeUndefined();
  });

  test('should have security headers', async ({ page }) => {
    const response = await page.goto('/');
    const headers = response?.headers();

    expect(headers?.['x-frame-options']).toBe('DENY');
    expect(headers?.['x-content-type-options']).toBe('nosniff');
    expect(headers?.['strict-transport-security']).toBeDefined();
  });

  test('should redirect HTTP to HTTPS', async ({ page }) => {
    // In production only
    if (process.env.NODE_ENV === 'production') {
      const response = await page.goto('http://app.example.com');
      expect(response?.url()).toMatch(/^https:/);
    }
  });

  test('should require authentication for protected routes', async ({ page }) => {
    await page.goto('/dashboard');
    await expect(page).toHaveURL(/\/login/);
  });

  test('should handle XSS payloads safely', async ({ page }) => {
    await page.goto('/login');

    // Try XSS payload in input
    const xssPayload = '<script>alert("xss")</script>';
    await page.fill('[name="email"]', xssPayload);
    await page.fill('[name="password"]', 'test');
    await page.click('button[type="submit"]');

    // Verify no alert dialog appeared
    const dialogs: string[] = [];
    page.on('dialog', (dialog) => dialogs.push(dialog.type()));

    expect(dialogs).not.toContain('alert');
  });
});

Incident Response

Incident Severity Levels

LevelDescriptionResponse TimeEscalation
P1Data breach, service downImmediateExecutive team
P2Security vulnerability exploited1 hourSecurity lead
P3Potential vulnerability found24 hoursDev team
P4Security improvement needed1 weekBacklog

Incident Response Process

┌─────────────────────────────────────────────────────────────────────────────┐
│                        Incident Response Process                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│  1. Detection & Reporting                                                     │
│     └── Automated monitoring / User report / Security scan                   │
│              │                                                                │
│              ▼                                                                │
│  2. Triage & Assessment                                                       │
│     └── Assess severity, impact, and scope                                   │
│              │                                                                │
│              ▼                                                                │
│  3. Containment                                                               │
│     └── Isolate affected systems, prevent spread                             │
│              │                                                                │
│              ▼                                                                │
│  4. Eradication                                                               │
│     └── Remove threat, patch vulnerability                                   │
│              │                                                                │
│              ▼                                                                │
│  5. Recovery                                                                  │
│     └── Restore services, verify functionality                               │
│              │                                                                │
│              ▼                                                                │
│  6. Post-Incident Review                                                      │
│     └── Document lessons learned, update procedures                          │
│                                                                               │
└─────────────────────────────────────────────────────────────────────────────┘

Security Contacts

RoleResponsibilityContact
Security LeadIncident coordinationsecurity@[organization].com
On-Call EngineerTechnical responseoncall@[organization].com
LegalCompliance, disclosurelegal@[organization].com
CommunicationsExternal communicationcomms@[organization].com

Security Checklist

Development Checklist

  • [ ] No secrets in code or version control
  • [ ] All user input validated and sanitized
  • [ ] All outputs properly encoded
  • [ ] Authentication required for protected routes
  • [ ] Authorization checked for all actions
  • [ ] HTTPS enforced
  • [ ] Security headers configured
  • [ ] Dependencies scanned for vulnerabilities
  • [ ] Error messages don't expose sensitive info
  • [ ] Logging doesn't contain sensitive data

Code Review Security Checklist

  • [ ] No hardcoded credentials or secrets
  • [ ] Input validation on all user data
  • [ ] Parameterized queries for database access
  • [ ] Proper output encoding
  • [ ] Authentication/authorization checks
  • [ ] No sensitive data in logs or errors
  • [ ] Secure defaults used
  • [ ] No unnecessary dependencies added
  • [ ] CORS properly configured
  • [ ] Rate limiting in place

Pre-Deployment Checklist

  • [ ] Security scan passed (no high/critical issues)
  • [ ] Penetration test completed (if applicable)
  • [ ] All security headers configured
  • [ ] SSL/TLS properly configured
  • [ ] Secrets in secure vault (not env files)
  • [ ] Audit logging enabled
  • [ ] Monitoring and alerting configured
  • [ ] Incident response plan reviewed
  • [ ] Backup and recovery tested
  • [ ] Compliance requirements verified

Previous
Test-Driven Development