These weren’t repos submitted for a security audit. They were projects developers posted to Hacker News themselves, to show off, to get feedback, to launch. We pulled 976 of them, shallow-cloned each one, scanned it with Prbl, and deleted the clone immediately after. All 976 scanned successfully. 31.6% of them had at least one high-severity finding. Not a style nitpick, not a linter warning. A finding that could plausibly lead to unauthorized access, credential exposure, or arbitrary code execution.
Top high-severity findings (occurrences)
31.6% is the share of repos with at least one high-severity finding. The counts above are individual findings, not repos. A single repo can contain the same issue more than once, so these numbers are not directly comparable. The table below has both.
Wondering if your repo is one of the 1 in 3? Find out before anyone else does.
Find what AI missed — freeAverage scan time: under 60 seconds. No signup required.
What “high-severity” actually means here
Three patterns showed up far more than anything else. Each one is the kind of thing that doesn’t show up in a demo, doesn’t show up in a code review that’s checking “does this work,” and shows up the day someone deliberately tries to break in.
| Finding | CWE | Occurrences | Affected repos |
|---|---|---|---|
| Path traversal | CWE-22 | 329 | 129 |
| Hardcoded credentials | CWE-798 | 214 | 102 |
| Code injection | CWE-94/95 | 167 | 72 |
Wondering if your code has any of this? Scan a public repo free, no account needed.
Scan a repo →1. Path traversal: someone reads or deletes files they were never supposed to touch
The single most common high-severity finding: 329 occurrences across 129 repos. The shape of the bug is almost always the same: a filename or path comes from user input, and it gets used to open, read, or delete a file with no check on what that path actually points to.
An illustrative, simplified example of the pattern:
app.get('/download', (req, res) => {
const file = req.query.file;
res.sendFile(path.join(uploadsDir, file));
});A normal request looks like /download?file=report.pdf. Change it to /download?file=../../.env and, if there’s no check on what that path resolves to, the server will hand over a file it never meant to expose.
What this actually costs you: a single request can pull files that were never meant to leave your server, config files, other users’ uploads, sometimes credentials sitting in a .env file. If your app stores anything per-user on disk, this is the bug that turns “my data” into “everyone’s data.”
2. Hardcoded credentials: the password is sitting in your source code
The second most common finding: 214 occurrences across 102 repos. A real password, API key, or signing secret typed directly into the code instead of pulled from an environment variable. Once that’s in source control, it’s permanent. Even if you delete the line in a later commit, the secret is still sitting in the git history, recoverable by anyone with read access to the repo, indefinitely.
What this actually costs you: if the repo is public, that credential is public. If it’s private but the repo ever gets cloned, forked, or leaked, that secret goes with it. Automated bots scan public GitHub repos for exactly this pattern around the clock.
3. Code injection: user input gets executed as code
The third most common finding: 167 occurrences across 72 repos. User-controlled input flows into something that dynamically loads or executes code, a module name, a shell command, an eval() call. Whoever controls that input has a path toward controlling what the server runs.
What this actually costs you: of the three patterns here, this is the one with the highest ceiling. Depending on the surrounding context, it can range from a controlled internal script issue up to remote code execution on infrastructure you don’t want a stranger touching.
Two more patterns worth knowing about
Beyond the three above, two other issues showed up constantly across the dataset, generally rated medium rather than high severity: routes that check whether someone is logged in but never check whether they own the specific thing they’re asking for, and randomness used in a security context (tokens, OTPs, session IDs) that’s predictable instead of truly random. Neither is exotic. Both are common in apps built fast, and both are the kind of thing that’s invisible until someone deliberately tries to break in.
Why this keeps happening
None of this is about careless developers. It’s about what “the code works” actually verifies. A path traversal bug doesn’t break the demo. A hardcoded credential doesn’t fail the test suite. Code that’s functionally correct and code that’s safe to put in front of real users are two different bars, and most review processes, human or automated, are only checking the first one.
Methodology
Repos were sourced from recent Show HN and Ask HN posts via the public HN Algolia API, filtered to repositories whose Show HN or Ask HN description suggested an end-user application, SaaS product, dashboard, service, or tool. Libraries, frameworks, tutorials, and example projects were excluded. Each repo was shallow-cloned, scanned with Prbl’s open-source scanner rules, and the clone deleted immediately afterward. No source code was retained, and no specific repo or finding is identified anywhere in this post. All 976 repos targeted were scanned successfully. Full methodology, including how we found and fixed false positives in our own rules before publishing these numbers, is in how we test Prbl.
Severity and counts above cover the three rule classes we’re fully confident are clean after manual review: path traversal, hardcoded credentials, and code injection. Two other common patterns (missing ownership checks, weak randomness) are discussed qualitatively above without a specific count, pending further precision work on those specific rules. Severity definitions follow standard CWE and OWASP Top 10 classifications.
The aggregate counts behind every number in this post are available as a raw CSV. It contains rule-level occurrence counts and affected-repo counts only, no repo names, file paths, or owners.
A note on the numbers above: occurrence counts and affected-repo counts are different things. A single repo can contain the same finding multiple times, for example the same unchecked file-path pattern repeated across several routes, so occurrence counts are higher than the number of repos actually affected by that specific issue. The table above has both.
Limitations
- Results are based on static analysis and may contain false positives.
- We manually reviewed and refined the rules used in this study, but did not manually verify every individual finding.
- Only projects shared publicly on Hacker News were included. This is not a random or representative sample of all GitHub repositories.
- Findings represent code-level risk and do not account for deployment-specific mitigations.
Frequently asked questions
What percentage of GitHub repos have security vulnerabilities?
In a sample of 976 real, recently posted GitHub repos, 31.6% had at least one high-severity security finding, most commonly path traversal, hardcoded credentials, or code injection.
What is the most common security vulnerability in public GitHub repos?
Path traversal was the single most common high-severity finding in this dataset (329 occurrences across 129 repos), ahead of hardcoded credentials (214 occurrences, 102 repos) and code injection (167 occurrences, 72 repos). Missing ownership checks on routes appeared even more often overall but is typically rated medium rather than high severity.
How do I check if my own repo has these issues?
Paste your GitHub repo URL into Prbl’s free scanner for a direct answer in under a minute. No account required.
What to actually do about it
The 976 repos in this dataset didn’t know they were being checked. Yours hasn’t been checked yet either.
You don’t need to read your own codebase line by line wondering if you’re one of the 1 in 3. Paste your GitHub repo URL into Prbl and get a direct answer in under a minute, free, no account required. If something high-severity turns up, you’ll know exactly which file, which line, and what to do about it.