Back to Blog
Security Testing
February 3, 2026
12 min read

Automated Security Testing: Building a Continuous Security Validation Framework

Learn how to implement automated security testing across your CI/CD pipeline, from unit test security assertions to production monitoring and regression testing.

Executive Summary

Manual security testing doesn't scale. This guide shows how to build comprehensive automated security testing that runs continuously, catches regressions early, and provides rapid feedback to developers without slowing delivery velocity.

The Automated Security Testing Pyramid

Similar to the testing pyramid for functional tests, security testing should have layers optimized for speed and coverage:

        ┌─────────────────────┐
        │  Manual Pentests   │  ← Quarterly/Annual
        │   (Comprehensive)   │
        └─────────────────────┘
               ▲
               │
        ┌──────────────────────┐
        │   DAST/IAST Tests    │  ← Weekly/PR
        │  (Dynamic Scanning)   │
        └──────────────────────┘
               ▲
               │
        ┌──────────────────────────┐
        │  SAST + SCA Scans        │  ← Every Commit/PR
        │ (Static Analysis)         │
        └──────────────────────────┘
               ▲
               │
        ┌────────────────────────────────┐
        │  Security Unit Tests           │  ← Every Commit
        │  (Input Validation, AuthZ)     │
        └────────────────────────────────┘

1. Security Unit Tests: The Foundation

Write unit tests that explicitly verify security properties:

Example: Testing Authorization Logic

// tests/security/authorization.test.ts
import { describe, it, expect } from '@jest/globals';
import { checkUserAccess } from '../auth/authorization';

describe('Authorization Security Tests', () => {
  it('should deny access to resources without ownership', async () => {
    const user = { id: 'user-123', role: 'user' };
    const resource = { id: 'doc-456', ownerId: 'user-789' };
    
    const hasAccess = await checkUserAccess(user, resource, 'read');
    
    expect(hasAccess).toBe(false);
  });

  it('should prevent privilege escalation via role modification', async () => {
    const user = { id: 'user-123', role: 'user' };
    
    // Attempt to modify role in request
    const modifiedUser = { ...user, role: 'admin' };
    
    const hasAdminAccess = await checkUserAccess(modifiedUser, {}, 'admin:panel');
    
    // Should verify against DB, not trust client data
    expect(hasAdminAccess).toBe(false);
  });

  it('should sanitize SQL inputs to prevent injection', () => {
    const maliciousInput = "1' OR '1'='1";
    
    expect(() => {
      queryUser(maliciousInput);
    }).not.toThrow();
    
    // Should use parameterized queries
    expect(queryUser(maliciousInput)).toBeNull();
  });
});

Example: Testing Cryptographic Functions

// tests/security/crypto.test.ts
describe('Cryptography Security Tests', () => {
  it('should use strong password hashing (bcrypt with cost >= 12)', async () => {
    const password = 'TestP@ssw0rd';
    const hash = await hashPassword(password);
    
    // Verify bcrypt format
    expect(hash).toMatch(/^$2[aby]$d{2}$/);
    
    // Extract cost factor
    const cost = parseInt(hash.split('$')[2]);
    expect(cost).toBeGreaterThanOrEqual(12);
  });

  it('should use cryptographically secure random for tokens', () => {
    const token1 = generateToken();
    const token2 = generateToken();
    
    expect(token1).not.toEqual(token2);
    expect(token1.length).toBeGreaterThanOrEqual(32);
    
    // Should use crypto.randomBytes, not Math.random()
    expect(token1).toMatch(/^[a-f0-9]{64}$/);
  });

  it('should reject weak encryption algorithms', () => {
    expect(() => {
      encrypt('data', 'DES'); // Weak cipher
    }).toThrow('Unsupported algorithm');
    
    expect(() => {
      encrypt('data', 'AES-256-GCM'); // Strong cipher
    }).not.toThrow();
  });
});

2. Static Application Security Testing (SAST)

Automated static analysis catches common vulnerabilities in source code:

SAST Tool Selection by Language:

JavaScript/TypeScript:

Semgrep, ESLint security plugins, NodeJsScan, retire.js

Python:

Bandit, Semgrep, PyLint security extensions, Safety

Java:

SpotBugs with FindSecBugs, PMD, SonarQube, Checkmarx

C/C++:

Clang Static Analyzer, Cppcheck, Coverity, Fortify

Go:

Gosec, StaticCheck, Semgrep

Example: Semgrep Custom Security Rules

# .semgrep/rules/custom-security.yml
rules:
  - id: hardcoded-secret
    pattern: |
      const $VAR = "$SECRET"
    message: Possible hardcoded secret detected
    severity: ERROR
    languages: [javascript, typescript]
    
  - id: sql-injection-risk
    pattern: |
      db.query("... " + $INPUT + " ...")
    message: Potential SQL injection - use parameterized queries
    severity: ERROR
    fix: db.query("... ?", [$INPUT])
    
  - id: missing-authentication
    pattern: |
      app.$METHOD($PATH, async ($REQ, $RES) => {
        ...
      })
    pattern-not-inside: |
      app.$METHOD($PATH, authenticateMiddleware, ...)
    message: Route missing authentication middleware
    severity: WARNING
    
  - id: weak-crypto
    pattern-either:
      - pattern: crypto.createCipher($ALG, ...)
      - pattern: crypto.pbkdf2($PASS, $SALT, $ITER, ...)
        metavariable-comparison:
          comparison: $ITER < 100000
    message: Weak cryptography detected
    severity: ERROR

3. Software Composition Analysis (SCA)

Automatically detect vulnerabilities in third-party dependencies:

SCA Implementation Strategy:

  • Pre-commit Hooks: Lightweight checks for new dependencies (npm audit, pip-audit)
  • PR Checks: Comprehensive SCA scans blocking merge on critical CVEs
  • Scheduled Scans: Daily/weekly scans detecting newly disclosed vulnerabilities
  • Dependency Updates: Automated PRs for security patches (Dependabot, Renovate)
  • License Compliance: Flag copyleft licenses incompatible with your usage

Example: Multi-Tool SCA Pipeline

# GitHub Actions workflow
name: Dependency Security Scan

on:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM
  pull_request:

jobs:
  sca-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: NPM Audit
        run: |
          npm audit --audit-level=high --json > npm-audit.json
          npm audit --audit-level=high
        continue-on-error: true
        
      - name: Snyk Test
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --json-file-output=snyk-results.json
          
      - name: OWASP Dependency Check
        run: |
          docker run --rm -v $(pwd):/src owasp/dependency-check \
            --scan /src --format JSON --out /src/dependency-check-report.json
            
      - name: Grype Scan
        uses: anchore/scan-action@v3
        with:
          path: "."
          fail-build: true
          severity-cutoff: high
          
      - name: License Compliance
        run: |
          npx license-checker --json --out licenses.json
          npx license-compliance-checker --allow MIT,Apache-2.0,BSD-3-Clause
          
      - name: Upload Results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: snyk-results.json

4. Dynamic Application Security Testing (DAST)

Test running applications by simulating attacks:

DAST Automation Approaches:

Authenticated Scanning

Configure DAST tools with valid credentials to test authenticated endpoints and authorization logic

API-Specific Testing

Import OpenAPI/Swagger specs for comprehensive API endpoint coverage

Staging Environment Scans

Run aggressive scans in staging to avoid production impact

Attack Simulation

Test for OWASP Top 10: SQLi, XSS, SSRF, XXE, insecure deserialization

Example: OWASP ZAP Automated Scan

# ZAP automation framework configuration
# zap-config.yml

env:
  contexts:
    - name: "staging-app"
      urls:
        - "https://staging.company.com"
      includePaths:
        - "https://staging.company.com/api/.*"
      authentication:
        method: "json"
        loginUrl: "https://staging.company.com/api/auth/login"
        loginBody: '{"username":"[email protected]","password":"test123"}'
        loggedInIndicator: "\Qtoken\E"
        
jobs:
  - type: "passiveScan-config"
    parameters:
      maxAlertsPerRule: 10
      scanOnlyInScope: true
      
  - type: "spider"
    parameters:
      maxDuration: 10
      maxDepth: 5
      
  - type: "activeScan"
    parameters:
      maxRuleDurationInMins: 5
      maxScanDurationInMins: 30
      scanPolicyName: "API-Scan-Policy"
      
  - type: "report"
    parameters:
      template: "traditional-json-plus"
      reportDir: "/zap/reports"
      reportFile: "zap-report"
      
  - type: "alertFilter"
    alertFilters:
      - ruleId: 10202  # Absence of Anti-CSRF tokens
        newRisk: "Info"  # Downgrade if using JWT
      - ruleId: 90022  # Application Error Disclosure
        url: "https://staging.company.com/health"
        newRisk: "Info"  # Health endpoint expected

5. Container and Infrastructure Security Testing

Automated Container Security Checks:

  • Image Scanning: Trivy, Grype, Clair for vulnerability detection
  • Dockerfile Linting: Hadolint for security best practices
  • Runtime Security: Falco for detecting anomalous container behavior
  • IaC Scanning: Checkov, tfsec for Terraform/CloudFormation
  • Kubernetes Security: KubeSec, Polaris, Kube-bench

6. Security Regression Testing

Ensure previously fixed vulnerabilities don't resurface:

// tests/security/regressions.test.ts
describe('Security Regression Tests', () => {
  it('CVE-2024-001: Should prevent path traversal in file upload', async () => {
    const response = await request(app)
      .post('/api/upload')
      .attach('file', Buffer.from('test'), '../../../etc/passwd');
    
    expect(response.status).toBe(400);
    expect(response.body.error).toContain('Invalid filename');
  });

  it('CVE-2024-002: Should validate JWT signature before trusting claims', async () => {
    const tamperedToken = createTamperedJWT({ userId: 'admin', role: 'admin' });
    
    const response = await request(app)
      .get('/api/admin/users')
      .set('Authorization', `Bearer ${tamperedToken}`);
    
    expect(response.status).toBe(401);
  });

  it('Issue-#1234: Should rate-limit password reset requests', async () => {
    const requests = Array(6).fill(null).map(() =>
      request(app).post('/api/auth/reset-password').send({ email: '[email protected]' })
    );
    
    const responses = await Promise.all(requests);
    const tooManyRequests = responses.filter(r => r.status === 429);
    
    expect(tooManyRequests.length).toBeGreaterThan(0);
  });
});

7. Measuring Automated Testing Effectiveness

Key Metrics:

Security Test Coverage

Percentage of security requirements with automated tests. Target: >80%

Mean Time to Detection (MTTD)

Average time from vulnerability introduction to detection. Target: <24 hours

False Positive Rate

Percentage of findings that aren't actual vulnerabilities. Target: <10%

Security Debt Trend

Total known unresolved vulnerabilities over time. Should decrease consistently

Test Execution Time

Total time for security test suite. Target: <15 minutes for critical path

Conclusion: Building a Security Testing Culture

Automated security testing is most effective when developers write security tests alongside functional tests, SAST/SCA tools provide fast feedback in CI, and DAST validates deployed applications. Start small—add security unit tests first, then layer in SAST, SCA, and finally DAST. Focus on reducing noise (false positives) and providing actionable remediation guidance.

Implementation Roadmap

Week 1-2: Security Unit Tests

Start writing tests for authentication, authorization, input validation

Week 3-4: SAST Integration

Add Semgrep or equivalent to PR checks, tune for low false positives

Month 2: SCA Automation

Enable Snyk/Dependabot for dependency vulnerability scanning

Month 3: DAST Setup

Configure ZAP or Burp for staging environment scans

Month 4+: Optimization

Add regression tests, tune thresholds, measure and improve coverage

Centralize Automated Security Testing

Securus Mind's Security Testing module orchestrates SAST, SCA, DAST, and custom security tests across your entire application portfolio with unified reporting, deduplication, and automated triaging.

Schedule a Demo