[CRITICAL] fix(security): scope legacy-key cleanup to authenticated user's own keys #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Managed by sh1pt Actions Fleet | |
| # pack: vu1nz-scan@1.0.0 | |
| # install: sh1pt-actions-store | |
| # hash: sha256:a5f27998f1a6ddd9e2ff263724a5d4eb5887a306210d9c00591d9a918a7136ad | |
| name: vu1nz security scan | |
| on: | |
| pull_request: | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| review: | |
| name: Review PR for security vulnerabilities | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install vu1nz | |
| run: pip install --quiet git+https://github.com/profullstack/vu1nz-gh-actions.git | |
| - name: Load env file | |
| env: | |
| ENV_FILE: ${{ secrets.ENV_FILE }} | |
| run: | | |
| echo "$ENV_FILE" > "$RUNNER_TEMP/.env" | |
| echo "Keys in ENV_FILE:" | |
| grep -oP '^[A-Z_]+(?==)' "$RUNNER_TEMP/.env" || echo "(no keys found or different format)" | |
| ANTHROPIC_API_KEY=$(grep -E '^ANTHROPIC_API_KEY=' "$RUNNER_TEMP/.env" | head -1 | sed 's/^ANTHROPIC_API_KEY=//') | |
| if [ -n "$ANTHROPIC_API_KEY" ]; then | |
| echo "::add-mask::$ANTHROPIC_API_KEY" | |
| echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> "$GITHUB_ENV" | |
| echo "ANTHROPIC_API_KEY found and exported" | |
| else | |
| echo "::warning::ANTHROPIC_API_KEY not found in ENV_FILE" | |
| fi | |
| - name: Review PR | |
| id: review | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NO_COLOR: "1" | |
| TERM: dumb | |
| run: | | |
| vu1nz review-pr main \ | |
| ${{ github.repository }} \ | |
| ${{ github.event.pull_request.number }} \ | |
| --token "$GITHUB_TOKEN" \ | |
| --json \ | |
| | tee "$RUNNER_TEMP/vu1nz-review-raw.txt" || true | |
| python3 -c " | |
| import json, re, sys | |
| raw = open('$RUNNER_TEMP/vu1nz-review-raw.txt').read() | |
| raw = re.sub(r'\x1b\[[0-9;]*m', '', raw) | |
| start = raw.find('{') | |
| if start >= 0: | |
| obj, _ = json.JSONDecoder(strict=False).raw_decode(raw, start) | |
| json.dump(obj, sys.stdout) | |
| else: | |
| print('{}') | |
| " > "$RUNNER_TEMP/vu1nz-review.json" | |
| - name: Build PR comment | |
| id: comment | |
| run: | | |
| python3 << 'PYEOF' | |
| import json, os, sys | |
| review_file = os.environ.get("RUNNER_TEMP", "") + "/vu1nz-review.json" | |
| comment_file = os.environ.get("RUNNER_TEMP", "") + "/vu1nz-comment.md" | |
| try: | |
| with open(review_file) as f: | |
| data = json.loads(f.read(), strict=False) | |
| except Exception as e: | |
| print(f"::warning::Could not parse review results: {e}") | |
| with open(comment_file, "w") as f: | |
| f.write("## vu1nz Security Review\n\nCould not parse review results.\n") | |
| sys.exit(0) | |
| findings = data.get("findings", []) | |
| analysis = data.get("analysis", "") | |
| pr = data.get("pr_number", "?") | |
| total = len(findings) | |
| counts = {"critical": 0, "high": 0, "medium": 0, "low": 0} | |
| for finding in findings: | |
| sev = finding.get("severity", "").lower() | |
| if sev in counts: | |
| counts[sev] += 1 | |
| has_hc = counts["critical"] > 0 or counts["high"] > 0 | |
| lines = ["## vu1nz Security Review", ""] | |
| lines.append(f"**{total}** finding(s) in PR #{pr}") | |
| lines.append("") | |
| badge_parts = [] | |
| for sev in ("critical", "high", "medium", "low"): | |
| if counts[sev] > 0: | |
| badge_parts.append(f"**{sev.upper()}**: {counts[sev]}") | |
| if badge_parts: | |
| lines.append(" | ".join(badge_parts)) | |
| lines.append("") | |
| if has_hc: | |
| lines.append("> **High or critical findings - review before merging.**") | |
| lines.append("") | |
| if findings: | |
| lines.append("### Findings") | |
| lines.append("") | |
| lines.append("| Severity | File | Issue | Suggestion |") | |
| lines.append("|----------|------|-------|------------|") | |
| for f in findings: | |
| sev = f.get("severity", "?").upper() | |
| file = f.get("file", "N/A") | |
| issue = f.get("issue", "").replace("\n", " ")[:150] | |
| suggestion = f.get("suggestion", "").replace("\n", " ")[:150] | |
| lines.append(f"| {sev} | `{file}` | {issue} | {suggestion} |") | |
| lines.append("") | |
| else: | |
| lines.append("No security issues found.") | |
| lines.append("") | |
| if analysis: | |
| lines.append("<details><summary>Full AI Analysis</summary>") | |
| lines.append("") | |
| lines.append(analysis) | |
| lines.append("") | |
| lines.append("</details>") | |
| body = "\n".join(lines) | |
| with open(comment_file, "w") as f: | |
| f.write(body) | |
| with open(os.environ.get("GITHUB_OUTPUT", ""), "a") as out: | |
| out.write(f"total={total}\n") | |
| out.write(f"has_high_critical={'true' if has_hc else 'false'}\n") | |
| if has_hc: | |
| print(f"::error::vu1nz found high/critical vulnerabilities in PR code") | |
| sys.exit(1) | |
| print(f"::notice::vu1nz review: {total} finding(s), no high/critical issues") | |
| PYEOF | |
| - name: Write report to job summary | |
| if: always() | |
| run: | | |
| if [ -f "$RUNNER_TEMP/vu1nz-comment.md" ]; then | |
| cat "$RUNNER_TEMP/vu1nz-comment.md" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "## vu1nz Security Review" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Scan completed but could not read results." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Comment on PR | |
| if: always() && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const commentFile = `${process.env.RUNNER_TEMP}/vu1nz-comment.md`; | |
| let body; | |
| try { | |
| body = fs.readFileSync(commentFile, 'utf8'); | |
| } catch { | |
| body = '## vu1nz Security Review\n\nScan completed but could not read results.'; | |
| } | |
| try { | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| }); | |
| const existing = comments.find(c => | |
| c.user.type === 'Bot' && c.body.includes('vu1nz Security Review') | |
| ); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| comment_id: existing.id, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: body, | |
| }); | |
| } | |
| } catch (err) { | |
| if (err.status === 403) { | |
| core.warning(`Cannot post PR comment (read-only token): ${err.message}. Findings are in the job summary.`); | |
| } else { | |
| throw err; | |
| } | |
| } |