Skip to content

Test v0.3.0 summary schema #19

Test v0.3.0 summary schema

Test v0.3.0 summary schema #19

Workflow file for this run

name: Security Scan
on:
push:
branches: ["main"]
pull_request:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
security:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare artifacts directory
run: mkdir -p artifacts/security
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.21"
- name: Install dependencies
run: go mod download
- name: Set up Python for Semgrep
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Run Semgrep (CLI JSON)
run: |
pip install semgrep
semgrep --config p/ci --json > artifacts/security/semgrep-report.json || true
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run Trivy FS Scan
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
format: json
output: artifacts/security/trivy-fs.json
severity: HIGH,CRITICAL
- name: Generate security summary (JSON)
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const dir = 'artifacts/security';
const summaryPath = path.join(dir, 'summary.json');
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
function readJson(file) {
if (!fs.existsSync(file)) return null;
const raw = fs.readFileSync(file, 'utf8');
try { return JSON.parse(raw); }
catch { return null; }
}
const result = {
version: "0.3.0",
status: "UNKNOWN",
blocking_count: 0,
summary: {},
findings: []
};
//
// GITLEAKS
//
const gitleaksPath = path.join(dir, 'gitleaks-report.json');
const gitleaks = readJson(gitleaksPath);
if (gitleaks) {
let count = 0;
if (Array.isArray(gitleaks)) count = gitleaks.length;
else if (Array.isArray(gitleaks.findings)) count = gitleaks.findings.length;
result.summary.gitleaks = { total: count };
}
//
// TRIVY FS
//
const trivyFsPath = path.join(dir, 'trivy-fs.json');
const trivy = readJson(trivyFsPath);
if (trivy?.Results) {
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
for (const r of trivy.Results) {
for (const v of (r.Vulnerabilities || [])) {
const sev = (v.Severity || "").toLowerCase();
if (counts[sev] !== undefined) counts[sev]++;
}
}
result.summary.trivy_fs = counts;
}
//
// SEMGREP
//
const semgrepPath = path.join(dir, 'semgrep-report.json');
const semgrep = readJson(semgrepPath);
if (semgrep) {
let results = [];
// formatos posibles:
// - { "results": [...] }
// - o un array directo
if (Array.isArray(semgrep)) {
results = semgrep;
} else if (Array.isArray(semgrep.results)) {
results = semgrep.results;
}
result.summary.semgrep = { total: results.length };
core.info(`Semgrep findings: ${results.length}`);
} else {
core.info("No semgrep-report.json found, skipping Semgrep summary.");
}
fs.writeFileSync(summaryPath, JSON.stringify(result, null, 2));
core.info("Wrote summary.json");
- name: Post PR Security Summary
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const marker = '<!-- devsecops-kit-security-summary -->';
const summaryPath = 'artifacts/security/summary.json';
let summary = null;
try { summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8')); }
catch { core.warning("No summary.json available."); }
let body = `${marker}\n### 🔐 DevSecOps Kit Security Summary\n\n`;
if (!summary) {
body += "_No summary available._\n";
} else {
const leaks = summary.summary?.gitleaks?.total ?? 0;
const trivy = summary.summary?.trivy_fs ?? {};
const semgrep = summary.summary?.semgrep ?? null;
body += `- **Gitleaks:** ${leaks} leak(s)\n`;
if (Object.keys(trivy).length > 0) {
body += `- **Trivy FS:**\n`;
for (const sev of Object.keys(trivy)) {
body += ` - ${sev.toUpperCase()}: ${trivy[sev]}\n`;
}
}
if (semgrep) {
body += `- **Semgrep:** ${semgrep.total} finding(s)\n`;
}
const blocking =
leaks > 0 ||
(trivy.critical ?? 0) > 0 ||
(trivy.high ?? 0) > 0;
body += `\n${blocking ? '🚨 _Blocking issues detected._' : '✅ _No blocking issues detected._'}\n`;
}
const { owner, repo } = context.repo;
const pr = context.issue.number;
const comments = await github.rest.issues.listComments({ owner, repo, issue_number: pr });
const existing = comments.data.find(c => c.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner, repo,
comment_id: existing.id,
body
});
} else {
await github.rest.issues.createComment({
owner, repo,
issue_number: pr,
body
});
}
- name: Upload security artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: security-reports
path: artifacts/security/