npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
- Setting up automated security scanning in a new or existing CI/CD pipeline
- Shifting security left by catching vulnerabilities before code reaches production
- Meeting compliance requirements (SOC 2, PCI-DSS, ISO 27001) that mandate automated security testing
- Integrating SAST, DAST, and SCA together to achieve comprehensive application security coverage
- Establishing security gates that block deployments containing critical or high-severity vulnerabilities
Do not use as a replacement for manual penetration testing. Automated scanning catches common vulnerability patterns but cannot replace human-driven security assessments for business logic flaws and complex attack chains.
Prerequisites
- CI/CD platform: GitHub Actions, GitLab CI, Jenkins, or Azure DevOps
- Container runtime (Docker) for running scanning tools
- A staging environment URL for DAST scanning (DAST cannot test static code)
- Repository access with permissions to modify CI/CD workflow files
- Tool-specific requirements:
- Semgrep: free for open-source rulesets (
p/security-audit,p/owasp-top-ten) - Trivy: free, no account required
- OWASP ZAP: free, Docker image available
- Gitleaks: free, no account required
- Semgrep: free for open-source rulesets (
Workflow
Step 1: Add Secrets Detection with Gitleaks
Secrets detection runs first because leaked credentials are the highest-priority finding. Add to .github/workflows/security.yml:
name: DevSecOps Security Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
secrets-scan:
name: Secrets Detection (Gitleaks)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for scanning all commits
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Configure .gitleaks.toml in the repository root for custom rules and allowlists:
[extend]
useDefault = true
[allowlist]
description = "Global allowlist"
paths = [
'''\.gitleaks\.toml''',
'''test/fixtures/.*''',
'''docs/examples/.*'''
]
[[rules]]
id = "custom-internal-api-key"
description = "Internal API key pattern"
regex = '''INTERNAL_KEY_[A-Za-z0-9]{32}'''
tags = ["internal", "api-key"]Step 2: Add SAST Scanning with Semgrep
Semgrep performs static code analysis to find security vulnerabilities, bugs, and code patterns:
sast-scan:
name: SAST (Semgrep)
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@v4
- name: Run Semgrep SAST scan
run: |
semgrep scan \
--config p/security-audit \
--config p/owasp-top-ten \
--config p/secrets \
--severity ERROR \
--error \
--json \
--output semgrep-results.json \
.
- name: Upload SAST results
if: always()
uses: actions/upload-artifact@v4
with:
name: semgrep-results
path: semgrep-results.jsonFor custom rules, create .semgrep/custom-rules.yml:
rules:
- id: no-exec-user-input
patterns:
- pattern: exec($INPUT)
- pattern-not: exec("...")
message: >
User input passed to exec(). This is a command injection vulnerability.
severity: ERROR
languages: [python]
metadata:
cwe: "CWE-78: OS Command Injection"
owasp: "A03:2021 - Injection"
- id: no-raw-sql-queries
patterns:
- pattern: cursor.execute(f"...")
- pattern: cursor.execute("..." + ...)
message: >
SQL query built with string concatenation or f-strings. Use parameterized queries.
severity: ERROR
languages: [python]
metadata:
cwe: "CWE-89: SQL Injection"
owasp: "A03:2021 - Injection"Step 3: Add SCA Scanning with Trivy
Trivy scans dependencies, container images, IaC files, and generates SBOM:
sca-scan:
name: SCA & Container Scan (Trivy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy filesystem scan (dependencies)
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
exit-code: '1'
format: 'json'
output: 'trivy-fs-results.json'
- name: Run Trivy IaC scan (Terraform, CloudFormation)
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: 'config'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
exit-code: '1'
format: 'json'
output: 'trivy-iac-results.json'
- name: Upload SCA results
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-results
path: trivy-*.json
container-scan:
name: Container Image Scan (Trivy)
runs-on: ubuntu-latest
needs: [sast-scan] # Build image only after SAST passes
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t app:${{ github.sha }} .
- name: Scan container image
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: 'app:${{ github.sha }}'
severity: 'CRITICAL,HIGH'
exit-code: '1'
format: 'json'
output: 'trivy-image-results.json'
- name: Generate SBOM
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: 'app:${{ github.sha }}'
format: 'cyclonedx'
output: 'sbom.json'
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.jsonStep 4: Add DAST Scanning with OWASP ZAP
DAST runs against a deployed staging environment. It is slower than SAST/SCA and should run asynchronously or on a schedule:
dast-scan:
name: DAST (OWASP ZAP)
runs-on: ubuntu-latest
needs: [deploy-staging] # Must run after app is deployed to staging
steps:
- uses: actions/checkout@v4
- name: Run ZAP Baseline Scan (fast, suitable for CI)
uses: zaproxy/action-baseline@v0.14.0
with:
target: ${{ vars.STAGING_URL }}
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a -j'
# For nightly full scans, use action-full-scan instead:
# - name: Run ZAP Full Scan (comprehensive, 30-60 min)
# uses: zaproxy/action-full-scan@v0.12.0
# with:
# target: ${{ vars.STAGING_URL }}Create .zap/rules.tsv to configure alert thresholds:
10010 IGNORE (Cookie No HttpOnly Flag - acceptable for non-sensitive cookies)
10011 IGNORE (Cookie Without Secure Flag - staging uses HTTP)
90033 WARN (Loosely Scoped Cookie)
10038 FAIL (Content Security Policy Header Not Set)
40012 FAIL (Cross Site Scripting - Reflected)
40014 FAIL (Cross Site Scripting - Persistent)
40018 FAIL (SQL Injection)
90019 FAIL (Server Side Code Injection)
90020 FAIL (Remote OS Command Injection)Step 5: Aggregate Results and Enforce Security Gates
Create a summary job that aggregates all scan results and enforces pass/fail gates:
security-gate:
name: Security Gate
runs-on: ubuntu-latest
needs: [secrets-scan, sast-scan, sca-scan, container-scan]
if: always()
steps:
- name: Check scan results
run: |
echo "Checking security scan results..."
# Fail the pipeline if any upstream job failed
if [[ "${{ needs.secrets-scan.result }}" == "failure" ]]; then
echo "BLOCKED: Secrets detected in repository"
exit 1
fi
if [[ "${{ needs.sast-scan.result }}" == "failure" ]]; then
echo "BLOCKED: SAST found critical/high vulnerabilities"
exit 1
fi
if [[ "${{ needs.sca-scan.result }}" == "failure" ]]; then
echo "BLOCKED: SCA found critical/high vulnerable dependencies"
exit 1
fi
if [[ "${{ needs.container-scan.result }}" == "failure" ]]; then
echo "BLOCKED: Container image has critical/high vulnerabilities"
exit 1
fi
echo "All security gates passed"Step 6: Configure Branch Protection Rules
Enforce the security pipeline as a required status check:
GitHub Repository > Settings > Branches > Branch Protection Rules
Branch name pattern: main
Require status checks to pass before merging: Enabled
Required status checks:
- Secrets Detection (Gitleaks)
- SAST (Semgrep)
- SCA & Container Scan (Trivy)
- Security Gate
Require branches to be up to date before merging: EnabledStep 7: Set Up Developer Feedback Loop
Configure pre-commit hooks so developers catch issues before pushing:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.22.1
hooks:
- id: gitleaks
- repo: https://github.com/semgrep/semgrep
rev: v1.102.0
hooks:
- id: semgrep
args: ['--config', 'p/security-audit', '--config', 'p/owasp-top-ten', '--error']Install and activate pre-commit:
pip install pre-commit
pre-commit install
pre-commit run --all-files # Test against existing codebaseKey Concepts
| Term | Definition |
|---|---|
| SAST (Static Application Security Testing) | Analyzes source code without executing it to find security vulnerabilities; runs fast, catches issues early, but cannot find runtime flaws |
| DAST (Dynamic Application Security Testing) | Tests a running application by sending requests and analyzing responses; finds runtime issues but requires a deployed environment |
| SCA (Software Composition Analysis) | Scans project dependencies against vulnerability databases (NVD, GitHub Advisory) to find known-vulnerable libraries |
| SBOM (Software Bill of Materials) | Machine-readable inventory of all components and dependencies in an application, used for vulnerability tracking and compliance |
| Shift Left | Security practice of moving security testing earlier in the SDLC, from post-deployment to pre-commit and CI stages |
| Security Gate | A CI/CD pipeline checkpoint that blocks deployment if security scan results exceed defined severity thresholds |
| Pre-commit Hook | Local Git hook that runs security checks before code is committed, providing the fastest developer feedback loop |
Verification
- Gitleaks blocks commits and PRs containing hardcoded secrets (test with a dummy API key)
- Semgrep scan runs on every PR and reports findings as annotations or comments
- Trivy filesystem scan detects a known-vulnerable dependency (test by adding a vulnerable package)
- Trivy container scan runs successfully against the built Docker image
- SBOM is generated and stored as a build artifact in CycloneDX or SPDX format
- OWASP ZAP baseline scan runs against the staging URL without crashing
- Security gate job blocks merges to main when any scan finds critical/high severity issues
- Branch protection rules enforce required status checks before merge
- Pre-commit hooks catch secrets and SAST findings locally before push
- Developer documentation explains how to interpret scan results and fix common findings
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.0 KB
API Reference: DevSecOps Security Scanning
Semgrep CLI (SAST)
# Scan with auto-detected rules
semgrep scan --config auto --json /path/to/code
# Scan with specific ruleset
semgrep scan --config p/owasp-top-ten --json /path/to/code
# Custom rule file
semgrep scan --config my_rules.yaml --json /path/to/code
# SARIF output for GitHub integration
semgrep scan --config auto --sarif -o results.sarif /path/to/codeTrivy CLI (SCA / Container)
# Scan container image
trivy image --format json --quiet nginx:latest
# Scan filesystem for vulnerabilities
trivy fs --format json --scanners vuln,secret /path/to/project
# Scan with severity filter
trivy image --severity CRITICAL,HIGH --format json myapp:latest
# Scan IaC files
trivy config --format json /path/to/terraform/Gitleaks CLI (Secret Detection)
# Detect secrets in git repo
gitleaks detect --source /path/to/repo --report-format json --report-path report.json
# Scan specific commit range
gitleaks detect --source . --log-opts="HEAD~10..HEAD" --report-format json
# Protect mode (pre-commit)
gitleaks protect --staged --report-format jsonCI/CD Pipeline Gate Logic
| Severity | Exit Code | Action |
|---|---|---|
| CRITICAL | 1 (fail) | Block merge/deploy |
| HIGH | 1 (fail) | Block merge/deploy |
| MEDIUM | 0 (warn) | Warning in PR comment |
| LOW | 0 (pass) | Informational only |
JSON Output Schema (Semgrep)
| Field | Description |
|---|---|
| results[].check_id | Rule identifier |
| results[].extra.severity | ERROR, WARNING, INFO |
| results[].path | Affected file path |
| results[].start.line | Line number |
| results[].extra.message | Finding description |
JSON Output Schema (Trivy)
| Field | Description |
|---|---|
| Results[].Target | Scanned target name |
| Results[].Vulnerabilities[].VulnerabilityID | CVE identifier |
| Results[].Vulnerabilities[].Severity | CRITICAL/HIGH/MEDIUM/LOW |
| Results[].Vulnerabilities[].FixedVersion | Version with fix |
Scripts 1
agent.py6.8 KB
#!/usr/bin/env python3
"""DevSecOps security scanning pipeline agent.
Orchestrates Semgrep (SAST), Trivy (container/SCA), and Gitleaks (secrets)
scans via subprocess, aggregates findings, and enforces severity gates.
"""
import argparse
import json
import subprocess
import sys
import datetime
SEVERITY_ORDER = {"CRITICAL": 4, "HIGH": 3, "MEDIUM": 2, "LOW": 1, "INFO": 0}
def run_semgrep(target_dir, config="auto"):
"""Run Semgrep SAST scan and return findings."""
cmd = ["semgrep", "scan", "--config", config, "--json", "--quiet", target_dir]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
data = json.loads(result.stdout) if result.stdout.strip() else {}
findings = []
for r in data.get("results", []):
findings.append({
"tool": "semgrep",
"rule_id": r.get("check_id", ""),
"severity": r.get("extra", {}).get("severity", "WARNING").upper(),
"message": r.get("extra", {}).get("message", ""),
"file": r.get("path", ""),
"line": r.get("start", {}).get("line", 0),
})
return findings
except FileNotFoundError:
return [{"tool": "semgrep", "error": "semgrep not installed"}]
except subprocess.TimeoutExpired:
return [{"tool": "semgrep", "error": "scan timed out"}]
except json.JSONDecodeError:
return [{"tool": "semgrep", "error": "invalid JSON output"}]
def run_trivy(image_or_path, scan_type="image"):
"""Run Trivy vulnerability scan on image or filesystem."""
cmd = ["trivy", scan_type, "--format", "json", "--quiet", image_or_path]
if scan_type == "fs":
cmd = ["trivy", "fs", "--format", "json", "--quiet", "--scanners", "vuln,secret", image_or_path]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
data = json.loads(result.stdout) if result.stdout.strip() else {}
findings = []
for res in data.get("Results", []):
for vuln in res.get("Vulnerabilities", []):
findings.append({
"tool": "trivy",
"rule_id": vuln.get("VulnerabilityID", ""),
"severity": vuln.get("Severity", "UNKNOWN").upper(),
"message": vuln.get("Title", ""),
"file": res.get("Target", ""),
"line": 0,
"fixed_version": vuln.get("FixedVersion", ""),
})
return findings
except FileNotFoundError:
return [{"tool": "trivy", "error": "trivy not installed"}]
except (subprocess.TimeoutExpired, json.JSONDecodeError) as e:
return [{"tool": "trivy", "error": str(e)}]
def run_gitleaks(repo_path):
"""Run Gitleaks secret detection scan."""
cmd = ["gitleaks", "detect", "--source", repo_path, "--report-format", "json",
"--report-path", "/dev/stdout", "--no-banner"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
data = json.loads(result.stdout) if result.stdout.strip().startswith("[") else []
findings = []
for leak in data:
findings.append({
"tool": "gitleaks",
"rule_id": leak.get("RuleID", ""),
"severity": "HIGH",
"message": leak.get("Description", ""),
"file": leak.get("File", ""),
"line": leak.get("StartLine", 0),
})
return findings
except FileNotFoundError:
return [{"tool": "gitleaks", "error": "gitleaks not installed"}]
except (subprocess.TimeoutExpired, json.JSONDecodeError) as e:
return [{"tool": "gitleaks", "error": str(e)}]
def enforce_gate(findings, fail_on="HIGH"):
"""Enforce security gate based on severity threshold."""
threshold = SEVERITY_ORDER.get(fail_on.upper(), 3)
blockers = [
f for f in findings
if not f.get("error") and SEVERITY_ORDER.get(f.get("severity", ""), 0) >= threshold
]
return {
"gate_threshold": fail_on,
"blocking_findings": len(blockers),
"passed": len(blockers) == 0,
}
def aggregate_report(all_findings):
"""Aggregate findings from all scanners."""
by_severity = {}
by_tool = {}
for f in all_findings:
if f.get("error"):
continue
sev = f.get("severity", "UNKNOWN")
tool = f.get("tool", "unknown")
by_severity[sev] = by_severity.get(sev, 0) + 1
by_tool[tool] = by_tool.get(tool, 0) + 1
return {
"total_findings": sum(1 for f in all_findings if not f.get("error")),
"by_severity": by_severity,
"by_tool": by_tool,
}
def main():
parser = argparse.ArgumentParser(description="DevSecOps security scanning pipeline")
parser.add_argument("target", nargs="?", help="Directory or container image to scan")
parser.add_argument("--semgrep", action="store_true", help="Run Semgrep SAST")
parser.add_argument("--trivy", action="store_true", help="Run Trivy vulnerability scan")
parser.add_argument("--trivy-type", default="fs", choices=["image", "fs"], help="Trivy scan type")
parser.add_argument("--gitleaks", action="store_true", help="Run Gitleaks secret detection")
parser.add_argument("--fail-on", default="HIGH", help="Fail gate on severity (default: HIGH)")
parser.add_argument("--output", "-o", help="Output JSON report path")
args = parser.parse_args()
print("[*] DevSecOps Security Scanning Pipeline")
report = {"timestamp": datetime.datetime.utcnow().isoformat() + "Z", "findings": []}
if not args.target:
print("[DEMO] Usage: python agent.py /path/to/code --semgrep --trivy --gitleaks")
print(" Tools: semgrep (SAST), trivy (SCA/container), gitleaks (secrets)")
print(json.dumps({"demo": True, "tools": ["semgrep", "trivy", "gitleaks"]}, indent=2))
sys.exit(0)
if args.semgrep:
report["findings"].extend(run_semgrep(args.target))
if args.trivy:
report["findings"].extend(run_trivy(args.target, args.trivy_type))
if args.gitleaks:
report["findings"].extend(run_gitleaks(args.target))
summary = aggregate_report(report["findings"])
gate = enforce_gate(report["findings"], args.fail_on)
report["summary"] = summary
report["gate"] = gate
print(f"[*] Total findings: {summary['total_findings']}")
print(f"[*] By severity: {summary['by_severity']}")
print(f"[*] Gate ({args.fail_on}): {'PASSED' if gate['passed'] else 'FAILED'}")
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps({"gate_passed": gate["passed"], "total": summary["total_findings"]}, indent=2))
sys.exit(0 if gate["passed"] else 1)
if __name__ == "__main__":
main()