Skip to content

Commit 076cc5a

Browse files
aksOpsclaude
andcommitted
Add dynamic vulnerability badge to README
Badge color changes based on count: green (0), yellow (1-3), red (4+). Updated via gh API in the badge update workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7f9e2ec commit 076cc5a

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<a href="https://github.com/RandomCodeSpace/code-iq/security"><img src="https://img.shields.io/badge/security-audited-purple?style=flat-square&logo=shieldsdotio&logoColor=white" alt="Security"></a>
2020
<a href="https://github.com/RandomCodeSpace/code-iq/security/dependabot"><img src="https://img.shields.io/github/dependabot/RandomCodeSpace/code-iq?style=flat-square&logo=dependabot&logoColor=white&label=Dependabot" alt="Dependabot"></a>
2121
<a href="https://github.com/RandomCodeSpace/code-iq/security/code-scanning"><img src="https://img.shields.io/github/actions/workflow/status/RandomCodeSpace/code-iq/codeql.yml?branch=main&style=flat-square&logo=github&label=CodeQL" alt="CodeQL"></a>
22+
<!-- DYNAMIC:vulnerabilities --><a href="https://github.com/RandomCodeSpace/code-iq/security/dependabot"><img src="https://img.shields.io/badge/vulnerabilities-1-yellow?style=flat-square&logo=hackthebox&logoColor=white" alt="1 Vulnerabilities"></a><!-- /DYNAMIC:vulnerabilities -->
2223
<!-- DYNAMIC:detectors --><a href="https://github.com/RandomCodeSpace/code-iq"><img src="https://img.shields.io/badge/detectors-58-brightgreen?style=flat-square&logo=codefactor&logoColor=white" alt="58 Detectors"></a><!-- /DYNAMIC:detectors -->
2324
<!-- DYNAMIC:languages --><a href="https://github.com/RandomCodeSpace/code-iq"><img src="https://img.shields.io/badge/languages-33-blue?style=flat-square&logo=stackblitz&logoColor=white" alt="33 Languages"></a><!-- /DYNAMIC:languages -->
2425
<!-- DYNAMIC:tests --><a href="https://github.com/RandomCodeSpace/code-iq"><img src="https://img.shields.io/badge/tests-113%20passed-brightgreen?style=flat-square&logo=pytest&logoColor=white" alt="113 passed Tests"></a><!-- /DYNAMIC:tests -->

scripts/update_readme_badges.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from __future__ import annotations
55

6+
import json
67
import re
78
import subprocess
89
import sys
@@ -56,6 +57,17 @@ def count_detectors_and_languages() -> tuple[int, int]:
5657
return int(parts[0]), int(parts[1])
5758

5859

60+
def count_vulnerabilities() -> int:
61+
"""Count open Dependabot alerts via gh CLI (requires repo access)."""
62+
try:
63+
out = _run(["gh", "api", "repos/RandomCodeSpace/code-iq/dependabot/alerts?state=open&per_page=100"])
64+
alerts = json.loads(out)
65+
return len(alerts) if isinstance(alerts, list) else 0
66+
except Exception:
67+
# Fallback: try GITHUB_TOKEN env var or return -1 to skip update
68+
return -1
69+
70+
5971
def fmt(n: int) -> str:
6072
"""Format number with commas for display, URL-encoded."""
6173
return f"{n:,}"
@@ -64,8 +76,11 @@ def fmt(n: int) -> str:
6476
def badge(label: str, value: str, color: str, logo: str) -> str:
6577
"""Generate a shields.io badge HTML snippet."""
6678
val_encoded = quote(value, safe="")
79+
link = "https://github.com/RandomCodeSpace/code-iq"
80+
if label == "vulnerabilities":
81+
link += "/security/dependabot"
6782
return (
68-
f'<a href="https://github.com/RandomCodeSpace/code-iq">'
83+
f'<a href="{link}">'
6984
f'<img src="https://img.shields.io/badge/{label}-{val_encoded}-{color}'
7085
f'?style=flat-square&logo={logo}&logoColor=white" alt="{value} {label.capitalize()}">'
7186
f"</a>"
@@ -86,12 +101,14 @@ def main() -> None:
86101
loc = count_loc()
87102
tests = count_tests()
88103
detectors, languages = count_detectors_and_languages()
104+
vulns = count_vulnerabilities()
89105

90106
print(f" Files: {files}")
91107
print(f" LOC: {loc:,}")
92108
print(f" Tests: {tests}")
93109
print(f" Detectors: {detectors}")
94110
print(f" Languages: {languages}")
111+
print(f" Vulnerabilities: {vulns if vulns >= 0 else 'skipped (no access)'}")
95112

96113
content = README.read_text()
97114
original = content
@@ -116,6 +133,13 @@ def main() -> None:
116133
content, "loc",
117134
badge("LOC", fmt(loc), "informational", "codacy"),
118135
)
136+
if vulns >= 0:
137+
vuln_color = "brightgreen" if vulns == 0 else "yellow" if vulns <= 3 else "red"
138+
vuln_label = "0 - clean" if vulns == 0 else str(vulns)
139+
content = update_badge(
140+
content, "vulnerabilities",
141+
badge("vulnerabilities", vuln_label, vuln_color, "hackthebox"),
142+
)
119143

120144
if content != original:
121145
README.write_text(content)

0 commit comments

Comments
 (0)