Security
How g1t keeps secrets out of your repositories, finds vulnerable dependencies, and has an agent land the upgrades that fix them.
Every project has a Security page at g1t.sh/<owner>/<project>/security.
It shows what g1t has found, and what is being done about each finding:
- Secrets. A push that adds a key or a token is refused before it lands, and each repository’s history is scanned once, in the background.
- Dependencies. Every package your lockfiles resolve is checked against the OSV database of known vulnerabilities. Each vulnerable package that has a fix gets an upgrade issue, and a g1t agent lands the upgrade through the usual pull request, checks, review and merge queue.
Findings are the workspace’s to fix, so the Security page needs a role on the repository, whether the project is public or private:
| Needs | |
|---|---|
| See findings, Re-scan now | Write |
| Upkeep agents on or off | Maintain |
| Allow, Resolve or Reopen a secret | Admin |
Someone with Read or Triage is told the page needs Write. The workspace’s
own page, g1t.sh/<owner>/-/security, lists the open findings of every
project the member can see them on, most severe first.
The overview
Section titled “The overview”The top of the page counts open findings by severity: critical, high, medium, low and unrated. A secret that is open, or that stopped a push, counts as critical. Below the counts:
- Re-scan now reads the dependencies again at once and scans the history again from the start.
- Upkeep agents turns upgrade issues on or off for the project. It is on unless someone turns it off. With it off, findings are still listed, and nothing is opened for them.
The Secrets and Dependencies tabs list each finding with its status and the issue or pull request fixing it.
Secret scanning
Section titled “Secret scanning”g1t looks for credentials whose format their issuer made recognisable, so a match is nearly always a real secret or a fake made to look like one:
| What | What it looks like |
|---|---|
| AWS access keys | AKIA or ASIA and 16 more characters |
| AWS secret access keys | 40 characters on a line that names an AWS secret |
| GitHub tokens | ghp_, gho_, ghu_, ghs_, ghr_, github_pat_ |
| GitLab tokens | glpat-, gloas-, glrt-, glptt-, gldt- |
| Stripe live keys | sk_live_, rk_live_ (test keys are left alone) |
| Slack | xoxb-, xoxp- and other tokens, and incoming webhook addresses |
| Google API keys | AIza and 35 more characters |
| Anthropic API keys | sk-ant- |
| OpenAI API keys | sk-proj-, sk-svcacct-, sk-admin-, and older sk- keys |
| Private keys | A PEM BEGIN … PRIVATE KEY header followed by the key |
| Service-role JWTs | A JWT whose claims carry service_role |
| npm tokens | npm_ |
| g1t tokens | g1t_ and 40 hex characters |
| SendGrid keys | SG. and two dotted parts |
Placeholders such as ghp_xxxxxxxx… are not reported. Lockfiles, and files
under node_modules/ and vendor/, are not scanned.
g1t never stores a secret it finds. It keeps a fingerprint, so it can
recognise the same secret again, and a short preview, such as AKIA…, so
you can recognise it.
Push protection
Section titled “Push protection”When a push over HTTPS adds a secret, g1t refuses the whole push and nothing is stored. Only the lines the push adds are checked, so a secret that is already in the repository does not block every later push to the same file. This applies to every push, including the pushes g1t’s agents make to their pull requests.
Git shows why, file and line:
$ git pushremote: g1t found a secret in this push, so nothing was pushed.remote:remote: config/prod.env:3 an AWS access key (commit 4807077)remote:remote: Take the secret out of the commit that adds it (git commit --amend, orremote: git rebase -i for an older commit), rotate it if it was ever real, andremote: push again.remote:remote: If it is not a real secret, such as a test fixture:remote: - add g1t:allow-secret in a comment on its line, orremote: - allow it once at https://g1t.sh/acme/rocket/security?tab=secrets&finding=sec_…remote: Allowing is recorded with your name, then the same push goes through.To https://g1t.sh/acme/rocket.git ! [remote rejected] main -> main (secret found: config/prod.env:3 has an AWS access key)To fix a real secret:
- Remove it from the commit that adds it:
git commit --amendfor the last commit,git rebase -ifor an older one. - Rotate it with whoever issued it. Once a secret has been on any machine but yours, treat it as known.
- Push again.
Allowing a false positive
Section titled “Allowing a false positive”A fake key in a test, or an example in documentation, is still reported, on purpose: it looks exactly like a real one. Two ways to let it through:
-
Mark the line. Put
g1t:allow-secretanywhere on the line, usually in a comment. The line is never reported, in any push or in history.const FIXTURE_KEY = "AKIA…"; // g1t:allow-secret -
Allow it once. Open the link in git’s message (or the finding on the Secrets tab), choose Allow, and say why. g1t records who allowed it and why. Then push again, unchanged: that secret no longer stops a push to this project.
Allowing needs the Admin role on the repository. Someone else pushing to a pull request’s fork sees the same message and asks someone who has it.
Secrets in history
Section titled “Secrets in history”The first time g1t sees a repository (when it is created, on its next push to the default branch, or when its Security page is first opened) it scans the default branch’s history in the background, a page of commits at a time, comparing each commit with its first parent. The page shows how far it has got. Scanning is metered to the workspace like other usage, and it pauses if the workspace reaches its spending limit.
A secret found in history is Open: it is in the repository, and anyone who could clone it may have it. Rotate it, then choose Resolve and say what you did. Removing it from the code is not enough, since it stays in history.
| Status | Meaning |
|---|---|
| Open | In the repository’s history. Rotate it, then resolve it. |
| Push blocked | A push carrying it was refused, so it never landed. |
| Allowed | Someone said it is not a real secret; pushes carrying it go through. |
| Resolved | Someone rotated or removed it. |
Reopen undoes an allow or a resolve.
Dependency upkeep
Section titled “Dependency upkeep”g1t reads these lockfiles on the default branch, up to four directories
deep, skipping node_modules, vendor, target, dist and build:
| Ecosystem | Files |
|---|---|
| npm | package-lock.json, pnpm-lock.yaml, yarn.lock |
| Cargo | Cargo.lock |
| Go | go.mod (or go.sum where there is no go.mod) |
| Python | poetry.lock, and pinned lines (name==1.2.3) in requirements.txt |
It reads them on every push to the default branch and again every day, and asks OSV about every package at the exact version locked. Each advisory found is listed with its id (its GHSA id when it has one), severity, the version that fixes it, and the lockfile that resolves the vulnerable version. A vulnerability that a later scan no longer finds is marked fixed.
Upgrade issues
Section titled “Upgrade issues”With Upkeep agents on, each vulnerable package that has a fixed version
gets one issue, labelled dependencies and security, such as:
Upgrade lodash to 4.17.21: fixes GHSA-35jh-r3h4-6jhm
The issue lists every advisory it fixes, and the target is the lowest version that fixes all of them. Its acceptance checks are:
-
a command per lockfile that fails while the lockfile still resolves the vulnerable version, and
-
the project’s tests, run in the lockfile’s directory by its package manager:
Lockfile Tests package-lock.jsonnpm ci && npm test --if-presentpnpm-lock.yamlpnpm install --frozen-lockfile && pnpm test --if-presentyarn.lockyarn install, thenyarn testCargo.lockcargo test --lockedgo.mod,go.sumgo test ./...Python projects get the lockfile check only, since there is no one way to run their tests.
g1t puts its agent on the first new upgrade at once and queues the rest, which start as the project has room for more agents. The agent upgrades the package, changes whatever code the upgrade breaks, and opens a pull request that goes through checks, review and the merge queue like any other. When it merges, the next scan finds the vulnerability fixed.
g1t opens at most eight upgrade issues per scan, most severe first, and never a second issue for a package while one is open. If you close an upgrade issue as not planned, g1t does not open it again. If agents cannot run in the workspace, the issue stays open with a comment saying why, for you to assign once they can, or to upgrade by hand.
Turn Upkeep agents off on the Security page to stop new upgrade issues for a project. Issues already open are left as they are.