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 | |-------|-----------------|-…

'',\n '''\\.secrets\\.baseline

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 | |-------|-----------------|-…

'',\n '''test/.*\\.py

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 | |-------|-----------------|-…

'',\n '''.*_test\\.go

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 | |-------|-----------------|-…

'',\n]\n```\n\n### GitHub Secret Scanning\n\n```yaml\n# .github/workflows/secret-scanning.yml\nname: Secret Scanning\non:\n push:\n branches: [main]\n pull_request:\n\njobs:\n gitleaks:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n\n - name: Gitleaks\n uses: gitleaks/gitleaks-action@v2\n env:\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n trufflehog:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n\n - name: TruffleHog\n uses: trufflesecurity/trufflehog@main\n with:\n extra_args: --only-verified\n```\n\n## Vulnerability Management Workflow\n\n### Vulnerability Tracking\n\n```csharp\n/// \u003csummary>\n/// Vulnerability management workflow automation.\n/// \u003c/summary>\npublic enum VulnStatus\n{\n New,\n Triaged,\n InProgress,\n Resolved,\n AcceptedRisk,\n FalsePositive\n}\n\npublic enum VulnSeverity { Critical = 1, High = 2, Medium = 3, Low = 4 }\n\n/// \u003csummary>\n/// Tracked vulnerability with lifecycle management.\n/// \u003c/summary>\npublic sealed class Vulnerability\n{\n public required string Id { get; init; }\n public string? CveId { get; init; }\n public required string Title { get; init; }\n public required string Description { get; init; }\n public required VulnSeverity Severity { get; init; }\n public required double CvssScore { get; init; }\n public required string AffectedComponent { get; init; }\n public required string AffectedVersion { get; init; }\n public string? FixedVersion { get; init; }\n\n // Tracking\n public VulnStatus Status { get; set; } = VulnStatus.New;\n public string? Assignee { get; set; }\n public DateTime DiscoveredDate { get; init; } = DateTime.UtcNow;\n public DateTime? DueDate { get; set; }\n public DateTime? ResolvedDate { get; set; }\n public List\u003cstring> Notes { get; } = [];\n}\n\n/// \u003csummary>\n/// Manages vulnerability lifecycle with SLA tracking.\n/// \u003c/summary>\npublic sealed class VulnerabilityManager\n{\n private static readonly IReadOnlyDictionary\u003cVulnSeverity, int> SlaDays = new Dictionary\u003cVulnSeverity, int>\n {\n [VulnSeverity.Critical] = 7,\n [VulnSeverity.High] = 30,\n [VulnSeverity.Medium] = 90,\n [VulnSeverity.Low] = 180,\n };\n\n private readonly Dictionary\u003cstring, Vulnerability> _vulnerabilities = new();\n\n public void AddVulnerability(Vulnerability vuln)\n {\n // Auto-set due date based on SLA\n vuln.DueDate ??= vuln.DiscoveredDate.AddDays(\n SlaDays.GetValueOrDefault(vuln.Severity, 90));\n\n _vulnerabilities[vuln.Id] = vuln;\n }\n\n public void Triage(string vulnId, string assignee, VulnStatus status = VulnStatus.Triaged)\n {\n if (_vulnerabilities.TryGetValue(vulnId, out var vuln))\n {\n vuln.Status = status;\n vuln.Assignee = assignee;\n vuln.Notes.Add($\"{DateTime.UtcNow:O}: Triaged to {assignee}\");\n }\n }\n\n public void Resolve(string vulnId, string resolution, VulnStatus status = VulnStatus.Resolved)\n {\n if (_vulnerabilities.TryGetValue(vulnId, out var vuln))\n {\n vuln.Status = status;\n vuln.ResolvedDate = DateTime.UtcNow;\n vuln.Notes.Add($\"{DateTime.UtcNow:O}: Resolved - {resolution}\");\n }\n }\n\n public void AcceptRisk(string vulnId, string justification, string approver)\n {\n if (_vulnerabilities.TryGetValue(vulnId, out var vuln))\n {\n vuln.Status = VulnStatus.AcceptedRisk;\n vuln.Notes.Add($\"{DateTime.UtcNow:O}: Risk accepted by {approver} - {justification}\");\n }\n }\n\n public IEnumerable\u003cVulnerability> GetOverdue()\n {\n var now = DateTime.UtcNow;\n return _vulnerabilities.Values.Where(v =>\n v.Status is not (VulnStatus.Resolved or VulnStatus.AcceptedRisk or VulnStatus.FalsePositive) &&\n v.DueDate.HasValue &&\n v.DueDate.Value \u003c now);\n }\n\n public VulnerabilityMetrics GetMetrics()\n {\n var vulns = _vulnerabilities.Values.ToList();\n\n return new VulnerabilityMetrics(\n Total: vulns.Count,\n Open: vulns.Count(v => v.Status is VulnStatus.New or VulnStatus.Triaged or VulnStatus.InProgress),\n Resolved: vulns.Count(v => v.Status == VulnStatus.Resolved),\n Overdue: GetOverdue().Count(),\n BySeverity: Enum.GetValues\u003cVulnSeverity>().ToDictionary(\n sev => sev.ToString(),\n sev => vulns.Count(v => v.Severity == sev)),\n MttrDays: CalculateMttr(vulns));\n }\n\n private static double CalculateMttr(List\u003cVulnerability> vulns)\n {\n var resolved = vulns\n .Where(v => v.Status == VulnStatus.Resolved && v.ResolvedDate.HasValue)\n .ToList();\n\n if (resolved.Count == 0) return 0.0;\n\n var totalDays = resolved.Sum(v => (v.ResolvedDate!.Value - v.DiscoveredDate).TotalDays);\n return totalDays / resolved.Count;\n }\n}\n\npublic sealed record VulnerabilityMetrics(\n int Total,\n int Open,\n int Resolved,\n int Overdue,\n Dictionary\u003cstring, int> BySeverity,\n double MttrDays);\n```\n\n## Security Champions Program\n\n### Program Structure\n\n```markdown\n# Security Champions Program\n\n## Roles and Responsibilities\n\n### Security Champion\n- Embedded security advocate within development team\n- First point of contact for security questions\n- Participates in security training and shares knowledge\n- Reviews security-critical code changes\n- Triages security findings for their team\n\n### Time Commitment\n- 10-20% of work time on security activities\n- Weekly security standup (30 min)\n- Monthly security training (2 hours)\n- Quarterly security deep-dive (4 hours)\n\n## Selection Criteria\n- 1+ year on the team\n- Interest in security\n- Good communication skills\n- Technical credibility with peers\n\n## Training Path\n1. **Month 1**: Security fundamentals\n - OWASP Top 10\n - Secure coding basics\n - Company security policies\n\n2. **Month 2**: Tools and processes\n - SAST/DAST tool usage\n - Security gate process\n - Vulnerability management\n\n3. **Month 3**: Advanced topics\n - Threat modeling\n - Security architecture review\n - Incident response basics\n\n## Metrics\n- Vulnerabilities found by champion reviews\n- Security training completion rate\n- Time to remediate findings\n- Security culture survey scores\n```\n\n## Security Checklist\n\n### Pre-Development\n\n- [ ] Threat model completed for new features\n- [ ] Security requirements documented\n- [ ] Secure design patterns identified\n- [ ] Security champions assigned\n\n### During Development\n\n- [ ] Pre-commit hooks enabled (secrets, linting)\n- [ ] SAST integrated in IDE\n- [ ] Secure coding guidelines followed\n- [ ] Security-sensitive code reviewed by champion\n\n### Pre-Deployment\n\n- [ ] All security gates passed\n- [ ] SAST findings addressed\n- [ ] SCA vulnerabilities resolved or accepted\n- [ ] DAST scan completed\n- [ ] Security review approved\n\n### Post-Deployment\n\n- [ ] Runtime security monitoring enabled\n- [ ] Vulnerability scanning scheduled\n- [ ] Incident response plan updated\n- [ ] Security metrics collected\n\n## References\n\n- **SAST Tools**: See `references/sast-tools.md` for detailed tool configurations\n- **Security Gates**: See `references/security-gates.md` for gate implementation\n- **Vulnerability Workflow**: See `references/vulnerability-workflow.md` for complete workflow\n\n## Related Skills\n\n- `secure-coding` - Secure development practices\n- `supply-chain-security` - Dependency and SBOM management\n- `threat-modeling` - Threat identification and mitigation\n\n---\n\n**Last Updated:** 2025-12-26\n---","attachment_filenames":["references/sast-tools.md","references/security-gates.md","references/vulnerability-workflow.md"],"attachments":[{"filename":"references/sast-tools.md","content":"# SAST Tools Reference\n\nDetailed configuration and usage guides for Static Application Security Testing tools.\n\n## Semgrep Deep Dive\n\n### Installation and Setup\n\n```bash\n# Install via pip\npip install semgrep\n\n# Install via Homebrew\nbrew install semgrep\n\n# Docker\ndocker run --rm -v \"${PWD}:/src\" semgrep/semgrep semgrep scan --config auto\n```\n\n### Custom Rule Development\n\n```yaml\n# custom-rules/sql-injection.yml\nrules:\n # Python SQLAlchemy injection\n - id: sqlalchemy-raw-query-injection\n patterns:\n - pattern-either:\n - pattern: |\n $SESSION.execute($QUERY % ...)\n - pattern: |\n $SESSION.execute($QUERY.format(...))\n - pattern: |\n $SESSION.execute(f\"...\")\n - pattern: |\n text($QUERY % ...)\n - pattern: |\n text($QUERY.format(...))\n - pattern: |\n text(f\"...\")\n message: |\n Potential SQL injection in SQLAlchemy. Use parameterized queries:\n session.execute(text(\"SELECT * FROM users WHERE id = :id\"), {\"id\": user_id})\n severity: ERROR\n languages: [python]\n metadata:\n cwe: \"CWE-89\"\n owasp: \"A03:2021 - Injection\"\n category: security\n technology:\n - sqlalchemy\n\n # Django ORM raw query injection\n - id: django-raw-sql-injection\n patterns:\n - pattern-either:\n - pattern: |\n $MODEL.objects.raw($QUERY % ...)\n - pattern: |\n $MODEL.objects.raw($QUERY.format(...))\n - pattern: |\n $MODEL.objects.raw(f\"...\")\n - pattern: |\n connection.cursor().execute($QUERY % ...)\n - pattern: |\n connection.cursor().execute($QUERY.format(...))\n message: |\n SQL injection in Django raw query. Use parameterized queries:\n Model.objects.raw(\"SELECT * FROM table WHERE id = %s\", [user_id])\n severity: ERROR\n languages: [python]\n metadata:\n cwe: \"CWE-89\"\n owasp: \"A03:2021 - Injection\"\n\n # Node.js SQL injection\n - id: nodejs-sql-injection\n patterns:\n - pattern-either:\n - pattern: |\n $POOL.query($QUERY + ...)\n - pattern: |\n $POOL.query(`... ${...} ...`)\n - pattern: |\n $CONNECTION.query($QUERY + ...)\n - pattern: |\n $CONNECTION.query(`... ${...} ...`)\n message: |\n SQL injection vulnerability. Use parameterized queries:\n pool.query(\"SELECT * FROM users WHERE id = $1\", [userId])\n severity: ERROR\n languages: [javascript, typescript]\n metadata:\n cwe: \"CWE-89\"\n\n # Command Injection\n - id: command-injection-subprocess\n patterns:\n - pattern-either:\n - pattern: subprocess.call($CMD, shell=True, ...)\n - pattern: subprocess.run($CMD, shell=True, ...)\n - pattern: subprocess.Popen($CMD, shell=True, ...)\n - pattern: os.system($CMD)\n - pattern: os.popen($CMD)\n - pattern-not: subprocess.call(\"...\", shell=True, ...)\n - pattern-not: subprocess.run(\"...\", shell=True, ...)\n message: |\n Potential command injection. Avoid shell=True with user input.\n Use subprocess with list arguments: subprocess.run([\"cmd\", arg1, arg2])\n severity: ERROR\n languages: [python]\n metadata:\n cwe: \"CWE-78\"\n owasp: \"A03:2021 - Injection\"\n\n # Path Traversal\n - id: path-traversal\n patterns:\n - pattern-either:\n - pattern: open(os.path.join($BASE, $INPUT), ...)\n - pattern: pathlib.Path($BASE) / $INPUT\n - pattern-not-inside: |\n if \"..\" in $INPUT:\n ...\n - pattern-not-inside: |\n $SAFE = os.path.normpath(...)\n ...\n message: |\n Potential path traversal. Validate input doesn't contain \"..\" and\n use os.path.realpath() to resolve the path within allowed directory.\n severity: WARNING\n languages: [python]\n metadata:\n cwe: \"CWE-22\"\n\n # Insecure Deserialization\n - id: insecure-pickle\n patterns:\n - pattern-either:\n - pattern: pickle.loads(...)\n - pattern: pickle.load(...)\n - pattern: cPickle.loads(...)\n - pattern: cPickle.load(...)\n message: |\n Pickle deserialization is unsafe with untrusted data.\n Use JSON or a safe serialization format instead.\n severity: WARNING\n languages: [python]\n metadata:\n cwe: \"CWE-502\"\n owasp: \"A08:2021 - Software and Data Integrity Failures\"\n\n # SSRF\n - id: ssrf-requests\n patterns:\n - pattern-either:\n - pattern: requests.get($URL, ...)\n - pattern: requests.post($URL, ...)\n - pattern: requests.put($URL, ...)\n - pattern: requests.delete($URL, ...)\n - pattern: urllib.request.urlopen($URL)\n - pattern-not: requests.get(\"...\", ...)\n - pattern-not: requests.post(\"...\", ...)\n - metavariable-regex:\n metavariable: $URL\n regex: '^[^\"''].*

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 | |-------|-----------------|-…

# Not a string literal\n message: |\n Potential SSRF vulnerability. Validate and allowlist URLs before making requests.\n Consider using a URL validation library.\n severity: WARNING\n languages: [python]\n metadata:\n cwe: \"CWE-918\"\n owasp: \"A10:2021 - Server-Side Request Forgery\"\n```\n\n### Semgrep CI Configuration\n\n```yaml\n# .semgrep.yml - Project configuration\nrules: [] # Use rulesets below\n\n# Rulesets to use\n# semgrep --config p/python --config p/security-audit --config p/owasp-top-ten\n\n# Rule severity overrides\nseverity:\n - rule: python.lang.security.audit.dangerous-subprocess-use\n level: error\n - rule: python.lang.security.audit.hardcoded-password\n level: error\n\n# Paths to scan\ninclude:\n - \"src/\"\n - \"app/\"\n - \"lib/\"\n\n# Paths to exclude\nexclude:\n - \"tests/\"\n - \"vendor/\"\n - \"**/node_modules/**\"\n - \"**/*.min.js\"\n - \"**/migrations/**\"\n```\n\n```yaml\n# .github/workflows/semgrep-full.yml\nname: Semgrep Full Analysis\non:\n push:\n branches: [main]\n pull_request:\n schedule:\n - cron: '0 4 * * *' # Daily 4 AM\n\njobs:\n semgrep:\n runs-on: ubuntu-latest\n container:\n image: semgrep/semgrep\n\n steps:\n - uses: actions/checkout@v5\n\n - name: Run Semgrep with multiple rulesets\n run: |\n semgrep scan \\\n --config p/python \\\n --config p/javascript \\\n --config p/typescript \\\n --config p/security-audit \\\n --config p/owasp-top-ten \\\n --config p/secrets \\\n --config ./custom-rules/ \\\n --sarif --output semgrep.sarif \\\n --json --output semgrep.json\n\n - name: Upload SARIF\n uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: semgrep.sarif\n\n - name: Check for high severity findings\n run: |\n HIGH_COUNT=$(jq '[.results[] | select(.extra.severity == \"ERROR\")] | length' semgrep.json)\n if [ \"$HIGH_COUNT\" -gt 0 ]; then\n echo \"Found $HIGH_COUNT high severity findings\"\n jq '.results[] | select(.extra.severity == \"ERROR\") | {path: .path, line: .start.line, message: .extra.message}' semgrep.json\n exit 1\n fi\n\n - name: Generate report\n run: |\n echo \"## Semgrep Security Scan Results\" > security-report.md\n echo \"\" >> security-report.md\n echo \"| Severity | Count |\" >> security-report.md\n echo \"|----------|-------|\" >> security-report.md\n for sev in ERROR WARNING INFO; do\n COUNT=$(jq \"[.results[] | select(.extra.severity == \\\"$sev\\\")] | length\" semgrep.json)\n echo \"| $sev | $COUNT |\" >> security-report.md\n done\n\n - name: Upload report\n uses: actions/upload-artifact@v4\n with:\n name: semgrep-report\n path: |\n semgrep.sarif\n semgrep.json\n security-report.md\n```\n\n## CodeQL Advanced Configuration\n\n### Custom CodeQL Queries\n\n```ql\n// queries/sql-injection.ql\n/**\n * @name SQL injection\n * @description Building SQL queries from user input enables SQL injection\n * @kind path-problem\n * @problem.severity error\n * @security-severity 9.8\n * @precision high\n * @id py/sql-injection\n * @tags security\n * external/cwe/cwe-89\n */\n\nimport python\nimport semmle.python.security.dataflow.SqlInjectionQuery\nimport DataFlow::PathGraph\n\nfrom SqlInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink\nwhere config.hasFlowPath(source, sink)\nselect sink.getNode(), source, sink, \"SQL query depends on $@.\", source.getNode(),\n \"user-provided value\"\n```\n\n```ql\n// queries/hardcoded-credentials.ql\n/**\n * @name Hardcoded credentials\n * @description Credentials stored in source code\n * @kind problem\n * @problem.severity error\n * @security-severity 9.0\n * @precision high\n * @id py/hardcoded-credentials\n * @tags security\n * external/cwe/cwe-798\n */\n\nimport python\n\npredicate isCredentialName(string name) {\n name.regexpMatch(\"(?i).*(password|passwd|pwd|secret|api_key|apikey|token|auth).*\")\n}\n\nfrom Assign assign, Name target, StringLiteral value\nwhere\n target = assign.getATarget() and\n value = assign.getValue() and\n isCredentialName(target.getId()) and\n value.getText().length() > 8 and\n not value.getText().regexpMatch(\"(?i).*(example|test|dummy|placeholder).*\")\nselect assign, \"Hardcoded credential assigned to variable '\" + target.getId() + \"'\"\n```\n\n### CodeQL Query Suite\n\n```yaml\n# .github/codeql/security-extended.qls\n- description: Extended security queries\n- queries: .\n- include:\n kind: problem\n tags contain: security\n- include:\n kind: path-problem\n tags contain: security\n- exclude:\n precision:\n - low\n - medium\n```\n\n### CodeQL Database Creation for Custom Builds\n\n```yaml\n# .github/workflows/codeql-custom.yml\nname: CodeQL Custom Analysis\non:\n push:\n branches: [main]\n pull_request:\n\njobs:\n analyze:\n runs-on: ubuntu-latest\n\n strategy:\n matrix:\n language: [python, javascript]\n\n steps:\n - uses: actions/checkout@v5\n\n - name: Initialize CodeQL\n uses: github/codeql-action/init@v3\n with:\n languages: ${{ matrix.language }}\n config-file: .github/codeql/codeql-config.yml\n queries: +security-extended,+security-and-quality\n\n # Custom build steps for compiled languages\n - name: Build Python package\n if: matrix.language == 'python'\n run: |\n pip install -e .\n pip install -r requirements.txt\n\n - name: Perform CodeQL Analysis\n uses: github/codeql-action/analyze@v3\n with:\n category: \"/language:${{ matrix.language }}\"\n upload: true\n output: codeql-results\n```\n\n```yaml\n# .github/codeql/codeql-config.yml\nname: \"CodeQL Security Config\"\n\n# Disable default queries and use specific ones\ndisable-default-queries: false\n\nqueries:\n - uses: security-extended\n - uses: security-and-quality\n - uses: ./custom-queries # Local custom queries\n\n# Additional paths to scan\npaths:\n - src\n - lib\n\n# Paths to exclude\npaths-ignore:\n - tests\n - \"**/test_*.py\"\n - \"**/*_test.go\"\n - node_modules\n - vendor\n```\n\n## SonarQube Advanced Configuration\n\n### Quality Profile Customization\n\n```xml\n\u003c!-- sonar-quality-profile.xml -->\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003cprofile>\n \u003cname>Security-Focused\u003c/name>\n \u003clanguage>py\u003c/language>\n \u003crules>\n \u003c!-- SQL Injection -->\n \u003crule>\n \u003crepositoryKey>python\u003c/repositoryKey>\n \u003ckey>S3649\u003c/key>\n \u003cpriority>BLOCKER\u003c/priority>\n \u003c/rule>\n \u003c!-- Command Injection -->\n \u003crule>\n \u003crepositoryKey>python\u003c/repositoryKey>\n \u003ckey>S4721\u003c/key>\n \u003cpriority>BLOCKER\u003c/priority>\n \u003c/rule>\n \u003c!-- Hardcoded Credentials -->\n \u003crule>\n \u003crepositoryKey>python\u003c/repositoryKey>\n \u003ckey>S2068\u003c/key>\n \u003cpriority>CRITICAL\u003c/priority>\n \u003c/rule>\n \u003c!-- LDAP Injection -->\n \u003crule>\n \u003crepositoryKey>python\u003c/repositoryKey>\n \u003ckey>S4817\u003c/key>\n \u003cpriority>BLOCKER\u003c/priority>\n \u003c/rule>\n \u003c!-- XPath Injection -->\n \u003crule>\n \u003crepositoryKey>python\u003c/repositoryKey>\n \u003ckey>S4817\u003c/key>\n \u003cpriority>CRITICAL\u003c/priority>\n \u003c/rule>\n \u003c/rules>\n\u003c/profile>\n```\n\n### SonarQube Scanner Configuration\n\n```properties\n# sonar-project.properties\nsonar.projectKey=my-project\nsonar.projectName=My Project\nsonar.projectVersion=1.0\n\n# Source encoding\nsonar.sourceEncoding=UTF-8\n\n# Source code location\nsonar.sources=src,lib\nsonar.tests=tests\n\n# Exclusions\nsonar.exclusions=**/node_modules/**,**/*.min.js,**/vendor/**,**/migrations/**\n\n# Test exclusions\nsonar.test.exclusions=**/test_*.py,**/*_test.go\n\n# Coverage\nsonar.python.coverage.reportPaths=coverage.xml\nsonar.javascript.lcov.reportPaths=coverage/lcov.info\n\n# Security hotspots\nsonar.security.hotspots.review.priority=HIGH,MEDIUM\n\n# Branch analysis\nsonar.branch.name=${GITHUB_REF_NAME}\n\n# Pull request analysis\nsonar.pullrequest.key=${GITHUB_PR_NUMBER}\nsonar.pullrequest.branch=${GITHUB_HEAD_REF}\nsonar.pullrequest.base=${GITHUB_BASE_REF}\n```\n\n### SonarQube Webhook for CI\n\n```csharp\n// SonarQube quality gate webhook handler - ASP.NET Core Minimal API\nusing System.Security.Cryptography;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Microsoft.AspNetCore.Mvc;\n\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddSingleton\u003cIQualityGateNotifier, QualityGateNotifier>();\n\nvar app = builder.Build();\n\napp.MapPost(\"/sonar/webhook\", async (\n HttpRequest request,\n [FromServices] IConfiguration config,\n [FromServices] IQualityGateNotifier notifier,\n [FromServices] ILogger\u003cProgram> logger) =>\n{\n // Read raw body for signature verification\n request.EnableBuffering();\n using var reader = new StreamReader(request.Body, leaveOpen: true);\n var body = await reader.ReadToEndAsync();\n request.Body.Position = 0;\n\n // Verify webhook signature\n var signature = request.Headers[\"X-Sonar-Webhook-HMAC-SHA256\"].FirstOrDefault();\n var secret = config[\"SonarQube:WebhookSecret\"] ?? throw new InvalidOperationException(\"Webhook secret not configured\");\n\n var expectedSignature = ComputeHmacSha256(body, secret);\n if (!CryptographicOperations.FixedTimeEquals(\n Encoding.UTF8.GetBytes(signature ?? \"\"),\n Encoding.UTF8.GetBytes(expectedSignature)))\n {\n return Results.Json(new { error = \"Invalid signature\" }, statusCode: 401);\n }\n\n // Parse webhook payload\n var data = JsonNode.Parse(body)?.AsObject();\n if (data is null)\n return Results.BadRequest(new { error = \"Invalid JSON payload\" });\n\n var project = data[\"project\"]?.AsObject();\n var qualityGate = data[\"qualityGate\"]?.AsObject();\n var status = qualityGate?[\"status\"]?.GetValue\u003cstring>();\n var conditions = qualityGate?[\"conditions\"]?.AsArray() ?? [];\n\n if (status == \"ERROR\")\n {\n // Quality gate failed - collect failed conditions\n var failedConditions = conditions\n .Where(c => c?[\"status\"]?.GetValue\u003cstring>() == \"ERROR\")\n .Select(c => new FailedCondition(\n Metric: c?[\"metric\"]?.GetValue\u003cstring>() ?? \"unknown\",\n Value: c?[\"value\"]?.GetValue\u003cstring>() ?? \"N/A\",\n ErrorThreshold: c?[\"errorThreshold\"]?.GetValue\u003cstring>() ?? \"N/A\"))\n .ToList();\n\n // Notify team (messaging platform, email, etc.)\n await notifier.NotifyQualityGateFailureAsync(\n project?[\"key\"]?.GetValue\u003cstring>() ?? \"unknown\",\n failedConditions);\n\n // Block deployment if in CD pipeline\n // ... integration with deployment system\n }\n\n return Results.Json(new { status = \"processed\" });\n});\n\napp.Run();\n\nstatic string ComputeHmacSha256(string data, string secret)\n{\n using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));\n var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));\n return Convert.ToHexString(hash).ToLowerInvariant();\n}\n\npublic sealed record FailedCondition(string Metric, string Value, string ErrorThreshold);\n\npublic interface IQualityGateNotifier\n{\n Task NotifyQualityGateFailureAsync(string project, List\u003cFailedCondition> conditions, CancellationToken ct = default);\n}\n\npublic sealed class QualityGateNotifier(ILogger\u003cQualityGateNotifier> logger) : IQualityGateNotifier\n{\n public Task NotifyQualityGateFailureAsync(string project, List\u003cFailedCondition> conditions, CancellationToken ct = default)\n {\n var message = new StringBuilder($\"Quality Gate Failed for {project}\\n\\n\");\n foreach (var condition in conditions)\n {\n message.AppendLine($\"- {condition.Metric}: {condition.Value} (threshold: {condition.ErrorThreshold})\");\n }\n\n // Send to messaging platform, email, etc.\n logger.LogWarning(\"Quality gate failure: {Message}\", message.ToString());\n return Task.CompletedTask;\n }\n}\n```\n\n## Tool Comparison Matrix\n\n| Feature | Semgrep | CodeQL | SonarQube |\n|---------|---------|--------|-----------|\n| **Speed** | Fast | Slow (deep analysis) | Medium |\n| **Language Support** | 30+ | 10+ | 25+ |\n| **Custom Rules** | YAML (easy) | QL (complex) | Java (plugin) |\n| **CI Integration** | Excellent | GitHub native | Good |\n| **IDE Support** | VS Code, IntelliJ | VS Code | SonarLint |\n| **Data Flow** | Pattern-based | Full taint tracking | Limited |\n| **License** | LGPL (Community) | MIT (OSS) | LGPL (Community) |\n| **Cloud Option** | Semgrep App | GitHub CodeQL | SonarCloud |\n| **False Positive Rate** | Low | Very Low | Medium |\n| **Setup Complexity** | Low | Medium | High |\n\n## Integration Patterns\n\n### Combined SAST Pipeline\n\n```yaml\n# .github/workflows/combined-sast.yml\nname: Combined SAST Analysis\non:\n pull_request:\n branches: [main]\n\njobs:\n semgrep:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n - uses: semgrep/semgrep-action@v1\n with:\n config: auto\n generateSarif: true\n\n - uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: semgrep.sarif\n category: semgrep\n\n codeql:\n runs-on: ubuntu-latest\n permissions:\n security-events: write\n steps:\n - uses: actions/checkout@v5\n - uses: github/codeql-action/init@v3\n with:\n languages: python, javascript\n - uses: github/codeql-action/analyze@v3\n\n sonarqube:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n - uses: sonarsource/sonarqube-scan-action@master\n env:\n SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}\n\n aggregate:\n needs: [semgrep, codeql, sonarqube]\n runs-on: ubuntu-latest\n steps:\n - name: Check all passed\n run: |\n echo \"All SAST tools completed successfully\"\n # Add custom aggregation logic here\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18856,"content_sha256":"2924357993684e1037a55e4cf25342390a9600ae91d760e16fcf900ef866177b"},{"filename":"references/security-gates.md","content":"# Security Gates Reference\n\nComprehensive implementation guide for security gates in CI/CD pipelines.\n\n## Gate Architecture\n\n### Pipeline Security Gate Flow\n\n```text\n┌─────────────────────────────────────────────────────────────────────────┐\n│ Security Gate Pipeline │\n├─────────────────────────────────────────────────────────────────────────┤\n│ │\n│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │\n│ │ SAST │ │ SCA │ │ Secrets │ │ License │ │\n│ │ Semgrep │ │ Snyk │ │Gitleaks │ │ Check │ │\n│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │\n│ │ │ │ │ │\n│ └──────────────┴──────────────┴──────────────┘ │\n│ │ │\n│ ▼ │\n│ ┌─────────────────┐ │\n│ │ Aggregator & │ │\n│ │ Policy Engine │ │\n│ └────────┬────────┘ │\n│ │ │\n│ ┌──────────────┼──────────────┐ │\n│ ▼ ▼ ▼ │\n│ ┌────────┐ ┌─────────┐ ┌─────────┐ │\n│ │ PASS │ │ WARN │ │ FAIL │ │\n│ └────────┘ └─────────┘ └─────────┘ │\n│ │ │ │\n│ ▼ ▼ │\n│ ┌─────────┐ ┌─────────┐ │\n│ │ Notify │ │ Block │ │\n│ │ Team │ │ Deploy │ │\n│ └─────────┘ └─────────┘ │\n│ │\n└─────────────────────────────────────────────────────────────────────────┘\n```\n\n## Complete Gate Implementation\n\n### Policy Configuration\n\n```yaml\n# security-policy.yml\nversion: \"1.0\"\n\n# Global settings\nsettings:\n fail_on_error: true\n allow_bypass: false\n bypass_approvers:\n - security-team\n - cto\n\n# Gate configurations by environment\nenvironments:\n development:\n sast:\n critical: warn\n high: warn\n medium: ignore\n sca:\n critical: warn\n high: ignore\n secrets:\n action: warn\n licenses:\n action: ignore\n\n staging:\n sast:\n critical: fail\n high: warn\n medium: ignore\n sca:\n critical: fail\n high: warn\n cvss_threshold: 8.0\n secrets:\n action: fail\n licenses:\n action: warn\n forbidden:\n - GPL-3.0\n - AGPL-3.0\n\n production:\n sast:\n critical: fail\n high: fail\n medium: warn\n sca:\n critical: fail\n high: fail\n medium: warn\n cvss_threshold: 7.0\n secrets:\n action: fail\n licenses:\n action: fail\n forbidden:\n - GPL-3.0\n - AGPL-3.0\n - LGPL-3.0\n dast:\n critical: fail\n high: fail\n```\n\n### Gate Engine Implementation\n\n```csharp\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing YamlDotNet.Serialization;\n\n/// \u003csummary>\n/// Security gate engine for CI/CD pipelines.\n/// \u003c/summary>\npublic enum GateAction { Pass, Warn, Fail, Ignore }\npublic enum FindingSeverity { Critical, High, Medium, Low, Info }\n\npublic sealed record Finding(\n string Scanner,\n FindingSeverity Severity,\n string Title,\n string Description,\n string? Location = null,\n string? Cwe = null,\n string? Cve = null,\n double? Cvss = null,\n bool FixAvailable = false);\n\npublic sealed class GateResult\n{\n public GateAction Action { get; set; } = GateAction.Pass;\n public List\u003cFinding> Findings { get; set; } = [];\n public List\u003cstring> Reasons { get; set; } = [];\n public Dictionary\u003cstring, Dictionary\u003cstring, int>> Metrics { get; set; } = [];\n public int SecretsCount { get; set; }\n}\n\npublic sealed class PolicyConfig\n{\n public GateAction SastCritical { get; init; } = GateAction.Fail;\n public GateAction SastHigh { get; init; } = GateAction.Fail;\n public GateAction SastMedium { get; init; } = GateAction.Warn;\n public GateAction ScaCritical { get; init; } = GateAction.Fail;\n public GateAction ScaHigh { get; init; } = GateAction.Warn;\n public double ScaCvssThreshold { get; init; } = 7.0;\n public GateAction SecretsAction { get; init; } = GateAction.Fail;\n public GateAction LicenseAction { get; init; } = GateAction.Warn;\n public HashSet\u003cstring> ForbiddenLicenses { get; init; } = [];\n public GateAction DastCritical { get; init; } = GateAction.Fail;\n public GateAction DastHigh { get; init; } = GateAction.Fail;\n\n public static PolicyConfig FromYaml(string configPath, string environment)\n {\n var yaml = File.ReadAllText(configPath);\n var deserializer = new DeserializerBuilder().Build();\n var config = deserializer.Deserialize\u003cDictionary\u003cstring, object>>(yaml);\n\n var envConfig = GetNestedDict(config, \"environments\", environment);\n var sast = GetNestedDict(envConfig, \"sast\");\n var sca = GetNestedDict(envConfig, \"sca\");\n var secrets = GetNestedDict(envConfig, \"secrets\");\n var licenses = GetNestedDict(envConfig, \"licenses\");\n var dast = GetNestedDict(envConfig, \"dast\");\n\n return new PolicyConfig\n {\n SastCritical = ParseAction(sast, \"critical\", GateAction.Fail),\n SastHigh = ParseAction(sast, \"high\", GateAction.Fail),\n SastMedium = ParseAction(sast, \"medium\", GateAction.Warn),\n ScaCritical = ParseAction(sca, \"critical\", GateAction.Fail),\n ScaHigh = ParseAction(sca, \"high\", GateAction.Warn),\n ScaCvssThreshold = GetDouble(sca, \"cvss_threshold\", 7.0),\n SecretsAction = ParseAction(secrets, \"action\", GateAction.Fail),\n LicenseAction = ParseAction(licenses, \"action\", GateAction.Warn),\n ForbiddenLicenses = GetStringList(licenses, \"forbidden\").ToHashSet(),\n DastCritical = ParseAction(dast, \"critical\", GateAction.Fail),\n DastHigh = ParseAction(dast, \"high\", GateAction.Fail)\n };\n }\n\n private static Dictionary\u003cstring, object> GetNestedDict(\n Dictionary\u003cstring, object>? dict, params string[] keys)\n {\n var current = dict;\n foreach (var key in keys)\n {\n if (current?.TryGetValue(key, out var value) == true &&\n value is Dictionary\u003cobject, object> nested)\n current = nested.ToDictionary(k => k.Key.ToString()!, v => v.Value);\n else\n return [];\n }\n return current ?? [];\n }\n\n private static GateAction ParseAction(Dictionary\u003cstring, object>? dict, string key, GateAction def) =>\n dict?.TryGetValue(key, out var v) == true && Enum.TryParse\u003cGateAction>(v?.ToString(), true, out var a) ? a : def;\n\n private static double GetDouble(Dictionary\u003cstring, object>? dict, string key, double def) =>\n dict?.TryGetValue(key, out var v) == true && double.TryParse(v?.ToString(), out var d) ? d : def;\n\n private static List\u003cstring> GetStringList(Dictionary\u003cstring, object>? dict, string key) =>\n dict?.TryGetValue(key, out var v) == true && v is List\u003cobject> list\n ? list.Select(x => x.ToString()!).ToList() : [];\n}\n\npublic sealed class SecurityGateEngine(PolicyConfig policy)\n{\n private readonly List\u003cFinding> _findings = [];\n private readonly HashSet\u003cstring> _licenses = [];\n\n private static readonly HashSet\u003cstring> SastScanners = [\"semgrep\", \"codeql\", \"sonarqube\"];\n private static readonly HashSet\u003cstring> ScaScanners = [\"npm-audit\", \"snyk\", \"pip-audit\", \"dotnet-audit\"];\n private static readonly HashSet\u003cstring> DastScanners = [\"zap\", \"burp\"];\n\n public void LoadSarif(string sarifPath, string scanner = \"unknown\")\n {\n var json = File.ReadAllText(sarifPath);\n var sarif = JsonNode.Parse(json);\n\n foreach (var run in sarif?[\"runs\"]?.AsArray() ?? [])\n {\n var toolName = run?[\"tool\"]?[\"driver\"]?[\"name\"]?.GetValue\u003cstring>() ?? scanner;\n\n foreach (var result in run?[\"results\"]?.AsArray() ?? [])\n {\n var severity = SarifToSeverity(result?[\"level\"]?.GetValue\u003cstring>() ?? \"warning\");\n string? location = null;\n\n var locations = result?[\"locations\"]?.AsArray();\n if (locations?.Count > 0)\n {\n var loc = locations[0]?[\"physicalLocation\"];\n var artifact = loc?[\"artifactLocation\"]?[\"uri\"]?.GetValue\u003cstring>() ?? \"\";\n var line = loc?[\"region\"]?[\"startLine\"]?.GetValue\u003cint>() ?? 0;\n location = $\"{artifact}:{line}\";\n }\n\n string? cwe = null;\n var tags = result?[\"properties\"]?[\"tags\"]?.AsArray();\n if (tags is not null)\n cwe = tags.Select(t => t?.GetValue\u003cstring>()).FirstOrDefault(t => t?.StartsWith(\"CWE-\") == true);\n\n _findings.Add(new Finding(\n Scanner: toolName,\n Severity: severity,\n Title: result?[\"ruleId\"]?.GetValue\u003cstring>() ?? \"Unknown\",\n Description: result?[\"message\"]?[\"text\"]?.GetValue\u003cstring>() ?? \"\",\n Location: location,\n Cwe: cwe));\n }\n }\n }\n\n public void LoadNpmAudit(string auditPath)\n {\n var json = File.ReadAllText(auditPath);\n var audit = JsonNode.Parse(json);\n\n foreach (var (name, vuln) in audit?[\"vulnerabilities\"]?.AsObject() ?? [])\n {\n var severityStr = vuln?[\"severity\"]?.GetValue\u003cstring>() ?? \"low\";\n var severity = Enum.TryParse\u003cFindingSeverity>(severityStr, true, out var s) ? s : FindingSeverity.Low;\n\n foreach (var via in vuln?[\"via\"]?.AsArray() ?? [])\n {\n if (via is JsonObject viaObj)\n {\n _findings.Add(new Finding(\n Scanner: \"npm-audit\",\n Severity: severity,\n Title: viaObj[\"title\"]?.GetValue\u003cstring>() ?? name,\n Description: viaObj[\"url\"]?.GetValue\u003cstring>() ?? \"\",\n Cve: viaObj[\"cve\"]?.GetValue\u003cstring>(),\n Cvss: viaObj[\"cvss\"]?[\"score\"]?.GetValue\u003cdouble>(),\n FixAvailable: vuln?[\"fixAvailable\"]?.GetValue\u003cbool>() ?? false));\n }\n }\n }\n }\n\n public void LoadGitleaks(string gitleaksPath)\n {\n var json = File.ReadAllText(gitleaksPath);\n var findings = JsonNode.Parse(json)?.AsArray() ?? [];\n\n foreach (var finding in findings)\n {\n _findings.Add(new Finding(\n Scanner: \"gitleaks\",\n Severity: FindingSeverity.Critical,\n Title: finding?[\"RuleID\"]?.GetValue\u003cstring>() ?? \"Secret\",\n Description: $\"Secret found in {finding?[\"File\"]?.GetValue\u003cstring>() ?? \"unknown\"}\",\n Location: $\"{finding?[\"File\"]?.GetValue\u003cstring>()}:{finding?[\"StartLine\"]?.GetValue\u003cint>()}\"));\n }\n }\n\n public void LoadLicenseScan(string licensePath)\n {\n var json = File.ReadAllText(licensePath);\n var data = JsonNode.Parse(json);\n\n foreach (var package in data?[\"packages\"]?.AsArray() ?? [])\n {\n var license = package?[\"license\"]?.GetValue\u003cstring>() ?? \"Unknown\";\n _licenses.Add(license);\n }\n }\n\n public GateResult Evaluate()\n {\n var result = new GateResult { Findings = [.. _findings] };\n\n var counts = new Dictionary\u003cstring, Dictionary\u003cstring, int>>\n {\n [\"sast\"] = new() { [\"critical\"] = 0, [\"high\"] = 0, [\"medium\"] = 0, [\"low\"] = 0 },\n [\"sca\"] = new() { [\"critical\"] = 0, [\"high\"] = 0, [\"medium\"] = 0, [\"low\"] = 0 },\n [\"dast\"] = new() { [\"critical\"] = 0, [\"high\"] = 0, [\"medium\"] = 0, [\"low\"] = 0 }\n };\n var secretsCount = 0;\n\n foreach (var finding in _findings)\n {\n var sev = finding.Severity.ToString().ToLowerInvariant();\n\n if (SastScanners.Contains(finding.Scanner))\n counts[\"sast\"][sev]++;\n else if (ScaScanners.Contains(finding.Scanner))\n counts[\"sca\"][sev]++;\n else if (finding.Scanner == \"gitleaks\")\n secretsCount++;\n else if (DastScanners.Contains(finding.Scanner))\n counts[\"dast\"][sev]++;\n }\n\n result.Metrics = counts;\n result.SecretsCount = secretsCount;\n\n // Evaluate thresholds\n EvaluateThreshold(result, \"SAST Critical\", counts[\"sast\"][\"critical\"], 0, policy.SastCritical);\n EvaluateThreshold(result, \"SAST High\", counts[\"sast\"][\"high\"], 0, policy.SastHigh);\n EvaluateThreshold(result, \"SAST Medium\", counts[\"sast\"][\"medium\"], 5, policy.SastMedium);\n EvaluateThreshold(result, \"SCA Critical\", counts[\"sca\"][\"critical\"], 0, policy.ScaCritical);\n EvaluateThreshold(result, \"SCA High\", counts[\"sca\"][\"high\"], 0, policy.ScaHigh);\n\n var highCvss = _findings.Count(f => ScaScanners.Contains(f.Scanner) && f.Cvss >= policy.ScaCvssThreshold);\n if (highCvss > 0)\n EvaluateThreshold(result, $\"SCA CVSS >= {policy.ScaCvssThreshold}\", highCvss, 0, GateAction.Fail);\n\n if (secretsCount > 0)\n EvaluateThreshold(result, \"Secrets\", secretsCount, 0, policy.SecretsAction);\n\n var forbidden = _licenses.Intersect(policy.ForbiddenLicenses).ToList();\n if (forbidden.Count > 0)\n EvaluateThreshold(result, $\"Forbidden Licenses ({string.Join(\", \", forbidden)})\", forbidden.Count, 0, policy.LicenseAction);\n\n EvaluateThreshold(result, \"DAST Critical\", counts[\"dast\"][\"critical\"], 0, policy.DastCritical);\n EvaluateThreshold(result, \"DAST High\", counts[\"dast\"][\"high\"], 0, policy.DastHigh);\n\n return result;\n }\n\n private static void EvaluateThreshold(GateResult result, string checkName, int count, int threshold, GateAction action)\n {\n if (count \u003c= threshold) return;\n\n if (action == GateAction.Fail)\n {\n result.Action = GateAction.Fail;\n result.Reasons.Add($\"FAIL: {checkName} = {count} (threshold: {threshold})\");\n }\n else if (action == GateAction.Warn)\n {\n if (result.Action != GateAction.Fail)\n result.Action = GateAction.Warn;\n result.Reasons.Add($\"WARN: {checkName} = {count} (threshold: {threshold})\");\n }\n }\n\n private static FindingSeverity SarifToSeverity(string level) => level switch\n {\n \"error\" => FindingSeverity.High,\n \"warning\" => FindingSeverity.Medium,\n \"note\" => FindingSeverity.Low,\n _ => FindingSeverity.Info\n };\n}\n\npublic static class SecurityGateReport\n{\n public static string GenerateMarkdown(GateResult result)\n {\n var sb = new StringBuilder();\n sb.AppendLine(\"# Security Gate Report\");\n sb.AppendLine();\n sb.AppendLine($\"## Status: {result.Action.ToString().ToUpperInvariant()}\");\n sb.AppendLine();\n\n if (result.Reasons.Count > 0)\n {\n sb.AppendLine(\"## Findings Summary\");\n sb.AppendLine();\n foreach (var reason in result.Reasons)\n sb.AppendLine($\"- {reason}\");\n sb.AppendLine();\n }\n\n sb.AppendLine(\"## Metrics\");\n sb.AppendLine();\n sb.AppendLine(\"| Scanner | Critical | High | Medium | Low |\");\n sb.AppendLine(\"|---------|----------|------|--------|-----|\");\n\n foreach (var scanner in new[] { \"sast\", \"sca\", \"dast\" })\n {\n var m = result.Metrics.GetValueOrDefault(scanner, []);\n sb.AppendLine($\"| {scanner.ToUpperInvariant()} | {m.GetValueOrDefault(\"critical\")} | {m.GetValueOrDefault(\"high\")} | {m.GetValueOrDefault(\"medium\")} | {m.GetValueOrDefault(\"low\")} |\");\n }\n\n if (result.SecretsCount > 0)\n {\n sb.AppendLine();\n sb.AppendLine($\"**Secrets Found:** {result.SecretsCount}\");\n }\n\n return sb.ToString();\n }\n}\n\n// CLI usage\nvar policy = File.Exists(\"security-policy.yml\")\n ? PolicyConfig.FromYaml(\"security-policy.yml\", \"staging\")\n : new PolicyConfig();\n\nvar engine = new SecurityGateEngine(policy);\n\nforeach (var sarif in Directory.GetFiles(\".\", \"*.sarif\"))\n engine.LoadSarif(sarif);\n\nif (File.Exists(\"npm-audit.json\"))\n engine.LoadNpmAudit(\"npm-audit.json\");\n\nif (File.Exists(\"gitleaks.json\"))\n engine.LoadGitleaks(\"gitleaks.json\");\n\nvar result = engine.Evaluate();\nvar report = SecurityGateReport.GenerateMarkdown(result);\n\nawait File.WriteAllTextAsync(\"security-report.md\", report);\nConsole.WriteLine(report);\n\nreturn result.Action == GateAction.Fail ? 1 : 0;\n```\n\n## GitHub Actions Complete Gate\n\n```yaml\n# .github/workflows/security-gate.yml\nname: Security Gate\non:\n pull_request:\n branches: [main, release/*]\n push:\n branches: [main]\n\nenv:\n ENVIRONMENT: ${{ github.event_name == 'push' && 'production' || 'staging' }}\n\njobs:\n sast:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n\n - name: Semgrep\n uses: semgrep/semgrep-action@v1\n with:\n config: auto\n generateSarif: true\n\n - name: Upload SARIF\n uses: actions/upload-artifact@v4\n with:\n name: sast-sarif\n path: semgrep.sarif\n\n sca:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n\n - name: npm audit\n if: hashFiles('package-lock.json') != ''\n run: |\n npm audit --json > npm-audit.json || true\n\n - name: pip-audit\n if: hashFiles('requirements.txt') != ''\n run: |\n pip install pip-audit\n pip-audit -r requirements.txt --format json > pip-audit.json || true\n\n - name: Upload results\n uses: actions/upload-artifact@v4\n with:\n name: sca-results\n path: |\n npm-audit.json\n pip-audit.json\n\n secrets:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n\n - name: Gitleaks\n uses: gitleaks/gitleaks-action@v2\n continue-on-error: true\n env:\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n GITLEAKS_ENABLE_COMMENTS: false\n\n - name: Upload results\n uses: actions/upload-artifact@v4\n with:\n name: secrets-results\n path: gitleaks-report.json\n if: always()\n\n licenses:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n\n - name: License check\n run: |\n npm install -g license-checker\n license-checker --json > licenses.json\n\n - name: Upload results\n uses: actions/upload-artifact@v4\n with:\n name: license-results\n path: licenses.json\n\n evaluate:\n needs: [sast, sca, secrets, licenses]\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n\n - name: Download all artifacts\n uses: actions/download-artifact@v4\n with:\n path: scan-results\n\n - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: '3.11'\n\n - name: Install dependencies\n run: pip install pyyaml\n\n - name: Evaluate Security Gate\n id: gate\n run: |\n python scripts/security_gate.py \\\n --policy security-policy.yml \\\n --environment ${{ env.ENVIRONMENT }} \\\n --sarif scan-results/sast-sarif/semgrep.sarif \\\n --npm-audit scan-results/sca-results/npm-audit.json \\\n --gitleaks scan-results/secrets-results/gitleaks-report.json \\\n --licenses scan-results/license-results/licenses.json \\\n --output security-report.md\n\n - name: Upload report\n uses: actions/upload-artifact@v4\n with:\n name: security-report\n path: security-report.md\n\n - name: Comment on PR\n if: github.event_name == 'pull_request'\n uses: actions/github-script@v7\n with:\n script: |\n const fs = require('fs');\n const report = fs.readFileSync('security-report.md', 'utf8');\n github.rest.issues.createComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n body: report\n });\n```\n\n## Break Glass Procedure\n\n### Emergency Bypass Workflow\n\n```yaml\n# .github/workflows/bypass-gate.yml\nname: Security Gate Bypass\non:\n workflow_dispatch:\n inputs:\n reason:\n description: 'Reason for bypass'\n required: true\n approver:\n description: 'Security team approver'\n required: true\n ticket:\n description: 'Associated ticket number'\n required: true\n expiry_hours:\n description: 'Bypass expiry (hours)'\n required: true\n default: '24'\n\njobs:\n validate:\n runs-on: ubuntu-latest\n steps:\n - name: Verify approver\n run: |\n ALLOWED_APPROVERS=\"security-lead,cto,vp-engineering\"\n if [[ ! \",${ALLOWED_APPROVERS},\" =~ \",${{ github.event.inputs.approver }},\" ]]; then\n echo \"Error: ${{ github.event.inputs.approver }} is not authorized\"\n exit 1\n fi\n\n - name: Create bypass record\n run: |\n cat \u003c\u003c EOF > bypass-record.json\n {\n \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\n \"requester\": \"${{ github.actor }}\",\n \"approver\": \"${{ github.event.inputs.approver }}\",\n \"reason\": \"${{ github.event.inputs.reason }}\",\n \"ticket\": \"${{ github.event.inputs.ticket }}\",\n \"expiry\": \"$(date -u -d '+${{ github.event.inputs.expiry_hours }} hours' +%Y-%m-%dT%H:%M:%SZ)\",\n \"commit\": \"${{ github.sha }}\"\n }\n EOF\n\n - name: Store bypass\n run: |\n # Store in secure location (e.g., S3, Vault)\n aws s3 cp bypass-record.json s3://security-audit-trail/bypasses/\n\n - name: Notify security team\n run: |\n # Send notification to security team\n echo \"Security gate bypass requested by ${{ github.actor }}\"\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":24280,"content_sha256":"1297594690cf81bebb8faf00ebdf169072ebac1b9992f0107e3133e6bb3a10c0"},{"filename":"references/vulnerability-workflow.md","content":"# Vulnerability Management Workflow Reference\n\n## Overview\n\nThis reference provides detailed implementation patterns for enterprise vulnerability management workflows, including ticketing integration, SLA enforcement, risk-based prioritization, and metrics tracking.\n\n## Vulnerability Intake and Triage\n\n### Unified Vulnerability Ingestion\n\n```csharp\nusing System.Security.Cryptography;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing System.Text.Json.Serialization;\n\n/// \u003csummary>\n/// Unified vulnerability ingestion from multiple scanners.\n/// Normalizes findings to common format and deduplicates.\n/// \u003c/summary>\n\npublic enum VulnerabilitySource\n{\n Sast,\n Dast,\n Sca,\n Container,\n Cloud,\n Pentest,\n BugBounty\n}\n\npublic enum Severity\n{\n Critical,\n High,\n Medium,\n Low,\n Info\n}\n\npublic enum VulnerabilityStatus\n{\n New,\n Triaged,\n InProgress,\n Remediated,\n Verified,\n AcceptedRisk,\n FalsePositive\n}\n\n/// \u003csummary>\n/// Normalized vulnerability record.\n/// \u003c/summary>\npublic sealed class Vulnerability\n{\n public required string Id { get; init; }\n public required string Title { get; init; }\n public required string Description { get; init; }\n public required Severity Severity { get; init; }\n public required VulnerabilitySource Source { get; init; }\n public required string Scanner { get; init; }\n\n // Location\n public string? FilePath { get; init; }\n public int? LineNumber { get; init; }\n public string? Url { get; init; }\n public string? Component { get; init; }\n\n // Classification\n public string? CweId { get; init; }\n public string? CveId { get; init; }\n public double? CvssScore { get; init; }\n public string? OwaspCategory { get; init; }\n\n // Context\n public string? AffectedAsset { get; init; }\n public string Environment { get; init; } = \"production\";\n public string Exploitability { get; init; } = \"unknown\";\n public string BusinessImpact { get; init; } = \"unknown\";\n\n // Tracking\n public VulnerabilityStatus Status { get; set; } = VulnerabilityStatus.New;\n public DateTimeOffset DiscoveredAt { get; init; } = DateTimeOffset.UtcNow;\n public DateTimeOffset? SlaDueDate { get; set; }\n public string? AssignedTo { get; set; }\n public string? TicketId { get; set; }\n\n // Metadata\n public JsonObject RawFinding { get; init; } = [];\n public List\u003cstring> Tags { get; init; } = [];\n\n /// \u003csummary>\n /// Generate unique fingerprint for deduplication.\n /// \u003c/summary>\n public string GenerateFingerprint()\n {\n var components = new[]\n {\n Source.ToString().ToLowerInvariant(),\n FilePath ?? \"\",\n LineNumber?.ToString() ?? \"\",\n Url ?? \"\",\n CweId ?? \"\",\n CveId ?? \"\",\n Title.Length > 100 ? Title[..100] : Title\n };\n\n var content = string.Join(\"|\", components);\n var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(content));\n return Convert.ToHexString(hashBytes)[..16].ToLowerInvariant();\n }\n}\n\n/// \u003csummary>\n/// Ingest and normalize vulnerabilities from multiple sources.\n/// \u003c/summary>\npublic sealed class VulnerabilityIngester(IngesterConfig config)\n{\n private static readonly Dictionary\u003cSeverity, int> DefaultSlaDays = new()\n {\n [Severity.Critical] = 1,\n [Severity.High] = 7,\n [Severity.Medium] = 30,\n [Severity.Low] = 90,\n [Severity.Info] = 180\n };\n\n /// \u003csummary>\n /// Ingest SARIF format (SAST tools).\n /// \u003c/summary>\n public List\u003cVulnerability> IngestSarif(JsonObject sarifData, VulnerabilitySource source)\n {\n var vulnerabilities = new List\u003cVulnerability>();\n var runs = sarifData[\"runs\"]?.AsArray() ?? [];\n\n foreach (var run in runs)\n {\n var toolName = run?[\"tool\"]?[\"driver\"]?[\"name\"]?.GetValue\u003cstring>() ?? \"unknown\";\n var rules = (run?[\"tool\"]?[\"driver\"]?[\"rules\"]?.AsArray() ?? [])\n .Where(r => r?[\"id\"] is not null)\n .ToDictionary(r => r![\"id\"]!.GetValue\u003cstring>(), r => r!.AsObject());\n\n var results = run?[\"results\"]?.AsArray() ?? [];\n foreach (var result in results)\n {\n var ruleId = result?[\"ruleId\"]?.GetValue\u003cstring>() ?? \"\";\n rules.TryGetValue(ruleId, out var rule);\n var severity = MapSarifSeverity(result?[\"level\"]?.GetValue\u003cstring>() ?? \"warning\");\n\n var location = result?[\"locations\"]?.AsArray().FirstOrDefault()?[\"physicalLocation\"];\n var filePath = location?[\"artifactLocation\"]?[\"uri\"]?.GetValue\u003cstring>() ?? \"\";\n var line = location?[\"region\"]?[\"startLine\"]?.GetValue\u003cint>() ?? 0;\n\n var vuln = new Vulnerability\n {\n Id = $\"{source.ToString().ToLowerInvariant()}-{GenerateId()}\",\n Title = rule?[\"shortDescription\"]?[\"text\"]?.GetValue\u003cstring>()\n ?? result?[\"ruleId\"]?.GetValue\u003cstring>()\n ?? \"Unknown\",\n Description = result?[\"message\"]?[\"text\"]?.GetValue\u003cstring>() ?? \"\",\n Severity = severity,\n Source = source,\n Scanner = toolName,\n FilePath = filePath,\n LineNumber = line > 0 ? line : null,\n CweId = ExtractCwe(rule),\n RawFinding = result?.AsObject() ?? []\n };\n\n vuln.SlaDueDate = CalculateSla(vuln);\n vulnerabilities.Add(vuln);\n }\n }\n\n return vulnerabilities;\n }\n\n /// \u003csummary>\n /// Ingest CycloneDX VEX format (SCA findings).\n /// \u003c/summary>\n public List\u003cVulnerability> IngestCycloneDxVex(JsonObject vexData)\n {\n var vulnerabilities = new List\u003cVulnerability>();\n var vulnDataArray = vexData[\"vulnerabilities\"]?.AsArray() ?? [];\n\n foreach (var vulnData in vulnDataArray)\n {\n var ratings = vulnData?[\"ratings\"]?.AsArray() ?? [];\n var severity = MapCycloneDxSeverity(ratings);\n\n var affects = vulnData?[\"affects\"]?.AsArray() ?? [];\n foreach (var affected in affects)\n {\n var vuln = new Vulnerability\n {\n Id = $\"sca-{GenerateId()}\",\n Title = vulnData?[\"id\"]?.GetValue\u003cstring>() ?? \"Unknown CVE\",\n Description = vulnData?[\"description\"]?.GetValue\u003cstring>() ?? \"\",\n Severity = severity,\n Source = VulnerabilitySource.Sca,\n Scanner = \"cyclonedx\",\n Component = affected?[\"ref\"]?.GetValue\u003cstring>(),\n CveId = vulnData?[\"id\"]?.GetValue\u003cstring>(),\n CvssScore = ExtractCvss(ratings),\n RawFinding = vulnData?.AsObject() ?? []\n };\n\n vuln.SlaDueDate = CalculateSla(vuln);\n vulnerabilities.Add(vuln);\n }\n }\n\n return vulnerabilities;\n }\n\n /// \u003csummary>\n /// Ingest OWASP ZAP JSON format (DAST findings).\n /// \u003c/summary>\n public List\u003cVulnerability> IngestZapReport(JsonObject zapData)\n {\n var vulnerabilities = new List\u003cVulnerability>();\n var sites = zapData[\"site\"]?.AsArray() ?? [];\n\n foreach (var site in sites)\n {\n var alerts = site?[\"alerts\"]?.AsArray() ?? [];\n foreach (var alert in alerts)\n {\n var severity = MapZapRisk(alert?[\"riskcode\"]?.GetValue\u003cstring>() ?? \"0\");\n var instances = alert?[\"instances\"]?.AsArray() ?? [];\n\n foreach (var instance in instances)\n {\n var vuln = new Vulnerability\n {\n Id = $\"dast-{GenerateId()}\",\n Title = alert?[\"name\"]?.GetValue\u003cstring>() ?? \"Unknown\",\n Description = alert?[\"desc\"]?.GetValue\u003cstring>() ?? \"\",\n Severity = severity,\n Source = VulnerabilitySource.Dast,\n Scanner = \"owasp-zap\",\n Url = instance?[\"uri\"]?.GetValue\u003cstring>(),\n CweId = alert?[\"cweid\"]?.GetValue\u003cstring>(),\n RawFinding = alert?.AsObject() ?? []\n };\n\n vuln.SlaDueDate = CalculateSla(vuln);\n vulnerabilities.Add(vuln);\n }\n }\n }\n\n return vulnerabilities;\n }\n\n private DateTimeOffset CalculateSla(Vulnerability vuln)\n {\n var slaDays = config.SlaPolicy.TryGetValue(vuln.Severity, out var days)\n ? days\n : DefaultSlaDays.GetValueOrDefault(vuln.Severity, 90);\n\n return vuln.DiscoveredAt.AddDays(slaDays);\n }\n\n private static Severity MapSarifSeverity(string level) => level switch\n {\n \"error\" => Severity.High,\n \"warning\" => Severity.Medium,\n \"note\" => Severity.Low,\n \"none\" => Severity.Info,\n _ => Severity.Medium\n };\n\n private static Severity MapCycloneDxSeverity(JsonArray ratings)\n {\n foreach (var rating in ratings)\n {\n if (rating?[\"method\"]?.GetValue\u003cstring>() == \"CVSSv3\")\n {\n var score = rating?[\"score\"]?.GetValue\u003cdouble>() ?? 0;\n return score switch\n {\n >= 9.0 => Severity.Critical,\n >= 7.0 => Severity.High,\n >= 4.0 => Severity.Medium,\n > 0 => Severity.Low,\n _ => Severity.Medium\n };\n }\n }\n return Severity.Medium;\n }\n\n private static Severity MapZapRisk(string riskCode) => riskCode switch\n {\n \"3\" => Severity.High,\n \"2\" => Severity.Medium,\n \"1\" => Severity.Low,\n \"0\" => Severity.Info,\n _ => Severity.Medium\n };\n\n private static string? ExtractCwe(JsonObject? rule)\n {\n var tags = rule?[\"properties\"]?[\"tags\"]?.AsArray() ?? [];\n return tags\n .Select(t => t?.GetValue\u003cstring>())\n .FirstOrDefault(t => t?.StartsWith(\"CWE-\", StringComparison.OrdinalIgnoreCase) == true);\n }\n\n private static double? ExtractCvss(JsonArray ratings)\n {\n foreach (var rating in ratings)\n {\n if (rating?[\"method\"]?.GetValue\u003cstring>() == \"CVSSv3\")\n return rating?[\"score\"]?.GetValue\u003cdouble>();\n }\n return null;\n }\n\n private static string GenerateId() => Guid.NewGuid().ToString(\"N\")[..8];\n}\n\npublic sealed record IngesterConfig\n{\n public Dictionary\u003cSeverity, int> SlaPolicy { get; init; } = [];\n}\n```\n\n## Risk-Based Prioritization\n\n### EPSS and Exploit Intelligence\n\n```csharp\nusing System.Collections.Concurrent;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\n\n/// \u003csummary>\n/// Risk-based prioritization using EPSS, threat intelligence, and business context.\n/// \u003c/summary>\n\n/// \u003csummary>\n/// Composite risk score with components.\n/// \u003c/summary>\npublic sealed record RiskScore(\n double TotalScore, // 0-100\n double SeverityScore,\n double ExploitabilityScore,\n double BusinessImpactScore,\n double AssetCriticalityScore,\n double ThreatIntelScore)\n{\n public string Priority => TotalScore switch\n {\n >= 80 => \"P1-Critical\",\n >= 60 => \"P2-High\",\n >= 40 => \"P3-Medium\",\n >= 20 => \"P4-Low\",\n _ => \"P5-Info\"\n };\n}\n\n/// \u003csummary>\n/// Calculate risk scores for vulnerabilities.\n/// \u003c/summary>\npublic sealed class RiskPrioritizer(HttpClient httpClient, PrioritizerConfig config)\n{\n private readonly ConcurrentDictionary\u003cstring, double?> _epssCache = new();\n private HashSet\u003cstring>? _kevCatalog;\n private DateTimeOffset _kevCacheExpiry = DateTimeOffset.MinValue;\n\n private static readonly Dictionary\u003cSeverity, double> SeverityScores = new()\n {\n [Severity.Critical] = 25,\n [Severity.High] = 20,\n [Severity.Medium] = 12,\n [Severity.Low] = 5,\n [Severity.Info] = 1\n };\n\n private static readonly Dictionary\u003cVulnerabilitySource, double> SourceDefaults = new()\n {\n [VulnerabilitySource.Dast] = 20, // Actively exploitable\n [VulnerabilitySource.Pentest] = 22,\n [VulnerabilitySource.BugBounty] = 20,\n [VulnerabilitySource.Sast] = 10,\n [VulnerabilitySource.Sca] = 12\n };\n\n private static readonly Dictionary\u003cstring, double> EnvironmentDefaults = new()\n {\n [\"production\"] = 15,\n [\"staging\"] = 8,\n [\"development\"] = 3\n };\n\n private static readonly HashSet\u003cstring> ThreatTags =\n [\"actively-exploited\", \"ransomware\", \"apt\", \"zero-day\"];\n\n /// \u003csummary>\n /// Calculate composite risk score.\n /// \u003c/summary>\n public async Task\u003cRiskScore> CalculateRiskAsync(\n Vulnerability vuln,\n CancellationToken ct = default)\n {\n // Base severity score (0-25)\n var severityScore = SeverityScores.GetValueOrDefault(vuln.Severity, 10);\n\n // Exploitability from EPSS or CVSS (0-25)\n var exploitabilityScore = await GetExploitabilityScoreAsync(vuln, ct);\n\n // Business impact based on affected component (0-20)\n var businessImpactScore = GetBusinessImpact(vuln);\n\n // Asset criticality (0-15)\n var assetCriticalityScore = GetAssetCriticality(vuln);\n\n // Threat intelligence (0-15)\n var threatIntelScore = await GetThreatIntelScoreAsync(vuln, ct);\n\n var total = severityScore +\n exploitabilityScore +\n businessImpactScore +\n assetCriticalityScore +\n threatIntelScore;\n\n return new RiskScore(\n TotalScore: Math.Min(100, total),\n SeverityScore: severityScore,\n ExploitabilityScore: exploitabilityScore,\n BusinessImpactScore: businessImpactScore,\n AssetCriticalityScore: assetCriticalityScore,\n ThreatIntelScore: threatIntelScore);\n }\n\n private async Task\u003cdouble> GetExploitabilityScoreAsync(\n Vulnerability vuln,\n CancellationToken ct)\n {\n // Try EPSS first for CVEs\n if (vuln.CveId is not null)\n {\n var epssScore = await FetchEpssScoreAsync(vuln.CveId, ct);\n if (epssScore.HasValue)\n return epssScore.Value * 25; // EPSS is 0-1, scale to 0-25\n }\n\n // Fall back to CVSS exploitability\n if (vuln.CvssScore.HasValue)\n return (vuln.CvssScore.Value / 10) * 25;\n\n // Default based on source\n return SourceDefaults.GetValueOrDefault(vuln.Source, 10);\n }\n\n private async Task\u003cdouble?> FetchEpssScoreAsync(string cveId, CancellationToken ct)\n {\n if (_epssCache.TryGetValue(cveId, out var cached))\n return cached;\n\n try\n {\n using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);\n cts.CancelAfter(TimeSpan.FromSeconds(5));\n\n var url = $\"https://api.first.org/data/v1/epss?cve={Uri.EscapeDataString(cveId)}\";\n var response = await httpClient.GetAsync(url, cts.Token);\n\n if (response.IsSuccessStatusCode)\n {\n var json = await response.Content.ReadFromJsonAsync\u003cJsonObject>(cts.Token);\n var dataArray = json?[\"data\"]?.AsArray();\n\n if (dataArray?.Count > 0)\n {\n var score = dataArray[0]?[\"epss\"]?.GetValue\u003cdouble>();\n _epssCache[cveId] = score;\n return score;\n }\n }\n }\n catch (OperationCanceledException) { }\n catch (HttpRequestException) { }\n\n _epssCache[cveId] = null;\n return null;\n }\n\n private double GetBusinessImpact(Vulnerability vuln)\n {\n var component = vuln.Component ?? vuln.AffectedAsset ?? \"\";\n\n foreach (var (pattern, impact) in config.BusinessContext)\n {\n if (component.Contains(pattern, StringComparison.OrdinalIgnoreCase))\n return impact * 20; // Scale to 0-20\n }\n\n return EnvironmentDefaults.GetValueOrDefault(vuln.Environment, 10);\n }\n\n private double GetAssetCriticality(Vulnerability vuln)\n {\n var asset = vuln.AffectedAsset ?? \"\";\n\n foreach (var (pattern, criticality) in config.AssetCriticality)\n {\n if (asset.Contains(pattern, StringComparison.OrdinalIgnoreCase))\n return criticality * 15; // Scale to 0-15\n }\n\n return 7.5; // Default medium criticality\n }\n\n private async Task\u003cdouble> GetThreatIntelScoreAsync(\n Vulnerability vuln,\n CancellationToken ct)\n {\n double score = 0;\n\n if (vuln.CveId is not null)\n {\n if (await CheckKevAsync(vuln.CveId, ct))\n score = 15; // Max score if in KEV catalog\n else if (await CheckExploitDbAsync(vuln.CveId, ct))\n score = 10;\n }\n\n // Check tags for threat intel indicators\n foreach (var tag in vuln.Tags)\n {\n if (ThreatTags.Contains(tag.ToLowerInvariant()))\n {\n score = Math.Max(score, 12);\n break;\n }\n }\n\n return Math.Min(15, score);\n }\n\n private async Task\u003cbool> CheckKevAsync(string cveId, CancellationToken ct)\n {\n // Refresh KEV catalog if expired (cache for 1 hour)\n if (_kevCatalog is null || DateTimeOffset.UtcNow > _kevCacheExpiry)\n {\n try\n {\n using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);\n cts.CancelAfter(TimeSpan.FromSeconds(10));\n\n const string kevUrl = \"https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json\";\n var json = await httpClient.GetFromJsonAsync\u003cJsonObject>(kevUrl, cts.Token);\n var vulns = json?[\"vulnerabilities\"]?.AsArray() ?? [];\n\n _kevCatalog = vulns\n .Select(v => v?[\"cveID\"]?.GetValue\u003cstring>())\n .Where(id => id is not null)\n .ToHashSet()!;\n\n _kevCacheExpiry = DateTimeOffset.UtcNow.AddHours(1);\n }\n catch\n {\n _kevCatalog ??= [];\n }\n }\n\n return _kevCatalog.Contains(cveId);\n }\n\n private Task\u003cbool> CheckExploitDbAsync(string cveId, CancellationToken ct)\n {\n // Simplified check - in production use Exploit-DB API\n return Task.FromResult(false);\n }\n}\n\npublic sealed record PrioritizerConfig\n{\n /// \u003csummary>\n /// Pattern → impact multiplier (0-1) for business functions.\n /// \u003c/summary>\n public Dictionary\u003cstring, double> BusinessContext { get; init; } = [];\n\n /// \u003csummary>\n /// Pattern → criticality multiplier (0-1) for assets.\n /// \u003c/summary>\n public Dictionary\u003cstring, double> AssetCriticality { get; init; } = [];\n}\n```\n\n## Ticketing Integration\n\n> **Provider-Agnostic Note:** This section demonstrates work item tracking integration patterns. The interface can be implemented for any ticketing platform: Jira, Azure DevOps, GitHub Issues, Linear, ServiceNow, etc.\n\n### Work Item Tracker Interface\n\n```csharp\n/// \u003csummary>\n/// Provider-agnostic work item tracking interface.\n/// Implement for your ticketing platform (Jira, Azure DevOps, GitHub Issues, etc.)\n/// \u003c/summary>\npublic interface IVulnerabilityTracker\n{\n Task\u003cstring> CreateTicketAsync(Vulnerability vuln, RiskScore risk, CancellationToken ct = default);\n Task UpdateStatusAsync(string ticketId, VulnerabilityStatus status, CancellationToken ct = default);\n Task AddRemediationCommentAsync(string ticketId, string commitHash, string prUrl, CancellationToken ct = default);\n}\n\n/// \u003csummary>\n/// Work item tracker configuration.\n/// \u003c/summary>\npublic sealed record TrackerConfig\n{\n public required string ServerUrl { get; init; }\n public required string ProjectKey { get; init; }\n public string IssueType { get; init; } = \"Bug\";\n public Dictionary\u003cstring, string> PriorityMapping { get; init; } = new()\n {\n [\"P1-Critical\"] = \"Highest\",\n [\"P2-High\"] = \"High\",\n [\"P3-Medium\"] = \"Medium\",\n [\"P4-Low\"] = \"Low\",\n [\"P5-Info\"] = \"Lowest\"\n };\n public Dictionary\u003cstring, string> CustomFields { get; init; } = [];\n}\n\n/// \u003csummary>\n/// Example work item tracker implementation.\n/// Replace HttpClient calls with your ticketing platform's SDK or API.\n/// \u003c/summary>\npublic sealed class WorkItemTracker(\n HttpClient httpClient,\n TrackerConfig config,\n ILogger\u003cWorkItemTracker> logger) : IVulnerabilityTracker\n{\n public async Task\u003cstring> CreateTicketAsync(\n Vulnerability vuln,\n RiskScore risk,\n CancellationToken ct = default)\n {\n var priority = config.PriorityMapping.GetValueOrDefault(risk.Priority, \"Medium\");\n\n var workItem = new WorkItemRequest\n {\n Project = config.ProjectKey,\n Summary = $\"[{vuln.Source.ToString().ToUpperInvariant()}] {vuln.Title}\",\n Description = BuildDescription(vuln, risk),\n IssueType = config.IssueType,\n Priority = priority,\n Labels = BuildLabels(vuln),\n DueDate = vuln.SlaDueDate\n };\n\n // Add custom fields if configured\n if (config.CustomFields.TryGetValue(\"severity_field\", out var severityField))\n workItem.CustomFields[severityField] = vuln.Severity.ToString();\n\n if (config.CustomFields.TryGetValue(\"cve_field\", out var cveField) && vuln.CveId is not null)\n workItem.CustomFields[cveField] = vuln.CveId;\n\n if (config.CustomFields.TryGetValue(\"risk_score_field\", out var riskField))\n workItem.CustomFields[riskField] = risk.TotalScore.ToString(\"F1\");\n\n var response = await httpClient.PostAsJsonAsync(\n $\"{config.ServerUrl}/api/issues\",\n workItem,\n JsonContext.Default.WorkItemRequest,\n ct);\n\n response.EnsureSuccessStatusCode();\n\n var result = await response.Content.ReadFromJsonAsync(\n JsonContext.Default.WorkItemResponse, ct);\n\n logger.LogInformation(\"Created work item {TicketId} for {CveId}\",\n result?.Key, vuln.CveId);\n\n return result?.Key ?? throw new InvalidOperationException(\"No ticket key returned\");\n }\n\n public async Task UpdateStatusAsync(\n string ticketId,\n VulnerabilityStatus status,\n CancellationToken ct = default)\n {\n var transitionName = status switch\n {\n VulnerabilityStatus.Triaged => \"Triage\",\n VulnerabilityStatus.InProgress => \"Start Progress\",\n VulnerabilityStatus.Remediated => \"Resolve\",\n VulnerabilityStatus.Verified => \"Close\",\n VulnerabilityStatus.AcceptedRisk => \"Accept Risk\",\n VulnerabilityStatus.FalsePositive => \"Mark False Positive\",\n _ => null\n };\n\n if (transitionName is null) return;\n\n var response = await httpClient.PostAsJsonAsync(\n $\"{config.ServerUrl}/api/issues/{ticketId}/transitions\",\n new { Name = transitionName },\n ct);\n\n response.EnsureSuccessStatusCode();\n }\n\n public async Task AddRemediationCommentAsync(\n string ticketId,\n string commitHash,\n string prUrl,\n CancellationToken ct = default)\n {\n var comment = $\"\"\"\n ### Remediation Applied\n - **Commit:** {commitHash}\n - **Pull Request:** {prUrl}\n - **Remediated:** {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC\n\n Please verify the fix and close this ticket if resolved.\n \"\"\";\n\n var response = await httpClient.PostAsJsonAsync(\n $\"{config.ServerUrl}/api/issues/{ticketId}/comments\",\n new { Body = comment },\n ct);\n\n response.EnsureSuccessStatusCode();\n }\n\n private static string BuildDescription(Vulnerability vuln, RiskScore risk)\n {\n var sb = new StringBuilder();\n\n sb.AppendLine(\"## Vulnerability Details\");\n sb.AppendLine($\"| Property | Value |\");\n sb.AppendLine($\"|----------|-------|\");\n sb.AppendLine($\"| Severity | {vuln.Severity} |\");\n sb.AppendLine($\"| Risk Score | {risk.TotalScore:F1}/100 ({risk.Priority}) |\");\n sb.AppendLine($\"| Source | {vuln.Source} |\");\n sb.AppendLine($\"| Scanner | {vuln.Scanner} |\");\n sb.AppendLine($\"| Discovered | {vuln.DiscoveredAt:yyyy-MM-dd HH:mm} UTC |\");\n sb.AppendLine($\"| SLA Due | {vuln.SlaDueDate?.ToString(\"yyyy-MM-dd\") ?? \"N/A\"} |\");\n\n if (vuln.FilePath is not null || vuln.Url is not null)\n {\n sb.AppendLine();\n sb.AppendLine(\"## Location\");\n sb.AppendLine($\"| Property | Value |\");\n sb.AppendLine($\"|----------|-------|\");\n sb.AppendLine($\"| File | {vuln.FilePath ?? \"N/A\"} |\");\n sb.AppendLine($\"| Line | {vuln.LineNumber?.ToString() ?? \"N/A\"} |\");\n sb.AppendLine($\"| URL | {vuln.Url ?? \"N/A\"} |\");\n }\n\n if (vuln.CveId is not null || vuln.CweId is not null)\n {\n sb.AppendLine();\n sb.AppendLine(\"## Classification\");\n sb.AppendLine($\"| Property | Value |\");\n sb.AppendLine($\"|----------|-------|\");\n sb.AppendLine($\"| CVE | {vuln.CveId ?? \"N/A\"} |\");\n sb.AppendLine($\"| CWE | {vuln.CweId ?? \"N/A\"} |\");\n sb.AppendLine($\"| CVSS | {vuln.CvssScore?.ToString(\"F1\") ?? \"N/A\"} |\");\n }\n\n sb.AppendLine();\n sb.AppendLine(\"## Description\");\n sb.AppendLine(vuln.Description);\n\n sb.AppendLine();\n sb.AppendLine(\"## Risk Score Breakdown\");\n sb.AppendLine($\"| Component | Score |\");\n sb.AppendLine($\"|-----------|-------|\");\n sb.AppendLine($\"| Severity | {risk.SeverityScore:F1} |\");\n sb.AppendLine($\"| Exploitability | {risk.ExploitabilityScore:F1} |\");\n sb.AppendLine($\"| Business Impact | {risk.BusinessImpactScore:F1} |\");\n sb.AppendLine($\"| **Total** | **{risk.TotalScore:F1}** |\");\n\n return sb.ToString();\n }\n\n private static List\u003cstring> BuildLabels(Vulnerability vuln)\n {\n var labels = new List\u003cstring>\n {\n $\"source-{vuln.Source.ToString().ToLowerInvariant()}\",\n $\"severity-{vuln.Severity.ToString().ToLowerInvariant()}\",\n \"security-vulnerability\"\n };\n\n if (vuln.CveId is not null)\n labels.Add(vuln.CveId.ToLowerInvariant().Replace(\"-\", \"_\"));\n\n if (vuln.CweId is not null)\n labels.Add(vuln.CweId.ToLowerInvariant().Replace(\"-\", \"_\"));\n\n labels.AddRange(vuln.Tags);\n\n return labels;\n }\n}\n\n// Source-generated JSON serialization for AOT compatibility\n[JsonSerializable(typeof(WorkItemRequest))]\n[JsonSerializable(typeof(WorkItemResponse))]\ninternal partial class JsonContext : JsonSerializerContext;\n\npublic sealed record WorkItemRequest\n{\n public required string Project { get; init; }\n public required string Summary { get; init; }\n public required string Description { get; init; }\n public required string IssueType { get; init; }\n public required string Priority { get; init; }\n public required List\u003cstring> Labels { get; init; }\n public DateTimeOffset? DueDate { get; init; }\n public Dictionary\u003cstring, string> CustomFields { get; init; } = [];\n}\n\npublic sealed record WorkItemResponse\n{\n public required string Key { get; init; }\n}\n```\n\n## SLA Enforcement and Escalation\n\n### SLA Monitor\n\n```csharp\nusing System.Net.Mail;\nusing System.Text.Json;\nusing Microsoft.Extensions.Logging;\n\n/// \u003csummary>\n/// SLA monitoring and automatic escalation.\n/// \u003c/summary>\n\npublic enum NotifyMethod { Email, Slack, PagerDuty, All }\n\n/// \u003csummary>\n/// Escalation level configuration.\n/// \u003c/summary>\npublic sealed record EscalationLevel(\n string Name,\n int HoursBeforeBreach,\n List\u003cstring> NotifyRoles,\n NotifyMethod NotifyMethod);\n\n/// \u003csummary>\n/// Escalation trigger information.\n/// \u003c/summary>\npublic sealed record EscalationTrigger(\n string VulnId,\n string Level,\n double HoursRemaining);\n\n/// \u003csummary>\n/// SLA compliance check results.\n/// \u003c/summary>\npublic sealed record SlaComplianceResult\n{\n public List\u003cVulnerability> Compliant { get; init; } = [];\n public List\u003cVulnerability> AtRisk { get; init; } = [];\n public List\u003cVulnerability> Breached { get; init; } = [];\n public List\u003cEscalationTrigger> EscalationsTriggered { get; init; } = [];\n}\n\n/// \u003csummary>\n/// Monitor SLA compliance and trigger escalations.\n/// \u003c/summary>\npublic sealed class SlaMonitor(\n HttpClient httpClient,\n SmtpClient smtpClient,\n SlaMonitorConfig config,\n ILogger\u003cSlaMonitor> logger)\n{\n private static readonly List\u003cEscalationLevel> DefaultEscalationLevels =\n [\n new(\"Warning\", 48, [\"assignee\", \"team_lead\"], NotifyMethod.Email),\n new(\"Urgent\", 24, [\"assignee\", \"team_lead\", \"security_manager\"], NotifyMethod.Slack),\n new(\"Critical\", 4, [\"assignee\", \"team_lead\", \"security_manager\", \"ciso\"], NotifyMethod.PagerDuty),\n new(\"Breach\", 0, [\"all_stakeholders\"], NotifyMethod.All)\n ];\n\n private static readonly Dictionary\u003cstring, string> LevelColors = new()\n {\n [\"Warning\"] = \"#FFA500\",\n [\"Urgent\"] = \"#FF6600\",\n [\"Critical\"] = \"#FF0000\",\n [\"Breach\"] = \"#8B0000\"\n };\n\n /// \u003csummary>\n /// Check SLA compliance for all vulnerabilities.\n /// \u003c/summary>\n public async Task\u003cSlaComplianceResult> CheckSlaComplianceAsync(\n IEnumerable\u003cVulnerability> vulnerabilities,\n CancellationToken ct = default)\n {\n var now = DateTimeOffset.UtcNow;\n var result = new SlaComplianceResult();\n var levels = config.EscalationLevels ?? DefaultEscalationLevels;\n\n foreach (var vuln in vulnerabilities)\n {\n if (vuln.Status is VulnerabilityStatus.Verified or VulnerabilityStatus.FalsePositive)\n continue;\n\n if (vuln.SlaDueDate is null)\n continue;\n\n var hoursRemaining = (vuln.SlaDueDate.Value - now).TotalHours;\n\n if (hoursRemaining \u003c 0)\n {\n result.Breached.Add(vuln);\n await TriggerEscalationAsync(vuln, levels[^1], ct);\n result.EscalationsTriggered.Add(new EscalationTrigger(\n vuln.Id, \"Breach\", hoursRemaining));\n }\n else\n {\n var escalated = false;\n foreach (var level in levels)\n {\n if (hoursRemaining \u003c= level.HoursBeforeBreach)\n {\n result.AtRisk.Add(vuln);\n await TriggerEscalationAsync(vuln, level, ct);\n result.EscalationsTriggered.Add(new EscalationTrigger(\n vuln.Id, level.Name, hoursRemaining));\n escalated = true;\n break;\n }\n }\n\n if (!escalated)\n result.Compliant.Add(vuln);\n }\n }\n\n return result;\n }\n\n private async Task TriggerEscalationAsync(\n Vulnerability vuln,\n EscalationLevel level,\n CancellationToken ct)\n {\n var message = BuildEscalationMessage(vuln, level);\n var recipients = ResolveRecipients(level.NotifyRoles, vuln);\n\n if (level.NotifyMethod == NotifyMethod.All)\n {\n await Task.WhenAll(\n SendEmailAsync(recipients, message, ct),\n SendSlackAsync(message, ct),\n SendPagerDutyAsync(vuln, level, message, ct));\n }\n else\n {\n var task = level.NotifyMethod switch\n {\n NotifyMethod.Email => SendEmailAsync(recipients, message, ct),\n NotifyMethod.Slack => SendSlackAsync(message, ct),\n NotifyMethod.PagerDuty => SendPagerDutyAsync(vuln, level, message, ct),\n _ => Task.CompletedTask\n };\n await task;\n }\n }\n\n private EscalationMessage BuildEscalationMessage(Vulnerability vuln, EscalationLevel level)\n {\n var hoursRemaining = vuln.SlaDueDate.HasValue\n ? (vuln.SlaDueDate.Value - DateTimeOffset.UtcNow).TotalHours\n : 0;\n\n var body = $\"\"\"\n Security Vulnerability SLA Alert\n\n Level: {level.Name}\n Vulnerability: {vuln.Title}\n ID: {vuln.Id}\n Severity: {vuln.Severity.ToString().ToUpperInvariant()}\n Hours Until SLA Breach: {Math.Max(0, hoursRemaining):F1}\n SLA Due Date: {vuln.SlaDueDate?.ToString(\"yyyy-MM-dd HH:mm\") ?? \"N/A\"} UTC\n\n Details:\n - Source: {vuln.Source}\n - CVE: {vuln.CveId ?? \"N/A\"}\n - Assigned To: {vuln.AssignedTo ?? \"Unassigned\"}\n - Ticket: {vuln.TicketId ?? \"No ticket\"}\n\n Action Required: Please remediate or escalate immediately.\n \"\"\";\n\n return new EscalationMessage(\n Subject: $\"[{level.Name}] Security Vulnerability SLA Alert - {vuln.Id}\",\n Body: body);\n }\n\n private List\u003cstring> ResolveRecipients(List\u003cstring> roles, Vulnerability vuln)\n {\n var recipients = new HashSet\u003cstring>();\n\n foreach (var role in roles)\n {\n if (role == \"assignee\" && vuln.AssignedTo is not null)\n {\n recipients.Add(vuln.AssignedTo);\n }\n else if (config.RoleMapping.TryGetValue(role, out var emails))\n {\n foreach (var email in emails)\n recipients.Add(email);\n }\n }\n\n return [.. recipients];\n }\n\n private async Task SendEmailAsync(\n List\u003cstring> recipients,\n EscalationMessage message,\n CancellationToken ct)\n {\n if (recipients.Count == 0) return;\n\n try\n {\n var mail = new MailMessage\n {\n From = new MailAddress(config.SmtpFromAddress),\n Subject = message.Subject,\n Body = message.Body\n };\n\n foreach (var recipient in recipients)\n mail.To.Add(recipient);\n\n await smtpClient.SendMailAsync(mail, ct);\n }\n catch (Exception ex)\n {\n logger.LogError(ex, \"Failed to send email escalation\");\n }\n }\n\n private async Task SendSlackAsync(EscalationMessage message, CancellationToken ct)\n {\n if (string.IsNullOrEmpty(config.SlackWebhookUrl)) return;\n\n try\n {\n var payload = new\n {\n attachments = new[]\n {\n new\n {\n color = LevelColors.GetValueOrDefault(message.Subject.Split(']')[0].TrimStart('['), \"#808080\"),\n title = message.Subject,\n text = message.Body\n }\n }\n };\n\n var response = await httpClient.PostAsJsonAsync(config.SlackWebhookUrl, payload, ct);\n response.EnsureSuccessStatusCode();\n }\n catch (Exception ex)\n {\n logger.LogError(ex, \"Failed to send Slack escalation\");\n }\n }\n\n private async Task SendPagerDutyAsync(\n Vulnerability vuln,\n EscalationLevel level,\n EscalationMessage message,\n CancellationToken ct)\n {\n if (string.IsNullOrEmpty(config.PagerDutyRoutingKey)) return;\n\n try\n {\n var payload = new\n {\n routing_key = config.PagerDutyRoutingKey,\n event_action = \"trigger\",\n dedup_key = vuln.Id,\n payload = new\n {\n summary = message.Subject,\n severity = level.Name is \"Critical\" or \"Breach\" ? \"critical\" : \"warning\",\n source = \"vulnerability-management\",\n custom_details = new\n {\n vulnerability_id = vuln.Id,\n severity = vuln.Severity.ToString(),\n sla_due = vuln.SlaDueDate?.ToString(\"o\")\n }\n }\n };\n\n var response = await httpClient.PostAsJsonAsync(\n \"https://events.pagerduty.com/v2/enqueue\",\n payload,\n ct);\n response.EnsureSuccessStatusCode();\n }\n catch (Exception ex)\n {\n logger.LogError(ex, \"Failed to send PagerDuty escalation\");\n }\n }\n}\n\npublic sealed record EscalationMessage(string Subject, string Body);\n\npublic sealed record SlaMonitorConfig\n{\n public string SmtpFromAddress { get; init; } = \"[email protected]\";\n public string? SlackWebhookUrl { get; init; }\n public string? PagerDutyRoutingKey { get; init; }\n public Dictionary\u003cstring, List\u003cstring>> RoleMapping { get; init; } = [];\n public List\u003cEscalationLevel>? EscalationLevels { get; init; }\n}\n```\n\n## Metrics and Reporting\n\n### Vulnerability Metrics Dashboard\n\n```csharp\n/// \u003csummary>\n/// Vulnerability management metrics and KPIs.\n/// \u003c/summary>\n\npublic enum BacklogTrend { Increasing, Stable, Decreasing }\n\n/// \u003csummary>\n/// Vulnerability management KPIs.\n/// \u003c/summary>\npublic sealed record VulnerabilityMetrics\n{\n // Volume metrics\n public required int TotalOpen { get; init; }\n public required int TotalClosedPeriod { get; init; }\n public required int NewThisPeriod { get; init; }\n\n // Severity breakdown\n public required Dictionary\u003cSeverity, int> OpenBySeverity { get; init; }\n\n // SLA metrics\n public required double SlaComplianceRate { get; init; }\n public required double AverageMttrDays { get; init; } // Mean Time To Remediate\n public required Dictionary\u003cSeverity, double> MttrBySeverity { get; init; }\n\n // Trend metrics\n public required double BurnDownRate { get; init; } // Vulnerabilities closed per day\n public required double DiscoveryRate { get; init; } // Vulnerabilities found per day\n public required BacklogTrend BacklogTrend { get; init; }\n\n // Quality metrics\n public required double FalsePositiveRate { get; init; }\n public required double ReopenRate { get; init; }\n\n // Age metrics\n public required double AverageAgeDays { get; init; }\n public required int OldestOpenDays { get; init; }\n public required Dictionary\u003cstring, int> AgeDistribution { get; init; }\n}\n\n/// \u003csummary>\n/// Interface for vulnerability storage.\n/// \u003c/summary>\npublic interface IVulnerabilityStore\n{\n Task\u003cIReadOnlyList\u003cVulnerability>> GetAllVulnerabilitiesAsync(CancellationToken ct = default);\n}\n\n/// \u003csummary>\n/// Calculate vulnerability management metrics.\n/// \u003c/summary>\npublic sealed class MetricsCalculator(IVulnerabilityStore store)\n{\n /// \u003csummary>\n /// Calculate all metrics for the given period.\n /// \u003c/summary>\n public async Task\u003cVulnerabilityMetrics> CalculateMetricsAsync(\n int periodDays = 30,\n CancellationToken ct = default)\n {\n var now = DateTimeOffset.UtcNow;\n var periodStart = now.AddDays(-periodDays);\n\n var allVulns = await store.GetAllVulnerabilitiesAsync(ct);\n\n var openVulns = allVulns\n .Where(v => v.Status is not VulnerabilityStatus.Verified\n and not VulnerabilityStatus.FalsePositive)\n .ToList();\n\n var closedThisPeriod = allVulns\n .Where(v => v.Status == VulnerabilityStatus.Verified\n && v.DiscoveredAt >= periodStart)\n .ToList();\n\n var newThisPeriod = allVulns\n .Where(v => v.DiscoveredAt >= periodStart)\n .ToList();\n\n // Calculate MTTR\n var mttrData = CalculateMttr(allVulns);\n\n // Calculate SLA compliance\n var slaCompliance = CalculateSlaCompliance(openVulns, now);\n\n // Calculate age distribution\n var ageDist = CalculateAgeDistribution(openVulns, now);\n\n // Calculate trend\n var burnDown = periodDays > 0 ? (double)closedThisPeriod.Count / periodDays : 0;\n var discovery = periodDays > 0 ? (double)newThisPeriod.Count / periodDays : 0;\n\n var trend = (discovery, burnDown) switch\n {\n _ when discovery > burnDown * 1.1 => BacklogTrend.Increasing,\n _ when burnDown > discovery * 1.1 => BacklogTrend.Decreasing,\n _ => BacklogTrend.Stable\n };\n\n return new VulnerabilityMetrics\n {\n TotalOpen = openVulns.Count,\n TotalClosedPeriod = closedThisPeriod.Count,\n NewThisPeriod = newThisPeriod.Count,\n OpenBySeverity = CountBySeverity(openVulns),\n SlaComplianceRate = slaCompliance,\n AverageMttrDays = mttrData.Average,\n MttrBySeverity = mttrData.BySeverity,\n BurnDownRate = burnDown,\n DiscoveryRate = discovery,\n BacklogTrend = trend,\n FalsePositiveRate = CalculateFpRate(allVulns),\n ReopenRate = CalculateReopenRate(allVulns),\n AverageAgeDays = CalculateAverageAge(openVulns, now),\n OldestOpenDays = GetOldestAge(openVulns, now),\n AgeDistribution = ageDist\n };\n }\n\n private static Dictionary\u003cSeverity, int> CountBySeverity(IEnumerable\u003cVulnerability> vulns) =>\n vulns.GroupBy(v => v.Severity)\n .ToDictionary(g => g.Key, g => g.Count());\n\n private static (double Average, Dictionary\u003cSeverity, double> BySeverity) CalculateMttr(\n IReadOnlyList\u003cVulnerability> vulns)\n {\n var closedVulns = vulns.Where(v => v.Status == VulnerabilityStatus.Verified).ToList();\n\n if (closedVulns.Count == 0)\n return (0, []);\n\n var mttrBySeverity = new Dictionary\u003cSeverity, List\u003cdouble>>();\n var allTimes = new List\u003cdouble>();\n\n foreach (var v in closedVulns)\n {\n // Placeholder: in production, calculate from actual remediation date\n const double remediationTime = 7;\n allTimes.Add(remediationTime);\n\n if (!mttrBySeverity.TryGetValue(v.Severity, out var list))\n {\n list = [];\n mttrBySeverity[v.Severity] = list;\n }\n list.Add(remediationTime);\n }\n\n return (\n Average: allTimes.Average(),\n BySeverity: mttrBySeverity.ToDictionary(\n kvp => kvp.Key,\n kvp => kvp.Value.Average())\n );\n }\n\n private static double CalculateSlaCompliance(\n IEnumerable\u003cVulnerability> vulns,\n DateTimeOffset now)\n {\n var applicable = vulns.Where(v => v.SlaDueDate.HasValue).ToList();\n\n if (applicable.Count == 0)\n return 100.0;\n\n var compliant = applicable.Count(v => v.SlaDueDate > now);\n return (double)compliant / applicable.Count * 100;\n }\n\n private static Dictionary\u003cstring, int> CalculateAgeDistribution(\n IEnumerable\u003cVulnerability> vulns,\n DateTimeOffset now)\n {\n var buckets = new Dictionary\u003cstring, int>\n {\n [\"0-7 days\"] = 0,\n [\"8-30 days\"] = 0,\n [\"31-90 days\"] = 0,\n [\"91-180 days\"] = 0,\n [\"180+ days\"] = 0\n };\n\n foreach (var v in vulns)\n {\n var age = (now - v.DiscoveredAt).Days;\n var bucket = age switch\n {\n \u003c= 7 => \"0-7 days\",\n \u003c= 30 => \"8-30 days\",\n \u003c= 90 => \"31-90 days\",\n \u003c= 180 => \"91-180 days\",\n _ => \"180+ days\"\n };\n buckets[bucket]++;\n }\n\n return buckets;\n }\n\n private static double CalculateAverageAge(\n IEnumerable\u003cVulnerability> vulns,\n DateTimeOffset now)\n {\n var ages = vulns.Select(v => (now - v.DiscoveredAt).Days).ToList();\n return ages.Count > 0 ? ages.Average() : 0;\n }\n\n private static int GetOldestAge(IEnumerable\u003cVulnerability> vulns, DateTimeOffset now) =>\n vulns.Select(v => (now - v.DiscoveredAt).Days).DefaultIfEmpty(0).Max();\n\n private static double CalculateFpRate(IReadOnlyList\u003cVulnerability> vulns)\n {\n if (vulns.Count == 0) return 0;\n\n var fpCount = vulns.Count(v => v.Status == VulnerabilityStatus.FalsePositive);\n return (double)fpCount / vulns.Count * 100;\n }\n\n private static double CalculateReopenRate(IReadOnlyList\u003cVulnerability> vulns)\n {\n // Would need to track state transitions - placeholder\n return 0;\n }\n}\n```\n\n## Remediation Verification\n\n### Automated Verification Pipeline\n\n```yaml\n# .github/workflows/verify-remediation.yml\nname: Verify Vulnerability Remediation\n\non:\n pull_request:\n types: [opened, synchronize]\n\njobs:\n verify-remediation:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n\n - name: Check for vulnerability tickets\n id: check-tickets\n run: |\n # Extract ticket IDs from PR description and commits\n PR_BODY=\"${{ github.event.pull_request.body }}\"\n COMMITS=$(git log --oneline origin/${{ github.base_ref }}..HEAD)\n\n # Pattern: SEC-123, VULN-456, etc.\n TICKETS=$(echo \"$PR_BODY $COMMITS\" | grep -oE '(SEC|VULN|SECURITY)-[0-9]+' | sort -u)\n\n if [ -n \"$TICKETS\" ]; then\n echo \"tickets=$TICKETS\" >> $GITHUB_OUTPUT\n echo \"has_tickets=true\" >> $GITHUB_OUTPUT\n else\n echo \"has_tickets=false\" >> $GITHUB_OUTPUT\n fi\n\n - name: Run targeted security scan\n if: steps.check-tickets.outputs.has_tickets == 'true'\n uses: returntocorp/semgrep-action@v1\n with:\n config: >-\n p/security-audit\n p/owasp-top-ten\n\n - name: Verify fix effectiveness\n if: steps.check-tickets.outputs.has_tickets == 'true'\n run: |\n # Get changed files\n CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD)\n\n # Run SAST on changed files only\n echo \"$CHANGED_FILES\" | xargs semgrep --config=auto --json > scan-results.json\n\n # Check if original vulnerability patterns are still present\n python scripts/verify-remediation.py \\\n --tickets \"${{ steps.check-tickets.outputs.tickets }}\" \\\n --scan-results scan-results.json\n\n - name: Update ticket status\n if: success() && steps.check-tickets.outputs.has_tickets == 'true'\n env:\n JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }}\n run: |\n for ticket in ${{ steps.check-tickets.outputs.tickets }}; do\n python scripts/update-ticket.py \\\n --ticket \"$ticket\" \\\n --status \"remediation-verified\" \\\n --pr \"${{ github.event.pull_request.html_url }}\" \\\n --commit \"${{ github.sha }}\"\n done\n```\n\n## Related Documentation\n\n- **Parent Skill**: See `../SKILL.md` for DevSecOps overview\n- **SAST Tools**: See `sast-tools.md` for detailed tool configurations\n- **Security Gates**: See `security-gates.md` for gate implementation\n\n---\n\n**Last Updated:** 2025-12-26\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":47816,"content_sha256":"97d85a19f4be5e40f2844a8ca1e1555d5f136214fc5d466f3e9191a1c5c2e31d"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"DevSecOps Practices","type":"text"}]},{"type":"paragraph","content":[{"text":"Comprehensive guidance for integrating security throughout the software development lifecycle using DevSecOps principles.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Implementing shift-left security practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setting up SAST tools (Semgrep, CodeQL, SonarQube)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configuring DAST scanning (OWASP ZAP, Burp Suite)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integrating security gates in CI/CD pipelines","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Building vulnerability management workflows","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Establishing security champions programs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Creating secure SDLC processes","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"DevSecOps Maturity Levels","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Level","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Characteristics","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key Practices","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Level 1: Initial","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Manual security reviews, ad-hoc testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Basic vulnerability scanning, security training","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Level 2: Managed","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Automated scanning in CI/CD, defined processes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SAST integration, security gates","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Level 3: Defined","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security embedded in all phases, metrics tracked","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DAST/IAST, threat modeling, SLAs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Level 4: Measured","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Continuous monitoring, risk-based decisions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full automation, security dashboards","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Level 5: Optimizing","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Predictive security, continuous improvement","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AI-assisted, chaos engineering","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Security Testing Types","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What It Finds","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tools","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SAST","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Build time","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Code vulnerabilities, patterns","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Semgrep, CodeQL, SonarQube","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SCA","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Build time","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dependency vulnerabilities","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Snyk, Dependabot, npm audit","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DAST","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runtime","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Running application vulns","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OWASP ZAP, Burp Suite","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"IAST","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runtime","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Combined SAST+DAST","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Contrast, Seeker","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Secrets","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Commit time","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hardcoded credentials","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Gitleaks, truffleHog","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Security Gates by Pipeline Stage","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n│ Commit │───►│ Build │───►│ Test │───►│ Deploy │───►│Production│\n└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘\n │ │ │ │ │\n ▼ ▼ ▼ ▼ ▼\n┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐\n│Secrets │ │SAST │ │DAST │ │Container│ │Runtime │\n│Scanning │ │SCA │ │Pen Test │ │Scanning │ │Security │\n│Pre-commit │License │ │IAST │ │Config │ │Monitoring\n└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"SAST (Static Application Security Testing)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Semgrep Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/semgrep.yml\nname: Semgrep\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n semgrep:\n runs-on: ubuntu-latest\n container:\n image: semgrep/semgrep\n\n steps:\n - uses: actions/checkout@v5\n\n - name: Run Semgrep\n run: semgrep scan --config auto --sarif --output semgrep.sarif\n\n - name: Upload SARIF\n uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: semgrep.sarif","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Semgrep Rules Configuration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .semgrep.yml\nrules:\n # SQL Injection\n - id: sql-injection\n patterns:\n - pattern-either:\n - pattern: cursor.execute($QUERY % ...)\n - pattern: cursor.execute($QUERY.format(...))\n - pattern: cursor.execute(f\"...\")\n message: \"Potential SQL injection. Use parameterized queries.\"\n severity: ERROR\n languages: [python]\n\n # Hardcoded Secrets\n - id: hardcoded-password\n pattern-regex: '(?i)(password|passwd|pwd)\\s*=\\s*[\"\\'][^\"\\']{8,}[\"\\']'\n message: \"Hardcoded password detected\"\n severity: ERROR\n languages: [python, javascript, typescript]\n\n # Insecure Crypto\n - id: insecure-hash\n patterns:\n - pattern-either:\n - pattern: hashlib.md5(...)\n - pattern: hashlib.sha1(...)\n message: \"Use SHA-256 or stronger for cryptographic purposes\"\n severity: WARNING\n languages: [python]","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"CodeQL Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/codeql.yml\nname: CodeQL Analysis\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n schedule:\n - cron: '0 6 * * 1' # Weekly\n\njobs:\n analyze:\n runs-on: ubuntu-latest\n permissions:\n security-events: write\n\n strategy:\n matrix:\n language: [javascript, python]\n\n steps:\n - uses: actions/checkout@v5\n\n - name: Initialize CodeQL\n uses: github/codeql-action/init@v3\n with:\n languages: ${{ matrix.language }}\n queries: +security-extended\n\n - name: Build (if needed)\n uses: github/codeql-action/autobuild@v3\n\n - name: Perform Analysis\n uses: github/codeql-action/analyze@v3\n with:\n category: \"/language:${{ matrix.language }}\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"SonarQube Integration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/sonarqube.yml\nname: SonarQube Analysis\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n sonarqube:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0 # Full history for accurate blame\n\n - name: SonarQube Scan\n uses: sonarsource/sonarqube-scan-action@master\n env:\n SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}\n\n - name: Quality Gate Check\n uses: sonarsource/sonarqube-quality-gate-action@master\n timeout-minutes: 5\n env:\n SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"properties"},"content":[{"text":"# sonar-project.properties\nsonar.projectKey=my-project\nsonar.organization=my-org\n\n# Source paths\nsonar.sources=src\nsonar.tests=tests\n\n# Exclusions\nsonar.exclusions=**/node_modules/**,**/*.test.js,**/vendor/**\n\n# Coverage\nsonar.javascript.lcov.reportPaths=coverage/lcov.info\nsonar.python.coverage.reportPaths=coverage.xml\n\n# Security hotspots review\nsonar.security.hotspots.review.priority=HIGH","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"DAST (Dynamic Application Security Testing)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"OWASP ZAP Integration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/zap.yml\nname: OWASP ZAP Scan\non:\n push:\n branches: [main]\n schedule:\n - cron: '0 2 * * 0' # Weekly Sunday 2 AM\n\njobs:\n zap-scan:\n runs-on: ubuntu-latest\n services:\n app:\n image: my-app:latest\n ports:\n - 8080:8080\n\n steps:\n - uses: actions/checkout@v5\n\n - name: ZAP Baseline Scan\n uses: zaproxy/[email protected]\n with:\n target: 'http://localhost:8080'\n rules_file_name: '.zap/rules.tsv'\n\n - name: ZAP Full Scan\n uses: zaproxy/[email protected]\n with:\n target: 'http://localhost:8080'\n cmd_options: '-a -j'\n\n - name: Upload Report\n uses: actions/upload-artifact@v4\n with:\n name: zap-report\n path: report_html.html","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"ZAP Rules Configuration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"tsv"},"content":[{"text":"# .zap/rules.tsv\n# Rule ID Action Description\n10010 IGNORE # Cookie No HttpOnly Flag (handled by framework)\n10011 WARN # Cookie Without Secure Flag\n10015 FAIL # Incomplete or No Cache-control and Pragma\n10016 WARN # Web Browser XSS Protection Not Enabled\n10017 FAIL # Cross-Domain JavaScript Source File Inclusion\n10019 FAIL # Content-Type Header Missing\n10020 FAIL # X-Frame-Options Header Not Set\n10021 FAIL # X-Content-Type-Options Header Missing\n10038 FAIL # Content Security Policy Header Not Set","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"DAST in Docker Compose","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# docker-compose.security.yml\nversion: '3.8'\n\nservices:\n app:\n build: .\n ports:\n - \"8080:8080\"\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8080/health\"]\n interval: 5s\n timeout: 10s\n retries: 5\n\n zap:\n image: ghcr.io/zaproxy/zaproxy:stable\n depends_on:\n app:\n condition: service_healthy\n volumes:\n - ./zap-reports:/zap/wrk\n command: >\n zap-full-scan.py\n -t http://app:8080\n -r zap-report.html\n -J zap-report.json\n -x zap-report.xml","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Gates","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Gate Configuration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"csharp"},"content":[{"text":"using System.Text.Json;\nusing System.Text.Json.Serialization;\n\n/// \u003csummary>\n/// Security gate enforcement for CI/CD pipelines.\n/// \u003c/summary>\npublic enum Severity { Critical, High, Medium, Low, Info }\n\npublic enum GateDecision { Pass, Warn, Fail }\n\n/// \u003csummary>\n/// Configuration for security gate thresholds.\n/// \u003c/summary>\npublic sealed record SecurityGateConfig\n{\n // SAST thresholds\n public int SastCriticalMax { get; init; } = 0;\n public int SastHighMax { get; init; } = 0;\n public int SastMediumMax { get; init; } = 5;\n\n // SCA thresholds\n public int ScaCriticalMax { get; init; } = 0;\n public int ScaHighMax { get; init; } = 2;\n public double ScaCvssThreshold { get; init; } = 7.0;\n\n // DAST thresholds\n public int DastCriticalMax { get; init; } = 0;\n public int DastHighMax { get; init; } = 1;\n\n // Secrets\n public int SecretsAllowed { get; init; } = 0;\n\n // License restrictions\n public IReadOnlyList\u003cstring> ForbiddenLicenses { get; init; } = [\"GPL-3.0\", \"AGPL-3.0\"];\n}\n\n/// \u003csummary>\n/// Aggregated results from security scans.\n/// \u003c/summary>\npublic sealed record ScanResults(\n Dictionary\u003cstring, int> SastFindings,\n Dictionary\u003cstring, int> ScaFindings,\n Dictionary\u003cstring, int> DastFindings,\n int SecretsFound,\n IReadOnlyList\u003cstring> Licenses);\n\n/// \u003csummary>\n/// Evaluates security gates for CI/CD pipelines.\n/// \u003c/summary>\npublic static class SecurityGateEvaluator\n{\n public static (GateDecision Decision, List\u003cstring> Reasons) Evaluate(\n SecurityGateConfig config,\n ScanResults results)\n {\n var reasons = new List\u003cstring>();\n var decision = GateDecision.Pass;\n\n // Check SAST\n if (results.SastFindings.GetValueOrDefault(\"critical\", 0) > config.SastCriticalMax)\n {\n decision = GateDecision.Fail;\n reasons.Add($\"SAST: {results.SastFindings[\"critical\"]} critical findings (max: {config.SastCriticalMax})\");\n }\n\n if (results.SastFindings.GetValueOrDefault(\"high\", 0) > config.SastHighMax)\n {\n decision = GateDecision.Fail;\n reasons.Add($\"SAST: {results.SastFindings[\"high\"]} high findings (max: {config.SastHighMax})\");\n }\n\n // Check SCA\n if (results.ScaFindings.GetValueOrDefault(\"critical\", 0) > config.ScaCriticalMax)\n {\n decision = GateDecision.Fail;\n reasons.Add($\"SCA: {results.ScaFindings[\"critical\"]} critical vulnerabilities (max: {config.ScaCriticalMax})\");\n }\n\n // Check secrets\n if (results.SecretsFound > config.SecretsAllowed)\n {\n decision = GateDecision.Fail;\n reasons.Add($\"Secrets: {results.SecretsFound} secrets detected\");\n }\n\n // Check licenses\n foreach (var license in results.Licenses)\n {\n if (config.ForbiddenLicenses.Contains(license))\n {\n decision = GateDecision.Fail;\n reasons.Add($\"License: Forbidden license {license} detected\");\n }\n }\n\n // Warnings (don't fail but report)\n if (results.SastFindings.GetValueOrDefault(\"medium\", 0) > config.SastMediumMax)\n {\n if (decision == GateDecision.Pass)\n decision = GateDecision.Warn;\n reasons.Add($\"SAST: {results.SastFindings[\"medium\"]} medium findings (threshold: {config.SastMediumMax})\");\n }\n\n return (decision, reasons);\n }\n}\n\n// Usage in CI (console app entry point)\npublic static class SecurityGateCli\n{\n public static async Task\u003cint> Main(string[] args)\n {\n var jsonPath = args.FirstOrDefault() ?? \"scan-results.json\";\n var json = await File.ReadAllTextAsync(jsonPath);\n var rawResults = JsonSerializer.Deserialize\u003cRawScanResults>(json)!;\n\n var results = new ScanResults(\n rawResults.Sast ?? new(),\n rawResults.Sca ?? new(),\n rawResults.Dast ?? new(),\n rawResults.Secrets,\n rawResults.Licenses ?? []);\n\n var config = new SecurityGateConfig();\n var (decision, reasons) = SecurityGateEvaluator.Evaluate(config, results);\n\n Console.WriteLine($\"Security Gate: {decision.ToString().ToUpper()}\");\n foreach (var reason in reasons)\n Console.WriteLine($\" - {reason}\");\n\n return decision == GateDecision.Fail ? 1 : 0;\n }\n\n private sealed record RawScanResults(\n [property: JsonPropertyName(\"sast\")] Dictionary\u003cstring, int>? Sast,\n [property: JsonPropertyName(\"sca\")] Dictionary\u003cstring, int>? Sca,\n [property: JsonPropertyName(\"dast\")] Dictionary\u003cstring, int>? Dast,\n [property: JsonPropertyName(\"secrets\")] int Secrets,\n [property: JsonPropertyName(\"licenses\")] List\u003cstring>? Licenses);\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"GitHub Actions Security Gate","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/security-gate.yml\nname: Security Gate\non:\n pull_request:\n branches: [main]\n\njobs:\n security-gate:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n\n # Run all security scans\n - name: SAST - Semgrep\n uses: semgrep/semgrep-action@v1\n with:\n config: auto\n generateSarif: true\n\n - name: SCA - npm audit\n run: npm audit --json > npm-audit.json || true\n\n - name: Secrets - Gitleaks\n uses: gitleaks/gitleaks-action@v2\n env:\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n # Aggregate and evaluate\n - name: Evaluate Security Gate\n run: |\n python scripts/security_gate.py \\\n --sast-results semgrep.sarif \\\n --sca-results npm-audit.json \\\n --secrets-results gitleaks.json\n\n - name: Comment on PR\n if: always()\n uses: actions/github-script@v7\n with:\n script: |\n const fs = require('fs');\n const report = fs.readFileSync('security-report.md', 'utf8');\n github.rest.issues.createComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n body: report\n });","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Secrets Scanning","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pre-commit Hook with Gitleaks","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .pre-commit-config.yaml\nrepos:\n - repo: https://github.com/gitleaks/gitleaks\n rev: v8.18.0\n hooks:\n - id: gitleaks\n\n - repo: https://github.com/Yelp/detect-secrets\n rev: v1.4.0\n hooks:\n - id: detect-secrets\n args: ['--baseline', '.secrets.baseline']","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Gitleaks Configuration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"toml"},"content":[{"text":"# .gitleaks.toml\ntitle = \"Gitleaks Configuration\"\n\n[extend]\nuseDefault = true\n\n[[rules]]\nid = \"custom-api-key\"\ndescription = \"Custom API Key Pattern\"\nregex = '''(?i)api[_-]?key\\s*[:=]\\s*['\"]?[a-zA-Z0-9]{32,}['\"]?'''\ntags = [\"key\", \"api\"]\n\n[[rules]]\nid = \"custom-password\"\ndescription = \"Hardcoded Password\"\nregex = '''(?i)(password|passwd|pwd)\\s*[:=]\\s*['\"][^'\"]{8,}['\"]'''\ntags = [\"password\"]\n\n[allowlist]\ndescription = \"Global allowlist\"\npaths = [\n '''\\.gitleaks\\.toml

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 | |-------|-----------------|-…

'',\n '''\\.secrets\\.baseline

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 | |-------|-----------------|-…

'',\n '''test/.*\\.py

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 | |-------|-----------------|-…

'',\n '''.*_test\\.go

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 | |-------|-----------------|-…

'',\n]","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"GitHub Secret Scanning","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/secret-scanning.yml\nname: Secret Scanning\non:\n push:\n branches: [main]\n pull_request:\n\njobs:\n gitleaks:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n\n - name: Gitleaks\n uses: gitleaks/gitleaks-action@v2\n env:\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n trufflehog:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v5\n with:\n fetch-depth: 0\n\n - name: TruffleHog\n uses: trufflesecurity/trufflehog@main\n with:\n extra_args: --only-verified","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Vulnerability Management Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Vulnerability Tracking","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"csharp"},"content":[{"text":"/// \u003csummary>\n/// Vulnerability management workflow automation.\n/// \u003c/summary>\npublic enum VulnStatus\n{\n New,\n Triaged,\n InProgress,\n Resolved,\n AcceptedRisk,\n FalsePositive\n}\n\npublic enum VulnSeverity { Critical = 1, High = 2, Medium = 3, Low = 4 }\n\n/// \u003csummary>\n/// Tracked vulnerability with lifecycle management.\n/// \u003c/summary>\npublic sealed class Vulnerability\n{\n public required string Id { get; init; }\n public string? CveId { get; init; }\n public required string Title { get; init; }\n public required string Description { get; init; }\n public required VulnSeverity Severity { get; init; }\n public required double CvssScore { get; init; }\n public required string AffectedComponent { get; init; }\n public required string AffectedVersion { get; init; }\n public string? FixedVersion { get; init; }\n\n // Tracking\n public VulnStatus Status { get; set; } = VulnStatus.New;\n public string? Assignee { get; set; }\n public DateTime DiscoveredDate { get; init; } = DateTime.UtcNow;\n public DateTime? DueDate { get; set; }\n public DateTime? ResolvedDate { get; set; }\n public List\u003cstring> Notes { get; } = [];\n}\n\n/// \u003csummary>\n/// Manages vulnerability lifecycle with SLA tracking.\n/// \u003c/summary>\npublic sealed class VulnerabilityManager\n{\n private static readonly IReadOnlyDictionary\u003cVulnSeverity, int> SlaDays = new Dictionary\u003cVulnSeverity, int>\n {\n [VulnSeverity.Critical] = 7,\n [VulnSeverity.High] = 30,\n [VulnSeverity.Medium] = 90,\n [VulnSeverity.Low] = 180,\n };\n\n private readonly Dictionary\u003cstring, Vulnerability> _vulnerabilities = new();\n\n public void AddVulnerability(Vulnerability vuln)\n {\n // Auto-set due date based on SLA\n vuln.DueDate ??= vuln.DiscoveredDate.AddDays(\n SlaDays.GetValueOrDefault(vuln.Severity, 90));\n\n _vulnerabilities[vuln.Id] = vuln;\n }\n\n public void Triage(string vulnId, string assignee, VulnStatus status = VulnStatus.Triaged)\n {\n if (_vulnerabilities.TryGetValue(vulnId, out var vuln))\n {\n vuln.Status = status;\n vuln.Assignee = assignee;\n vuln.Notes.Add($\"{DateTime.UtcNow:O}: Triaged to {assignee}\");\n }\n }\n\n public void Resolve(string vulnId, string resolution, VulnStatus status = VulnStatus.Resolved)\n {\n if (_vulnerabilities.TryGetValue(vulnId, out var vuln))\n {\n vuln.Status = status;\n vuln.ResolvedDate = DateTime.UtcNow;\n vuln.Notes.Add($\"{DateTime.UtcNow:O}: Resolved - {resolution}\");\n }\n }\n\n public void AcceptRisk(string vulnId, string justification, string approver)\n {\n if (_vulnerabilities.TryGetValue(vulnId, out var vuln))\n {\n vuln.Status = VulnStatus.AcceptedRisk;\n vuln.Notes.Add($\"{DateTime.UtcNow:O}: Risk accepted by {approver} - {justification}\");\n }\n }\n\n public IEnumerable\u003cVulnerability> GetOverdue()\n {\n var now = DateTime.UtcNow;\n return _vulnerabilities.Values.Where(v =>\n v.Status is not (VulnStatus.Resolved or VulnStatus.AcceptedRisk or VulnStatus.FalsePositive) &&\n v.DueDate.HasValue &&\n v.DueDate.Value \u003c now);\n }\n\n public VulnerabilityMetrics GetMetrics()\n {\n var vulns = _vulnerabilities.Values.ToList();\n\n return new VulnerabilityMetrics(\n Total: vulns.Count,\n Open: vulns.Count(v => v.Status is VulnStatus.New or VulnStatus.Triaged or VulnStatus.InProgress),\n Resolved: vulns.Count(v => v.Status == VulnStatus.Resolved),\n Overdue: GetOverdue().Count(),\n BySeverity: Enum.GetValues\u003cVulnSeverity>().ToDictionary(\n sev => sev.ToString(),\n sev => vulns.Count(v => v.Severity == sev)),\n MttrDays: CalculateMttr(vulns));\n }\n\n private static double CalculateMttr(List\u003cVulnerability> vulns)\n {\n var resolved = vulns\n .Where(v => v.Status == VulnStatus.Resolved && v.ResolvedDate.HasValue)\n .ToList();\n\n if (resolved.Count == 0) return 0.0;\n\n var totalDays = resolved.Sum(v => (v.ResolvedDate!.Value - v.DiscoveredDate).TotalDays);\n return totalDays / resolved.Count;\n }\n}\n\npublic sealed record VulnerabilityMetrics(\n int Total,\n int Open,\n int Resolved,\n int Overdue,\n Dictionary\u003cstring, int> BySeverity,\n double MttrDays);","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Champions Program","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Program Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"# Security Champions Program\n\n## Roles and Responsibilities\n\n### Security Champion\n- Embedded security advocate within development team\n- First point of contact for security questions\n- Participates in security training and shares knowledge\n- Reviews security-critical code changes\n- Triages security findings for their team\n\n### Time Commitment\n- 10-20% of work time on security activities\n- Weekly security standup (30 min)\n- Monthly security training (2 hours)\n- Quarterly security deep-dive (4 hours)\n\n## Selection Criteria\n- 1+ year on the team\n- Interest in security\n- Good communication skills\n- Technical credibility with peers\n\n## Training Path\n1. **Month 1**: Security fundamentals\n - OWASP Top 10\n - Secure coding basics\n - Company security policies\n\n2. **Month 2**: Tools and processes\n - SAST/DAST tool usage\n - Security gate process\n - Vulnerability management\n\n3. **Month 3**: Advanced topics\n - Threat modeling\n - Security architecture review\n - Incident response basics\n\n## Metrics\n- Vulnerabilities found by champion reviews\n- Security training completion rate\n- Time to remediate findings\n- Security culture survey scores","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Checklist","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pre-Development","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Threat model completed for new features","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Security requirements documented","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Secure design patterns identified","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Security champions assigned","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"During Development","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Pre-commit hooks enabled (secrets, linting)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"SAST integrated in IDE","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Secure coding guidelines followed","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Security-sensitive code reviewed by champion","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pre-Deployment","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"All security gates passed","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"SAST findings addressed","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"SCA vulnerabilities resolved or accepted","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"DAST scan completed","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Security review approved","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Post-Deployment","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Runtime security monitoring enabled","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Vulnerability scanning scheduled","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Incident response plan updated","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Security metrics collected","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SAST Tools","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/sast-tools.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for detailed tool configurations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security Gates","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/security-gates.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for gate implementation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Vulnerability Workflow","type":"text","marks":[{"type":"strong"}]},{"text":": See ","type":"text"},{"text":"references/vulnerability-workflow.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete workflow","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Related Skills","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"secure-coding","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Secure development practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"supply-chain-security","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Dependency and SBOM management","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"threat-modeling","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Threat identification and mitigation","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Last Updated:","type":"text","marks":[{"type":"strong"}]},{"text":" 2025-12-26","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"devsecops-practices","author":"@skillopedia","source":{"stars":76,"repo_name":"claude-code-plugins","origin_url":"https://github.com/melodic-software/claude-code-plugins/blob/HEAD/plugins/security/skills/devsecops-practices/SKILL.md","repo_owner":"melodic-software","body_sha256":"a4f5fa4c882e416b33413ad9505c138842da33a315686299c4b25bee05a594e9","cluster_key":"b774c3064fc1b904d33f0abba3684ccae783894ffe820da54ca079f65dc4692c","clean_bundle":{"format":"clean-skill-bundle-v1","source":"melodic-software/claude-code-plugins/plugins/security/skills/devsecops-practices/SKILL.md","attachments":[{"id":"80a14988-e2bd-5dbe-9a8e-dcc25d41a178","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/80a14988-e2bd-5dbe-9a8e-dcc25d41a178/attachment.md","path":"references/sast-tools.md","size":18856,"sha256":"2924357993684e1037a55e4cf25342390a9600ae91d760e16fcf900ef866177b","contentType":"text/markdown; charset=utf-8"},{"id":"d52cf2ad-c0d5-5d48-b8ba-633ec33cc7f8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d52cf2ad-c0d5-5d48-b8ba-633ec33cc7f8/attachment.md","path":"references/security-gates.md","size":24280,"sha256":"1297594690cf81bebb8faf00ebdf169072ebac1b9992f0107e3133e6bb3a10c0","contentType":"text/markdown; charset=utf-8"},{"id":"9af877cf-71ca-57cb-b85e-fdb7386ffce2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9af877cf-71ca-57cb-b85e-fdb7386ffce2/attachment.md","path":"references/vulnerability-workflow.md","size":47816,"sha256":"97d85a19f4be5e40f2844a8ca1e1555d5f136214fc5d466f3e9191a1c5c2e31d","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"9d6615c7f088cb260312d0718f12f40112c63f640af7c046ea4f165b6ae4c393","attachment_count":3,"text_attachments":3,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"plugins/security/skills/devsecops-practices/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"security","import_tag":"clean-skills-v1","description":"DevSecOps methodology guidance covering shift-left security, SAST/DAST/IAST integration, security gates in CI/CD pipelines, vulnerability management workflows, and security champions programs.","allowed-tools":"Read, Glob, Grep, Task"}},"renderedAt":1782979326231}

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 | |-------|-----------------|-…