What it actually gives away
People underestimate this one because they picture their current source code and decide they can live with that. The current source is the least of it.
The mechanism, briefly
A repository is a database, and it is made of ordinary files.
Git keeps everything in the .git folder at the top of a project. Not a copy of the current files: a complete record of every version of every file the project has ever contained, along with who changed each one and when.
It stores that record as plain files on disk. A small file naming the current branch, a set of files naming where each branch points, and then the objects themselves, either one file per object or gathered into a handful of larger archive files for efficiency.
That last detail is the whole problem. Because it is all ordinary files, a web server that has been pointed at the project folder will serve them exactly as it serves an image. There is nothing to exploit and no vulnerability involved. The server is asked for a file, the file is there, and the server hands it over.
Which is also why the fix is so cheap. Nothing is broken. The server simply needs telling not to answer for that folder.
The part that catches people
Deleting a secret does not delete it.
This is the single most important paragraph on this website.
Suppose a configuration file with a live database password was committed by accident. Somebody noticed a fortnight later, took the password out, committed the fix, and moved on. The current file is clean. Everything looks right.
The repository still has the original commit, because that is what a repository is for. The whole point of version control is that the earlier state is recoverable. Removing a line from the current file adds a new commit describing the removal, and leaves the old commit exactly where it was.
So if the repository is readable from the internet, the password is readable from the internet, and it has been readable since the day it was first committed rather than since somebody noticed.
The same is true of a key that was rotated, a staging credential nobody thought mattered, a customer list committed once during a migration, and the file somebody added and removed in the same afternoon.
The inventory
What comes out, in rough order of how much it hurts
1. Credentials, from any point in the history
Database passwords, mail account passwords, payment gateway keys, cloud access keys, third party API tokens, signing secrets, and the contents of any environment file that was ever committed. This is the finding that turns a leak into an incident, because it is the one that gets somebody into a system rather than merely informed about it.
Worth knowing: the repository's own configuration file records where the project was cloned from. On a project cloned over HTTPS with a token, that address sometimes contains the token, in plain text, in a file called config.
2. The layout of everything not linked from the site
Administrative paths, upload directories, backup scripts, internal hostnames, staging and development addresses, cron jobs, deployment scripts, and the names of database tables and columns. Most attacks on a small site begin by guessing at this. An exposed repository replaces the guessing with reading.
3. Your exact dependency versions
The lock files list every library the project uses and the precise version of each one. Anybody can then look those versions up against published vulnerability lists rather than probing your site to find out what it runs. It converts a slow noisy job into a quick quiet one.
4. The logic you would rather not explain
How discounts are calculated, how permissions are decided, which checks happen in the browser and which happen on the server, and where the shortcuts are. Anything relying on nobody knowing how it works stops relying on anything at all.
5. Your people
Every commit is stamped with a name, an email address and a timestamp. Taken together that is a staff list, a set of live email addresses, an indication of who is senior, and a record of who works late. It is the raw material for a convincing phishing message addressed to the right person.
6. Commented out code and old branches
The endpoint that was disabled but not removed. The test account left in place. The debugging route somebody added during an outage in 2023. All still there, all still readable.
How likely is it that somebody found it
Assume they did, because looking costs nothing.
This is not a leak that waits to be discovered by somebody targeting you. Automated scanners request this address on every website they can find, continuously, because the check is one cheap request and the payoff when it lands is large.
So the honest answer to "would anybody really have bothered" is that bothering is free and it is done at scale, indiscriminately, whether or not anybody has heard of your business.
Your access logs will tell you something useful. Search them for the path. You will almost certainly find requests, because everybody gets them. What matters is whether any of them were answered successfully rather than refused, and whether a single address then made a run of further requests.
Not finding successful requests is genuinely reassuring. Finding them, or having no logs going back far enough, means treating every secret in the history as known.
Afterwards
Blocking the folder is the first step, not the last one.
Once the folder is no longer served, nothing new leaks. Everything that already leaked is still out there, and the only remedy for a published secret is to make it stop working.
- Change every credential the repository has ever held. Not the ones you remember committing. Every one, because the ones you remember are not the problem.
- Rotate keys and tokens at every third party service the project touches, and revoke the old ones rather than simply issuing new ones alongside.
- Change the deploy credential itself, if the repository was cloned using a token or a deploy key.
- Look for sessions and access that are already open, in your hosting control panel, your database and your cloud account, and end anything you cannot account for.
- Then stop the file reaching the server at all. Blocking the folder is a guard. Not deploying the repository in the first place is the actual fix.
If this is more than you want to work through
Dalaric runs an authorised test of one web application, with the findings triaged and written up in plain English and a call to go through them. £595 plus VAT. It is automated testing read by a person rather than a manual penetration test, and you will be told plainly when it is the wrong thing for you.
A repository in a web root usually means the way the site is deployed is the real finding, and that is infrastructure work rather than a security report.
Short answers
Questions people actually ask
Does this mean somebody has already copied my code?
Assume so, and act on that basis. This is not a leak that waits to be found by somebody targeting you: scanners request this address on every website they can reach, continuously, because the check costs one request and the payoff is large. Your access logs will tell you whether any of those requests were answered rather than refused.
I removed the password in a later commit. Is it still a problem?
Yes, and this is the single most misunderstood thing about git. Removing a line adds a new commit describing the removal. The commit that first contained the password still contains it, because keeping the earlier state is the entire point of version control. If the repository is readable, the password is readable, and it has been since the day it was committed. What to do about that.
My repository is private on GitHub. Can this still happen?
Yes. The private one is the copy on your hosting provider. What is exposed is the copy on your web server, which got there when somebody deployed by pulling or by copying the project folder. The two are unrelated, and the permissions on one say nothing about the other.
Is this really a vulnerability, or just untidy?
Nothing is broken and nothing is being exploited. The server is asked for a file, the file is there, and it hands it over. That is also why the fix is so cheap. It becomes an incident rather than an embarrassment when the history contains a credential, which is the usual case rather than the unlucky one.
My code is open source anyway. Does it matter?
Less, but not none. The published repository and the deployed one are rarely identical: the deployed one often carries an environment file, a configuration committed in a hurry, or a branch that was never meant to be public. It is worth blocking regardless, because it costs a minute.
How would I know if anybody actually downloaded it?
Search your access logs for the path. You will find requests, because everybody gets them. What matters is whether any were answered with a success rather than refused, and whether one address then made a run of further requests. Finding none is genuinely reassuring. Having no logs going back far enough means treating every secret in the history as known.
More guides
How to block it
The rule for Apache, nginx, Caddy, LiteSpeed, IIS and Cloudflare.
Deploying without publishing the repository
Three ways to get code onto a server that cannot leak like this.
A password is in your history
Why changing it is the fix and rewriting history is not.