Skip to content
All posts

How to Scan a GitHub Repo for Secrets, CVEs, and SAST Issues

6 min readapplication security · SAST · dependency scanning · secrets detection

Your source code is the most concentrated map of your attack surface you will ever have. It holds your dependencies, your authentication logic, your data-handling code, and—too often—a few secrets that were never meant to ship. If you want to scan a GitHub repo for vulnerabilities in a way that actually reduces risk, you need to look at three distinct problems at once: leaked secrets, vulnerable dependencies, and insecure code patterns.

This post walks through those three pillars, why each one matters on its own, and how to prioritize findings so your team fixes what attackers are actually exploiting instead of drowning in a 4,000-line report.

The Three Pillars of Repository Scanning

No single tool finds everything. Repository security is layered because the risks are fundamentally different in shape. A hardcoded API key is a credential problem. A vulnerable lodash version is a supply-chain problem. A SQL query built from string concatenation is a code-design problem. You need a dedicated technique for each.

Pillar Tool type Example finding
Secrets detection Secret scanner (e.g. gitleaks) AWS access key committed in .env.example
Dependency / CVE scanning SCA on lockfiles (e.g. osv-scanner, Trivy) log4j-core 2.14.1 with a known RCE CVE
SAST Static pattern analysis (e.g. Semgrep) Unsanitized input flowing into a raw SQL query

Let's take each in turn.

Pillar 1: Secrets Detection

A secret in your git history is a live credential until someone rotates it. The danger isn't only the current HEAD—it's the entire commit history. Developers routinely commit a key, notice the mistake, and delete it in the next commit, believing the problem is gone. It isn't. The key still sits in the history, retrievable by anyone who clones the repo.

Secret scanners look for high-entropy strings and known credential formats: cloud access keys, database connection strings, private keys, JWTs, OAuth tokens, and provider-specific patterns (Stripe, GitHub, Slack, and so on).

Why it matters: A leaked cloud key can mean a compromised account within minutes. Automated bots scrape public GitHub for exactly these patterns.

What to do about findings:

Pillar 2: Dependency and CVE Scanning

Most of the code you ship, you didn't write. Modern applications pull in hundreds of transitive dependencies, and any one of them can carry a known vulnerability (a CVE). Software Composition Analysis (SCA) reads your lockfiles—package-lock.json, poetry.lock, go.sum, and friends—resolves the exact versions in use, and matches them against vulnerability databases.

Lockfiles matter here because they pin transitive dependencies. Scanning only your top-level package.json misses the vulnerable package three levels down that you never chose directly.

Why it matters: Supply-chain attacks and known-CVE exploitation are among the most common breach vectors precisely because they scale. One vulnerable library can affect thousands of applications.

Prioritization: How KEV and EPSS Prevent Alert Fatigue

Here is the hard truth about CVE scanning: a real-world repo can surface hundreds of findings, and most of them will never be exploited. If you treat every CVE as equally urgent, your team burns out, ignores the report, and misses the one that matters.

Two signals fix this:

The practical effect: instead of a flat list of 300 CVEs sorted by severity, you get a short list of exploited-in-the-wild issues to fix today, then a probability-ranked queue for everything else. That is the difference between a report your team acts on and one they archive.

What to do about findings:

  1. Fix anything on the KEV list immediately.
  2. Sort the rest by EPSS; address high-probability items next.
  3. Most fixes are a version bump—update the dependency to a patched release and re-run the scan.
  4. For unfixable findings, document the risk and add a compensating control.

Pillar 3: SAST (Static Application Security Testing)

Secrets and dependencies cover what you imported. SAST covers what you wrote. Static analysis parses your own source code and flags insecure patterns without running the program: SQL injection, command injection, insecure deserialization, weak cryptography, path traversal, and missing authorization checks.

Good SAST is pattern-based and language-aware. It understands that user input reaching a shell command is dangerous in a way that a generic linter never could.

Why it matters: These are the bugs that map directly to the OWASP Top 10—injection, broken access control, cryptographic failures. They are flaws in your design, not someone else's package, so only you can fix them.

What to do about findings:

Bringing the Three Together on a Connected Repo

Running three separate tools, reconciling three report formats, and manually cross-referencing CVEs against exploit data is a job in itself. The point of a unified scan is to do all of it in one pass and hand you a single prioritized view.

This is the model NANOTESTING uses. When you connect a GitHub repository, it clones the repo into a private temporary directory and runs all three pillars together: gitleaks for hardcoded secrets, osv-scanner and Trivy for dependency CVEs (enriched with CISA KEV and EPSS priority), and Semgrep for SAST pattern analysis. Findings map to the OWASP Top 10 (2025) and other frameworks, and the repo gets an A–F grade so you can track direction over time. If your repo also contains Kubernetes manifests, mobile code, or smart contracts, those are scanned too.

One honesty principle is worth calling out: if a tool can't run—say there's no Kubernetes manifest to analyze—that shows up as a coverage gap, never as a clean pass. A green checkmark you didn't earn is worse than no result at all, because it tells you you're safe when no one actually looked.

A Practical Workflow

Whether you assemble these tools yourself or use a hosted scanner, the workflow is the same:

  1. Scan the whole repo, including history for secrets.
  2. Resolve lockfiles and pull CVE data with KEV/EPSS context.
  3. Run SAST against your own code.
  4. Prioritize ruthlessly: rotate leaked secrets now, fix KEV-listed CVEs now, then work the EPSS-ranked queue and high-impact SAST findings.
  5. Re-scan after fixes to confirm and to catch regressions.

To scan a GitHub repo for vulnerabilities well, you don't need more alerts—you need the right three lenses and a way to know which findings are real and exploited. Cover secrets, dependencies, and your own code, prioritize by what attackers are actually using, and re-run the scan as part of your normal cadence. That is what turns a security report from noise into a to-do list.

Related posts