Pwnkemon
← All posts
Architecture·9 June 2026·7 min read

We scan your private repo. Here's why we can't leak it.

To scan your private repo, we have to clone it. That means, for a few minutes, your source code lives on a machine we run. If you're not slightly uncomfortable with that sentence, you're not paying attention. Handing source to a third party is a real risk, and “trust us” is not a security control.

So we designed the scanner around a harder assumption: that the scan container is already compromised. The repo we're scanning might be hostile. A dependency might be malicious. The question we built around isn't “how do we keep the container clean”, it's “when the container is owned, what can the attacker actually reach?” The answer we want is: nothing worth having.

No long-lived secrets on the box

The obvious prize inside a scan container would be credentials: our LLM API key, a database URL, a GitHub App private key. So none of them are there.

The scan runs on a dedicated host that holds no standing secrets. When it needs to talk to the LLM for triage, it doesn't hold the model API key. It calls a broker on our API that makes the call on its behalf, with a per-scan spend cap. When it needs to clone your private repo, it doesn't hold the GitHub App key either. It asks the broker for a short-lived installation token, scoped to the one scan, that expires within the hour. Steal everything on the box and you get tokens that are already expiring and scoped to the job you were already running.

Per-scan tokens, not a master key

Every scan gets its own token, valid only for that scan's ID, usable only on the three endpoints a scan legitimately needs, and dead shortly after the scan's timeout. A compromised container can't use its token to claim another customer's job, fetch another repo's install token, or post results for a scan it doesn't own. The blast radius of a stolen token is exactly one scan: the one the attacker had already compromised anyway.

This is the same instinct behind not running our scanners on your CI runner: minimise what any single compromised component can touch.

Locked-down egress

A compromised container's next move is to phone home and POST your source to an attacker server. So the container can't freely reach the internet. Egress is restricted per scan to a computed allowlist of exactly the hosts that scan legitimately needs: your repo's Git endpoints, the package registries the dependency scanners consult, the LLM broker, and nothing else. Traffic to anywhere else is dropped at the firewall, not politely refused. There's no open path from inside the container to an arbitrary host on the internet.

The repo's own code never runs

The subtle risk with code scanning is that some tools execute the code they analyse. pip-audit resolves and builds packages; a naive npm install runs lifecycle scripts. Either is arbitrary-code-execution-as-a-service on behalf of a repo we don't trust.

We don't do that. Python dependencies go through osv-scanner, a static lookup against the OSV database, with no build and no install. Node dependencies go through npm audit --package-lock-only, which reads the lockfile and never installs node_modules or fires a lifecycle script. The scanners read your code; they never run it. On top of that, the scanners themselves execute inside a locked-down sandbox: fresh namespaces, a read-only root filesystem, and no network, so even a scanner exploited by a crafted repo is boxed in.

The code doesn't outlive the scan

The clone lands in a per-scan temporary directory that is destroyed when the scan finishes, success or failure. We don't keep your source. What persists is the findings report (severities, file paths, the triage rationale), not the code itself. The machine is cattle, not a pet: it holds your repo for the minutes it takes to scan it and nothing after.

Why we write this down

Most scanners ask for read access to your code and tell you it's fine. We think the honest version is: it's fine because of specific, auditable controls, and you should be able to see them. No standing secrets to steal, per-scan tokens that expire, egress that can't reach an attacker, scanners that never run your code, and a clone that's deleted when we're done. Each one is there to answer the same question, “what if the container is owned?”, with the same answer: “then the attacker got nothing.”

We hold ourselves to the same bar we'd want from anyone we handed our own source to. It's also why we scan ourselves every day: the scanner that reads your repo is the same one that reads ours.