Skip to main content
Security Testing·Lesson 5 of 5

Security Automation

Manual security testing does not scale. To catch vulnerabilities continuously, you need to embed security checks into your development workflow. This is the practice of DevSecOps -- shifting security left into the CI/CD pipeline.

The DevSecOps Pipeline

Code ──> Commit ──> Build ──> Test ──> Deploy ──> Monitor
                                               
                                               
 SAST    Secret    SCA/Dep   DAST    Config     Runtime
 Lint    Scanning  Audit     Scan    Audit      Protection

Each stage adds a security layer:

StageTool TypeWhat It Catches
CodeSASTCode-level vulnerabilities
CommitSecretsHardcoded credentials
BuildSCAVulnerable dependencies
TestDASTRuntime vulnerabilities
DeployConfigInfrastructure misconfigurations
MonitorRuntimeActive attacks, anomalies

Secret Scanning with Gitleaks

Prevent credentials from entering your repository.

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

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  secret-scanning:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Custom Gitleaks Configuration

# .gitleaks.toml
title = "Custom Gitleaks Config"

[[rules]]
id = "firebase-api-key"
description = "Firebase API Key"
regex = '''AIza[0-9A-Za-z\-_]{35}'''
tags = ["key", "firebase"]

[[rules]]
id = "generic-api-key"
description = "Generic API Key"
regex = '''(?i)(api[_-]?key|apikey)\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]'''
tags = ["key", "api"]

[allowlist]
paths = [
  '''\.env\.example''',
  '''docs/.*\.md'''
]

Static Application Security Testing (SAST)

Analyze source code for vulnerabilities without running the application.

ESLint Security Plugin for JavaScript/TypeScript

# Install security-focused ESLint plugin
pnpm add -D eslint-plugin-security
// eslint.config.js
import security from 'eslint-plugin-security';

export default [
  {
    plugins: { security },
    rules: {
      'security/detect-object-injection': 'warn',
      'security/detect-non-literal-regexp': 'warn',
      'security/detect-unsafe-regex': 'error',
      'security/detect-buffer-noassert': 'error',
      'security/detect-eval-with-expression': 'error',
      'security/detect-no-csrf-before-method-override': 'error',
      'security/detect-possible-timing-attacks': 'warn',
    }
  }
];

Semgrep for Multi-Language SAST

# In your CI pipeline
  sast-scanning:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/javascript
            p/typescript
            p/react

Custom Semgrep Rules

# .semgrep/custom-rules.yml
rules:
  - id: no-innerhtml
    patterns:
      - pattern: $EL.innerHTML = $VALUE
    message: "Using innerHTML can lead to XSS. Use textContent or a sanitizer."
    languages: [javascript, typescript]
    severity: ERROR

  - id: no-hardcoded-secrets
    patterns:
      - pattern: |
          const $KEY = "..."
      - metavariable-regex:
          metavariable: $KEY
          regex: ".*(password|secret|api_key|token).*"
    message: "Possible hardcoded secret in variable $KEY"
    languages: [javascript, typescript]
    severity: ERROR

Dependency Scanning (SCA)

Check your dependencies for known vulnerabilities.

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

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

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Audit dependencies
        run: pnpm audit --audit-level=high

      - name: Check for outdated packages
        run: pnpm outdated || true

Automated Dependency Updates with Renovate

// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"],
    "assignees": ["security-team"]
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true,
      "automergeType": "branch"
    }
  ]
}

Dynamic Application Security Testing (DAST)

Scan the running application for vulnerabilities.

  dast-scanning:
    runs-on: ubuntu-latest
    needs: [deploy-staging]
    steps:
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.12.0
        with:
          target: 'https://staging.target-app.com'
          rules_file_name: '.zap/rules.tsv'
          fail_action: true

      - name: Upload ZAP Report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: zap-report
          path: report_html.html

ZAP Rules Configuration

# .zap/rules.tsv
# Rule ID	Action	Description
10038	IGNORE	Content Security Policy (intentional inline scripts)
10063	WARN	Feature Policy Header Not Set
10098	FAIL	Cross-Domain Misconfiguration
40012	FAIL	Cross Site Scripting (Reflected)
40014	FAIL	Cross Site Scripting (Persistent)
90033	FAIL	Loosely Scoped Cookie

Complete Security Pipeline

Here is a full pipeline that combines all stages:

name: Security Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # Weekly Monday scan

jobs:
  secrets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2

  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: returntocorp/semgrep-action@v1
        with:
          config: p/owasp-top-ten

  dependencies:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pnpm install --frozen-lockfile
      - run: pnpm audit --audit-level=high

  dast:
    runs-on: ubuntu-latest
    needs: [secrets, sast, dependencies]
    steps:
      - uses: zaproxy/action-baseline@v0.12.0
        with:
          target: 'https://staging.target-app.com'

  security-gate:
    runs-on: ubuntu-latest
    needs: [secrets, sast, dependencies, dast]
    steps:
      - name: Security Gate Check
        run: |
          echo "All security checks passed"
          echo "Safe to deploy to production"

Monitoring and Alerting

Security does not end at deployment.

// Example: Runtime security monitoring middleware (Express.js)
function securityMonitor(req, res, next) {
  const suspiciousPatterns = [
    /(\%27)|(\')|(\-\-)/i,          // SQL injection
    /<script[\s>]/i,                 // XSS attempts
    /\.\.\//,                        // Path traversal
    /etc\/passwd/i,                  // System file access
  ];

  const input = `${req.url} ${JSON.stringify(req.body)}`;

  for (const pattern of suspiciousPatterns) {
    if (pattern.test(input)) {
      console.warn(`[SECURITY] Suspicious request detected`, {
        ip: req.ip,
        method: req.method,
        url: req.url,
        pattern: pattern.toString(),
        timestamp: new Date().toISOString()
      });
      // Log but don't block -- WAF handles blocking
      break;
    }
  }

  next();
}

Key Takeaways

  • Shift security left by integrating checks into every pipeline stage
  • Secret scanning prevents credentials from entering version control
  • SAST catches code vulnerabilities before they reach production
  • Dependency scanning flags known vulnerabilities in third-party packages
  • DAST tests the running application for exploitable issues
  • A security gate ensures nothing deploys without passing all checks
  • Runtime monitoring catches attacks that bypass pre-deployment testing

You have completed the Security Testing course. You now have the skills to identify, exploit, and automate the detection of security vulnerabilities in web applications.