TechLead
Lesson 9 of 9
5 min read
Web Security

Security Testing & Monitoring

Implement security testing in your CI/CD pipeline and set up monitoring for security events.

Security Testing

Security testing should be automated and integrated into your development workflow. Find vulnerabilities before attackers do.

Static Application Security Testing (SAST)

# ESLint security plugin
npm install --save-dev eslint-plugin-security

# .eslintrc.js
module.exports = {
  plugins: ['security'],
  extends: ['plugin:security/recommended'],
  rules: {
    'security/detect-object-injection': 'error',
    'security/detect-non-literal-regexp': 'error',
    'security/detect-unsafe-regex': 'error',
    'security/detect-buffer-noassert': 'error',
    'security/detect-child-process': 'warn',
    'security/detect-disable-mustache-escape': 'error',
    'security/detect-eval-with-expression': 'error',
    'security/detect-no-csrf-before-method-override': 'error',
    'security/detect-possible-timing-attacks': 'error',
  },
};

Semgrep

# Install Semgrep
pip install semgrep

# Run security rules
semgrep --config auto .

# Run specific rulesets
semgrep --config p/security-audit .
semgrep --config p/javascript .

# Custom rules
# semgrep.yml
rules:
  - id: hardcoded-password
    pattern: password = "..."
    message: "Hardcoded password detected"
    severity: ERROR
    languages: [javascript, typescript]

Dynamic Application Security Testing (DAST)

# OWASP ZAP Docker
docker run -t owasp/zap2docker-stable zap-baseline.py -t https://your-app.com

# Generate HTML report
docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable \
  zap-baseline.py -t https://your-app.com -r report.html

Dependency Scanning

# GitHub Actions workflow
name: Security Scan

on: [push, pull_request]

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

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

      - name: Run Snyk to check vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          severity: 'HIGH,CRITICAL'

Security Headers Testing

// Test security headers with Playwright
import { test, expect } from '@playwright/test';

test('security headers are set correctly', async ({ page }) => {
  const response = await page.goto('https://your-app.com');
  const headers = response.headers();

  expect(headers['strict-transport-security']).toContain('max-age=');
  expect(headers['x-frame-options']).toBe('DENY');
  expect(headers['x-content-type-options']).toBe('nosniff');
  expect(headers['content-security-policy']).toBeDefined();
  expect(headers['referrer-policy']).toBeDefined();
});

test('no sensitive data in response', async ({ page }) => {
  const response = await page.goto('https://your-app.com/api/user');
  const body = await response.json();

  // Ensure password is never returned
  expect(body.password).toBeUndefined();
  expect(body.passwordHash).toBeUndefined();
});

Penetration Testing Automation

// Custom security tests
describe('Security Tests', () => {
  test('SQL injection protection', async () => {
    const maliciousInputs = [
      "'; DROP TABLE users; --",
      "' OR '1'='1",
      "1; SELECT * FROM users",
    ];

    for (const input of maliciousInputs) {
      const response = await fetch('/api/search', {
        method: 'POST',
        body: JSON.stringify({ query: input }),
      });

      // Should not return error indicating SQL execution
      expect(response.status).not.toBe(500);
      const data = await response.json();
      expect(data.error).not.toContain('SQL');
    }
  });

  test('XSS protection', async () => {
    const xssPayloads = [
      '<script>alert(1)</script>',
      '<img src=x onerror=alert(1)>',
      'javascript:alert(1)',
    ];

    for (const payload of xssPayloads) {
      const response = await fetch('/api/comment', {
        method: 'POST',
        body: JSON.stringify({ text: payload }),
      });

      const data = await response.json();
      // Payload should be sanitized or rejected
      expect(data.text).not.toContain('