devsecops-practices

Comprehensive guidance for integrating security throughout the software development lifecycle using DevSecOps principles.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "devsecops-practices" with this command: npx skills add melodic-software/claude-code-plugins/melodic-software-claude-code-plugins-devsecops-practices

DevSecOps Practices

Comprehensive guidance for integrating security throughout the software development lifecycle using DevSecOps principles.

When to Use This Skill

  • Implementing shift-left security practices

  • Setting up SAST tools (Semgrep, CodeQL, SonarQube)

  • Configuring DAST scanning (OWASP ZAP, Burp Suite)

  • Integrating security gates in CI/CD pipelines

  • Building vulnerability management workflows

  • Establishing security champions programs

  • Creating secure SDLC processes

Quick Reference

DevSecOps Maturity Levels

Level Characteristics Key Practices

Level 1: Initial Manual security reviews, ad-hoc testing Basic vulnerability scanning, security training

Level 2: Managed Automated scanning in CI/CD, defined processes SAST integration, security gates

Level 3: Defined Security embedded in all phases, metrics tracked DAST/IAST, threat modeling, SLAs

Level 4: Measured Continuous monitoring, risk-based decisions Full automation, security dashboards

Level 5: Optimizing Predictive security, continuous improvement AI-assisted, chaos engineering

Security Testing Types

Type When What It Finds Tools

SAST Build time Code vulnerabilities, patterns Semgrep, CodeQL, SonarQube

SCA Build time Dependency vulnerabilities Snyk, Dependabot, npm audit

DAST Runtime Running application vulns OWASP ZAP, Burp Suite

IAST Runtime Combined SAST+DAST Contrast, Seeker

Secrets Commit time Hardcoded credentials Gitleaks, truffleHog

Security Gates by Pipeline Stage

┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Commit │───►│ Build │───►│ Test │───►│ Deploy │───►│Production│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │Secrets │ │SAST │ │DAST │ │Container│ │Runtime │ │Scanning │ │SCA │ │Pen Test │ │Scanning │ │Security │ │Pre-commit │License │ │IAST │ │Config │ │Monitoring └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘

SAST (Static Application Security Testing)

Semgrep Setup

.github/workflows/semgrep.yml

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

jobs: semgrep: runs-on: ubuntu-latest container: image: semgrep/semgrep

steps:
  - uses: actions/checkout@v5

  - name: Run Semgrep
    run: semgrep scan --config auto --sarif --output semgrep.sarif

  - name: Upload SARIF
    uses: github/codeql-action/upload-sarif@v3
    with:
      sarif_file: semgrep.sarif

Semgrep Rules Configuration

.semgrep.yml

rules:

SQL Injection

  • id: sql-injection patterns:
    • pattern-either:
      • pattern: cursor.execute($QUERY % ...)
      • pattern: cursor.execute($QUERY.format(...))
      • pattern: cursor.execute(f"...") message: "Potential SQL injection. Use parameterized queries." severity: ERROR languages: [python]

Hardcoded Secrets

  • id: hardcoded-password pattern-regex: '(?i)(password|passwd|pwd)\s*=\s*["'][^"']{8,}["']' message: "Hardcoded password detected" severity: ERROR languages: [python, javascript, typescript]

Insecure Crypto

  • id: insecure-hash patterns:
    • pattern-either:
      • pattern: hashlib.md5(...)
      • pattern: hashlib.sha1(...) message: "Use SHA-256 or stronger for cryptographic purposes" severity: WARNING languages: [python]

CodeQL Setup

.github/workflows/codeql.yml

name: CodeQL Analysis on: push: branches: [main] pull_request: branches: [main] schedule: - cron: '0 6 * * 1' # Weekly

jobs: analyze: runs-on: ubuntu-latest permissions: security-events: write

strategy:
  matrix:
    language: [javascript, python]

steps:
  - uses: actions/checkout@v5

  - name: Initialize CodeQL
    uses: github/codeql-action/init@v3
    with:
      languages: ${{ matrix.language }}
      queries: +security-extended

  - name: Build (if needed)
    uses: github/codeql-action/autobuild@v3

  - name: Perform Analysis
    uses: github/codeql-action/analyze@v3
    with:
      category: "/language:${{ matrix.language }}"

SonarQube Integration

.github/workflows/sonarqube.yml

name: SonarQube Analysis on: push: branches: [main] pull_request: branches: [main]

jobs: sonarqube: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 # Full history for accurate blame

  - name: SonarQube Scan
    uses: sonarsource/sonarqube-scan-action@master
    env:
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

  - name: Quality Gate Check
    uses: sonarsource/sonarqube-quality-gate-action@master
    timeout-minutes: 5
    env:
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

sonar-project.properties

sonar.projectKey=my-project sonar.organization=my-org

Source paths

sonar.sources=src sonar.tests=tests

Exclusions

sonar.exclusions=/node_modules/,/*.test.js,/vendor/**

Coverage

sonar.javascript.lcov.reportPaths=coverage/lcov.info sonar.python.coverage.reportPaths=coverage.xml

Security hotspots review

sonar.security.hotspots.review.priority=HIGH

DAST (Dynamic Application Security Testing)

OWASP ZAP Integration

.github/workflows/zap.yml

name: OWASP ZAP Scan on: push: branches: [main] schedule: - cron: '0 2 * * 0' # Weekly Sunday 2 AM

jobs: zap-scan: runs-on: ubuntu-latest services: app: image: my-app:latest ports: - 8080:8080

steps:
  - uses: actions/checkout@v5

  - name: ZAP Baseline Scan
    uses: zaproxy/action-baseline@v0.11.0
    with:
      target: 'http://localhost:8080'
      rules_file_name: '.zap/rules.tsv'

  - name: ZAP Full Scan
    uses: zaproxy/action-full-scan@v0.9.0
    with:
      target: 'http://localhost:8080'
      cmd_options: '-a -j'

  - name: Upload Report
    uses: actions/upload-artifact@v4
    with:
      name: zap-report
      path: report_html.html

ZAP Rules Configuration

.zap/rules.tsv

Rule ID Action Description

10010 IGNORE # Cookie No HttpOnly Flag (handled by framework) 10011 WARN # Cookie Without Secure Flag 10015 FAIL # Incomplete or No Cache-control and Pragma 10016 WARN # Web Browser XSS Protection Not Enabled 10017 FAIL # Cross-Domain JavaScript Source File Inclusion 10019 FAIL # Content-Type Header Missing 10020 FAIL # X-Frame-Options Header Not Set 10021 FAIL # X-Content-Type-Options Header Missing 10038 FAIL # Content Security Policy Header Not Set

DAST in Docker Compose

docker-compose.security.yml

version: '3.8'

services: app: build: . ports: - "8080:8080" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 5s timeout: 10s retries: 5

zap: image: ghcr.io/zaproxy/zaproxy:stable depends_on: app: condition: service_healthy volumes: - ./zap-reports:/zap/wrk command: > zap-full-scan.py -t http://app:8080 -r zap-report.html -J zap-report.json -x zap-report.xml

Security Gates

Gate Configuration

using System.Text.Json; using System.Text.Json.Serialization;

/// <summary> /// Security gate enforcement for CI/CD pipelines. /// </summary> public enum Severity { Critical, High, Medium, Low, Info }

public enum GateDecision { Pass, Warn, Fail }

/// <summary> /// Configuration for security gate thresholds. /// </summary> public sealed record SecurityGateConfig { // SAST thresholds public int SastCriticalMax { get; init; } = 0; public int SastHighMax { get; init; } = 0; public int SastMediumMax { get; init; } = 5;

// SCA thresholds
public int ScaCriticalMax { get; init; } = 0;
public int ScaHighMax { get; init; } = 2;
public double ScaCvssThreshold { get; init; } = 7.0;

// DAST thresholds
public int DastCriticalMax { get; init; } = 0;
public int DastHighMax { get; init; } = 1;

// Secrets
public int SecretsAllowed { get; init; } = 0;

// License restrictions
public IReadOnlyList&#x3C;string> ForbiddenLicenses { get; init; } = ["GPL-3.0", "AGPL-3.0"];

}

/// <summary> /// Aggregated results from security scans. /// </summary> public sealed record ScanResults( Dictionary<string, int> SastFindings, Dictionary<string, int> ScaFindings, Dictionary<string, int> DastFindings, int SecretsFound, IReadOnlyList<string> Licenses);

/// <summary> /// Evaluates security gates for CI/CD pipelines. /// </summary> public static class SecurityGateEvaluator { public static (GateDecision Decision, List<string> Reasons) Evaluate( SecurityGateConfig config, ScanResults results) { var reasons = new List<string>(); var decision = GateDecision.Pass;

    // Check SAST
    if (results.SastFindings.GetValueOrDefault("critical", 0) > config.SastCriticalMax)
    {
        decision = GateDecision.Fail;
        reasons.Add($"SAST: {results.SastFindings["critical"]} critical findings (max: {config.SastCriticalMax})");
    }

    if (results.SastFindings.GetValueOrDefault("high", 0) > config.SastHighMax)
    {
        decision = GateDecision.Fail;
        reasons.Add($"SAST: {results.SastFindings["high"]} high findings (max: {config.SastHighMax})");
    }

    // Check SCA
    if (results.ScaFindings.GetValueOrDefault("critical", 0) > config.ScaCriticalMax)
    {
        decision = GateDecision.Fail;
        reasons.Add($"SCA: {results.ScaFindings["critical"]} critical vulnerabilities (max: {config.ScaCriticalMax})");
    }

    // Check secrets
    if (results.SecretsFound > config.SecretsAllowed)
    {
        decision = GateDecision.Fail;
        reasons.Add($"Secrets: {results.SecretsFound} secrets detected");
    }

    // Check licenses
    foreach (var license in results.Licenses)
    {
        if (config.ForbiddenLicenses.Contains(license))
        {
            decision = GateDecision.Fail;
            reasons.Add($"License: Forbidden license {license} detected");
        }
    }

    // Warnings (don't fail but report)
    if (results.SastFindings.GetValueOrDefault("medium", 0) > config.SastMediumMax)
    {
        if (decision == GateDecision.Pass)
            decision = GateDecision.Warn;
        reasons.Add($"SAST: {results.SastFindings["medium"]} medium findings (threshold: {config.SastMediumMax})");
    }

    return (decision, reasons);
}

}

// Usage in CI (console app entry point) public static class SecurityGateCli { public static async Task<int> Main(string[] args) { var jsonPath = args.FirstOrDefault() ?? "scan-results.json"; var json = await File.ReadAllTextAsync(jsonPath); var rawResults = JsonSerializer.Deserialize<RawScanResults>(json)!;

    var results = new ScanResults(
        rawResults.Sast ?? new(),
        rawResults.Sca ?? new(),
        rawResults.Dast ?? new(),
        rawResults.Secrets,
        rawResults.Licenses ?? []);

    var config = new SecurityGateConfig();
    var (decision, reasons) = SecurityGateEvaluator.Evaluate(config, results);

    Console.WriteLine($"Security Gate: {decision.ToString().ToUpper()}");
    foreach (var reason in reasons)
        Console.WriteLine($"  - {reason}");

    return decision == GateDecision.Fail ? 1 : 0;
}

private sealed record RawScanResults(
    [property: JsonPropertyName("sast")] Dictionary&#x3C;string, int>? Sast,
    [property: JsonPropertyName("sca")] Dictionary&#x3C;string, int>? Sca,
    [property: JsonPropertyName("dast")] Dictionary&#x3C;string, int>? Dast,
    [property: JsonPropertyName("secrets")] int Secrets,
    [property: JsonPropertyName("licenses")] List&#x3C;string>? Licenses);

}

GitHub Actions Security Gate

.github/workflows/security-gate.yml

name: Security Gate on: pull_request: branches: [main]

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

  # Run all security scans
  - name: SAST - Semgrep
    uses: semgrep/semgrep-action@v1
    with:
      config: auto
      generateSarif: true

  - name: SCA - npm audit
    run: npm audit --json > npm-audit.json || true

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

  # Aggregate and evaluate
  - name: Evaluate Security Gate
    run: |
      python scripts/security_gate.py \
        --sast-results semgrep.sarif \
        --sca-results npm-audit.json \
        --secrets-results gitleaks.json

  - name: Comment on PR
    if: always()
    uses: actions/github-script@v7
    with:
      script: |
        const fs = require('fs');
        const report = fs.readFileSync('security-report.md', 'utf8');
        github.rest.issues.createComment({
          owner: context.repo.owner,
          repo: context.repo.repo,
          issue_number: context.issue.number,
          body: report
        });

Secrets Scanning

Pre-commit Hook with Gitleaks

.pre-commit-config.yaml

repos:

Gitleaks Configuration

.gitleaks.toml

title = "Gitleaks Configuration"

[extend] useDefault = true

[[rules]] id = "custom-api-key" description = "Custom API Key Pattern" regex = '''(?i)api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9]{32,}['"]?''' tags = ["key", "api"]

[[rules]] id = "custom-password" description = "Hardcoded Password" regex = '''(?i)(password|passwd|pwd)\s*[:=]\s*['"][^'"]{8,}['"]''' tags = ["password"]

[allowlist] description = "Global allowlist" paths = [ '''.gitleaks.toml$''', '''.secrets.baseline$''', '''test/..py$''', '''._test.go$''', ]

GitHub Secret Scanning

.github/workflows/secret-scanning.yml

name: Secret Scanning on: push: branches: [main] pull_request:

jobs: gitleaks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0

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

trufflehog: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0

  - name: TruffleHog
    uses: trufflesecurity/trufflehog@main
    with:
      extra_args: --only-verified

Vulnerability Management Workflow

Vulnerability Tracking

/// <summary> /// Vulnerability management workflow automation. /// </summary> public enum VulnStatus { New, Triaged, InProgress, Resolved, AcceptedRisk, FalsePositive }

public enum VulnSeverity { Critical = 1, High = 2, Medium = 3, Low = 4 }

/// <summary> /// Tracked vulnerability with lifecycle management. /// </summary> public sealed class Vulnerability { public required string Id { get; init; } public string? CveId { get; init; } public required string Title { get; init; } public required string Description { get; init; } public required VulnSeverity Severity { get; init; } public required double CvssScore { get; init; } public required string AffectedComponent { get; init; } public required string AffectedVersion { get; init; } public string? FixedVersion { get; init; }

// Tracking
public VulnStatus Status { get; set; } = VulnStatus.New;
public string? Assignee { get; set; }
public DateTime DiscoveredDate { get; init; } = DateTime.UtcNow;
public DateTime? DueDate { get; set; }
public DateTime? ResolvedDate { get; set; }
public List&#x3C;string> Notes { get; } = [];

}

/// <summary> /// Manages vulnerability lifecycle with SLA tracking. /// </summary> public sealed class VulnerabilityManager { private static readonly IReadOnlyDictionary<VulnSeverity, int> SlaDays = new Dictionary<VulnSeverity, int> { [VulnSeverity.Critical] = 7, [VulnSeverity.High] = 30, [VulnSeverity.Medium] = 90, [VulnSeverity.Low] = 180, };

private readonly Dictionary&#x3C;string, Vulnerability> _vulnerabilities = new();

public void AddVulnerability(Vulnerability vuln)
{
    // Auto-set due date based on SLA
    vuln.DueDate ??= vuln.DiscoveredDate.AddDays(
        SlaDays.GetValueOrDefault(vuln.Severity, 90));

    _vulnerabilities[vuln.Id] = vuln;
}

public void Triage(string vulnId, string assignee, VulnStatus status = VulnStatus.Triaged)
{
    if (_vulnerabilities.TryGetValue(vulnId, out var vuln))
    {
        vuln.Status = status;
        vuln.Assignee = assignee;
        vuln.Notes.Add($"{DateTime.UtcNow:O}: Triaged to {assignee}");
    }
}

public void Resolve(string vulnId, string resolution, VulnStatus status = VulnStatus.Resolved)
{
    if (_vulnerabilities.TryGetValue(vulnId, out var vuln))
    {
        vuln.Status = status;
        vuln.ResolvedDate = DateTime.UtcNow;
        vuln.Notes.Add($"{DateTime.UtcNow:O}: Resolved - {resolution}");
    }
}

public void AcceptRisk(string vulnId, string justification, string approver)
{
    if (_vulnerabilities.TryGetValue(vulnId, out var vuln))
    {
        vuln.Status = VulnStatus.AcceptedRisk;
        vuln.Notes.Add($"{DateTime.UtcNow:O}: Risk accepted by {approver} - {justification}");
    }
}

public IEnumerable&#x3C;Vulnerability> GetOverdue()
{
    var now = DateTime.UtcNow;
    return _vulnerabilities.Values.Where(v =>
        v.Status is not (VulnStatus.Resolved or VulnStatus.AcceptedRisk or VulnStatus.FalsePositive) &#x26;&#x26;
        v.DueDate.HasValue &#x26;&#x26;
        v.DueDate.Value &#x3C; now);
}

public VulnerabilityMetrics GetMetrics()
{
    var vulns = _vulnerabilities.Values.ToList();

    return new VulnerabilityMetrics(
        Total: vulns.Count,
        Open: vulns.Count(v => v.Status is VulnStatus.New or VulnStatus.Triaged or VulnStatus.InProgress),
        Resolved: vulns.Count(v => v.Status == VulnStatus.Resolved),
        Overdue: GetOverdue().Count(),
        BySeverity: Enum.GetValues&#x3C;VulnSeverity>().ToDictionary(
            sev => sev.ToString(),
            sev => vulns.Count(v => v.Severity == sev)),
        MttrDays: CalculateMttr(vulns));
}

private static double CalculateMttr(List&#x3C;Vulnerability> vulns)
{
    var resolved = vulns
        .Where(v => v.Status == VulnStatus.Resolved &#x26;&#x26; v.ResolvedDate.HasValue)
        .ToList();

    if (resolved.Count == 0) return 0.0;

    var totalDays = resolved.Sum(v => (v.ResolvedDate!.Value - v.DiscoveredDate).TotalDays);
    return totalDays / resolved.Count;
}

}

public sealed record VulnerabilityMetrics( int Total, int Open, int Resolved, int Overdue, Dictionary<string, int> BySeverity, double MttrDays);

Security Champions Program

Program Structure

Security Champions Program

Roles and Responsibilities

Security Champion

  • Embedded security advocate within development team
  • First point of contact for security questions
  • Participates in security training and shares knowledge
  • Reviews security-critical code changes
  • Triages security findings for their team

Time Commitment

  • 10-20% of work time on security activities
  • Weekly security standup (30 min)
  • Monthly security training (2 hours)
  • Quarterly security deep-dive (4 hours)

Selection Criteria

  • 1+ year on the team
  • Interest in security
  • Good communication skills
  • Technical credibility with peers

Training Path

  1. Month 1: Security fundamentals

    • OWASP Top 10
    • Secure coding basics
    • Company security policies
  2. Month 2: Tools and processes

    • SAST/DAST tool usage
    • Security gate process
    • Vulnerability management
  3. Month 3: Advanced topics

    • Threat modeling
    • Security architecture review
    • Incident response basics

Metrics

  • Vulnerabilities found by champion reviews
  • Security training completion rate
  • Time to remediate findings
  • Security culture survey scores

Security Checklist

Pre-Development

  • Threat model completed for new features

  • Security requirements documented

  • Secure design patterns identified

  • Security champions assigned

During Development

  • Pre-commit hooks enabled (secrets, linting)

  • SAST integrated in IDE

  • Secure coding guidelines followed

  • Security-sensitive code reviewed by champion

Pre-Deployment

  • All security gates passed

  • SAST findings addressed

  • SCA vulnerabilities resolved or accepted

  • DAST scan completed

  • Security review approved

Post-Deployment

  • Runtime security monitoring enabled

  • Vulnerability scanning scheduled

  • Incident response plan updated

  • Security metrics collected

References

  • SAST Tools: See references/sast-tools.md for detailed tool configurations

  • Security Gates: See references/security-gates.md for gate implementation

  • Vulnerability Workflow: See references/vulnerability-workflow.md for complete workflow

Related Skills

  • secure-coding

  • Secure development practices

  • supply-chain-security

  • Dependency and SBOM management

  • threat-modeling

  • Threat identification and mitigation

Last Updated: 2025-12-26

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

api-security

No summary provided by upstream source.

Repository SourceNeeds Review
Security

agentic-layer-audit

No summary provided by upstream source.

Repository SourceNeeds Review
Security

container-security

No summary provided by upstream source.

Repository SourceNeeds Review