Six reviewers each attacked one part of Amadan — auth, access control, transport, CI, crypto, and the web layer — and traced every issue to the real code and the test that catches it. An Aikido SAST + secrets scan ran alongside.
The result that matters: nobody found a way for a hacked hub or instance to read an E2EE repo's contents, index, or keys. The bugs that were actually exploitable lived in the ordinary app and infrastructure layers, and they're fixed. What's left is hardening.
Every finding and where it stands. Full write-ups of the fixes are in the changelog.
| ID | Finding | Sev | Surface | Status |
|---|
Amadan's promise: hack the server, get nothing. For E2EE repos that holds for content. Here's exactly what metadata the server can still see, read straight from the code.
An E2EE repo is only ever ciphertext on the server: code, file names, branches, commit messages, the index, discussions, CI logs and secrets. The server never writes a plaintext copy to disk. Every git and browse route returns 404 first. A canary push leaves no trace in the raw database, and the stored packs aren't valid git — you can't unpack them without the key.
CT ciphertext · PT plaintext on server · meta structural metadata · H keyed handle (correlatable, not the name).
| Data | private_e2ee | Notes |
|---|---|---|
| Git contents, file names, tree shape, commit messages/authors/dates | CT | Sealed packs; never materialized |
| Refs, branch names, default branch, commit SHAs | CT | Inside the sealed manifest |
| Browse index, README, discussions, CI logs, CI secret values & names | CT | Per-purpose sealed subkeys |
| Manifest version, pack count, sealed pack sizes, push timing | meta | Activity, not content |
| Each member's public keys and role; member count | PT | The members table (wrapped key stays CT) |
| Who pushed / posted / reported CI, and when | meta | Author pubkey + timestamps |
| Repo existence, namespace, name, tier, rename history | meta | Hub metadata by design |
| Branch / commit / secret / pipeline identifiers | H | Keyed handles — counts, not names |
One caveat. The plain private tier is GitHub-style: content and secrets sit in plaintext on the server, protected by permissions rather than crypto. Only private_e2ee gets the "server sees nothing" guarantee. Worth saying out loud so nobody over-claims it.
Hardening worth doing, none of it urgent. Start with R-01 — it's the only open item that touches the core promise.
Things the reviewers tried to break and couldn't, with the tests that keep them that way.
A local Aikido SAST + secrets scan of the changed and security-critical code, run on the machine.
No secrets. No real bugs. Two heuristic flags came up; both are false positives once you read the code.
| Rule | Where | Why it fired | Assessment |
|---|---|---|---|
| G204 command exec | runnerisolate.go | Runs a subprocess whose path comes from a variable. | False positive — the path is exec.LookPath("bwrap"), the args are fixed flags, and there's no shell. Nothing to inject. |
| G107 SSRF | oauth.go | Builds a request URL from a config value. | False positive — the host is AMADAN_KEYMAIL_ISSUER, set by whoever runs the deployment. A user can't steer it. |
Yes — as a CI step, with no changes to Amadan. It slots into the merge gate Amadan already has. A built-in integration isn't possible, because Aikido doesn't connect to Amadan as a git host.
Amadan already blocks a merge unless the latest commit passed CI. Add Aikido as a step in that CI, and a failed scan blocks the merge. Two ways to run it:
A built-in status check, like Aikido posts on GitHub PRs, would need Aikido to support Amadan as a git host, or Amadan to expose a compatible API. Neither exists yet.
Every fix from this round, each with its own test, all green under make test. Line references are on branch security-posture. ← Back to the dashboard