Skip to content
378 linesCodeBlameRaw
1---
2title: Security
3description: How g1t keeps secrets out of your repositories, finds vulnerable dependencies, and opens the pull requests that fix them.
4---
5
6import Aside from '../../../components/Aside.astro';
7
8Every project has a **Security** section at `g1t.sh/<owner>/<project>/security`.
9It shows what g1t has found, and what is being done about each alert:
10
11- **Secrets.** A push that adds a key or a token is refused before it lands,
12 and each repository's history is scanned once, in the background. See
13 also [secret protection](/guides/security/secret-protection/): custom
14 patterns, bypassing with a reason, and validity checks.
15- **Dependencies.** Every package your lockfiles resolve is checked against
16 the [OSV](https://osv.dev) database of known vulnerabilities. For each
17 vulnerable package that has a fix, g1t opens a pull request that raises it
18 to the fixed version, and the pull request lands through your branch's
19 required checks and merge queue. See also the
20 [supply chain](/guides/security/supply-chain/): the dependency graph, its
21 SBOM, and dependency review on pull requests.
22- **Code.** Static analysis results uploaded as SARIF become alerts, and
23 checks on pull requests. See [code scanning](/guides/security/code-scanning/).
24
25Its pages are **Overview**, **Secret scanning**, **Code scanning**,
26**Vulnerabilities**, **Dependency graph**, **Dependency updates** and
27**Settings**. Secret scanning,
28push protection, vulnerability alerts, security updates and the dependency
29graph are free everywhere; the rest is free on public repositories and part
30of the Security and quality activation on private ones. See
31[what's free and what's paid](/guides/security/pricing/), and the
32workspace's [security overview](/guides/security/security-overview/).
33
34Alerts are the workspace's to fix, so the Security page needs a
35[role](/guides/access-and-roles/) on the repository, whether the project
36is public or private:
37
38| | Needs |
39| --- | --- |
40| See alerts, **Re-scan now** | Write |
41| **Dismiss** or **Reopen** an alert: a secret, a dependency or code scanning | Write |
42| **Bypass** push protection for a blocked secret | Write (or a request, with [delegated bypass](/guides/security/secret-protection/#delegated-bypass)) |
43| **Fix with g1t** | Write, and agents allowed to run |
44| **Security updates** on or off, **Set up code scanning** | Maintain |
45| **Security settings**, **Custom patterns**, reviewing bypass requests | Admin |
46
47A workspace's [security managers](/guides/workspaces/#roles-that-add-to-a-member)
48can do all of this on every repository in it, and change the workspace's
49security settings, whatever their role; owners can too.
50
51Someone with Read or Triage is told the page needs Write. The workspace's
52own page, `g1t.sh/<owner>/-/security`, lists the open alerts of every
53project the member can see them on, most severe first.
54
55## The overview
56
57The **Overview** counts open alerts by severity: critical, high,
58medium, low and unrated. The counts are:
59
60- each open vulnerability, by its severity,
61- each open code scanning alert, by its severity, and
62- each secret in the repository's history that looks real, as critical.
63
64A secret that a push carried but that never landed (push blocked) is not
65counted, and neither is a [likely test value](#likely-test-values). A
66dismissed alert is not counted. Below the counts:
67
68- **Re-scan now** (on **Vulnerabilities**) reads the dependencies again at
69 once and scans the history again from the start.
70- **Security updates** (on **Vulnerabilities**) turns
71 [security updates](#security-updates) on or off for the project. It is on
72 unless someone turns it off.
73
74**Secret scanning** and **Vulnerabilities** list each alert. Filter either
75by state:
76
77| State | Meaning |
78| --- | --- |
79| Open | Needs attention. |
80| Dismissed | Someone dismissed it with a reason. |
81| Fixed | The secret was revoked, or the vulnerable version is no longer resolved by any lockfile. |
82
83## Secret scanning
84
85g1t looks for credentials whose format their issuer made recognisable, so a
86match is nearly always a real secret or a fake made to look like one:
87
88| What | What it looks like |
89| --- | --- |
90| AWS access keys | `AKIA`, `ASIA`, `ABIA` or `ACCA` and 16 more characters |
91| AWS secret access keys | 40 characters on a line that names an AWS secret |
92| GitHub tokens | `ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_`, `github_pat_` |
93| GitLab tokens | `glpat-`, `gloas-`, `glrt-`, `glptt-`, `gldt-` |
94| Stripe live keys | `sk_live_`, `rk_live_` (test keys are left alone) |
95| Slack tokens | `xoxb-`, `xoxp-`, `xoxa-`, `xoxr-`, `xoxs-`, `xoxe-` |
96| Slack webhooks | `https://hooks.slack.com/services/` addresses |
97| Google API keys | `AIza` and 35 more characters |
98| Anthropic API keys | `sk-ant-` |
99| OpenAI API keys | `sk-proj-`, `sk-svcacct-`, `sk-admin-`, and older `sk-` keys |
100| Private keys | A PEM `BEGIN … PRIVATE KEY` header followed by the key |
101| Service-role JWTs | A JWT whose claims carry `service_role` |
102| npm tokens | `npm_` |
103| g1t tokens | `g1t_` and 40 hex characters |
104| SendGrid keys | `SG.` and two dotted parts |
105
106Placeholders made of a handful of characters, such as `ghp_xxxxxxxx…`, are
107not reported. Lockfiles, and files under `node_modules/` and `vendor/`, are
108not scanned.
109
110g1t never stores a secret it finds. It keeps a fingerprint, so it can
111recognise the same secret again, and a short preview, such as `AKIA…`, so
112you can recognise it.
113
114### Likely test values
115
116g1t judges each value it finds by the value alone, never by the file it is
117in: a key under `tests/` is as real as one under `src/` if it was ever
118issued. A value is a **likely test value** when it:
119
120- is a key its issuer publishes as an example, such as AWS's documented
121 example keys;
122- says it is an example: it contains `example`, `sample`, `dummy`, `fake`,
123 `placeholder`, `changeme`, `notreal`, `redacted` or `xxxxx`;
124- counts up for six or more characters in a row, like `abcdef` or `123456`;
125- repeats one character five or more times in a row;
126- is a short piece repeated; or
127- has too little randomness to be a real key.
128
129A likely test value is still listed, in its own group on **Secret
130scanning**, with the reason. It never stops a push and is never counted as
131critical. Private keys are judged by the words only.
132
133### Push protection
134
135When a push over HTTPS adds a secret, g1t refuses the whole push and nothing
136is stored. Only the lines the push adds are checked, so a secret that is
137already in the repository does not block every later push to the same file.
138This applies to every push, including the pushes g1t and its agents make to
139pull requests.
140
141Very large pushes are scanned after they land, not before. Such a push is
142too large to read in full before it is stored, so it goes through, and g1t
143then scans every commit it added, on whichever branch, in the
144background. A secret found there is an open alert, as if it had been found
145in history, and the workspace's owners get an email when one looks real.
146To have a large push checked before it lands, push it in parts, as
147[Size limits](/guides/git/#size-limits) shows.
148
149Git lists every secret it found, by file and line, with a link to allow
150each one:
151
152```text
153$ git push
154remote: g1t found a secret in this push, so nothing was pushed.
155remote:
156remote: config/prod.env:3 an AWS access key (commit 4807077)
157remote:
158remote: Take the secret out of the commit that adds it (git commit --amend, or
159remote: git rebase -i for an older commit), rotate it if it was ever real, and
160remote: push again.
161remote:
162remote: If it is not a real secret (a test fixture), or you will rotate it later:
163remote: - add g1t:allow-secret in a comment on its line, or
164remote: - bypass it with a reason at https://g1t.sh/acme/rocket/security/secret-scanning/sec_…
165remote: A bypass is recorded with your name and reason (or goes to an owner
166remote: to approve, if your workspace asks), then the same push goes through.
167To https://g1t.sh/acme/rocket.git
168 ! [remote rejected] main -> main (secret found: config/prod.env:3 has an AWS access key)
169```
170
171To fix a real secret:
172
1731. Remove it from the commit that adds it: `git commit --amend` for the last
174 commit, `git rebase -i` for an older one.
1752. Rotate it with whoever issued it. Once a secret has been on any machine
176 but yours, treat it as known.
1773. Push again.
178
179### Letting a secret through
180
181A fake key that does not look made up is still reported, on purpose: it
182looks exactly like a real one. Two ways to let it through:
183
184- **Mark the line.** Put `g1t:allow-secret` anywhere on the line, usually in
185 a comment. The line is never reported, in any push or in history.
186
187 ```ts
188 const FIXTURE_KEY = "AKIA…"; // g1t:allow-secret
189 ```
190
191- **Bypass it.** Open the link in git's message and choose **Bypass push
192 protection** with a reason, then push again, unchanged. Anyone with Write
193 can; see [pushing past push protection](/guides/security/secret-protection/#pushing-past-push-protection).
194- **Dismiss the alert.** Open the alert on **Secret scanning**, choose
195 **Dismiss** with the reason **False positive** or **Used in tests**, and
196 push again, unchanged: that secret no longer stops a push to this
197 project.
198
199Dismissing a secret alert needs the Admin [role](/guides/access-and-roles/)
200on the repository. Someone without it sees the same message and asks
201someone who has it.
202
203### Secrets in history
204
205The first time g1t sees a repository (when it is created, on its next push
206to the default branch, or when its Security page is first opened) it scans
207the default branch's history in the background, a page of commits at a
208time, comparing each commit with its first parent. The page shows how far
209it has got. Scanning is metered to the workspace like other usage, and it
210pauses if the workspace reaches its spending limit.
211
212A secret found in history is open: it is in the repository, and anyone
213who could clone it may have it. Rotate it, then dismiss it as **Revoked**.
214Removing it from the code is not enough, since it stays in history.
215
216### Dismissing a secret alert
217
218Choose **Dismiss**, pick a reason, and add a comment if you like (up to 500
219characters):
220
221| Reason | API value | What happens |
222| --- | --- | --- |
223| False positive | `false_positive` | Dismissed. Pushes carrying the secret go through. |
224| Used in tests | `used_in_tests` | Dismissed. Pushes carrying the secret go through. |
225| Revoked | `revoked` | Fixed. The secret was real and has been revoked or rotated. |
226| Won't fix | `wont_fix` | Dismissed. Pushes carrying the secret go through. |
227
228Who dismissed it, when, the reason and the comment are kept in the alert's
229activity log. **Reopen** puts the alert back to open; a secret that never
230landed goes back to blocking pushes.
231
232## Dependencies
233
234g1t reads these lockfiles on the default branch, up to four directories
235deep, skipping `node_modules`, `vendor`, `target`, `dist` and `build`:
236
237| Ecosystem | Files |
238| --- | --- |
239| npm | `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock` |
240| Cargo | `Cargo.lock` |
241| Go | `go.mod` (or `go.sum` where there is no `go.mod`) |
242| Python | `poetry.lock`, and pinned lines (`name==1.2.3`) in `requirements.txt` |
243
244It reads them on every push to the default branch and again every day, and
245asks OSV about every package at the exact version locked. Each advisory
246found is listed with its id (its GHSA id when it has one), severity, the
247version that fixes it, and the lockfile that resolves the vulnerable
248version. A vulnerability that a later scan no longer finds is marked fixed.
249
250### Dismissing a dependency alert
251
252Dismissing a dependency alert needs the Write role. Pick a reason, and add
253a comment if you like (up to 500 characters):
254
255| Reason | API value |
256| --- | --- |
257| A fix has already been started | `fix_started` |
258| No bandwidth to fix this | `no_bandwidth` |
259| Risk is tolerable to this project | `tolerable_risk` |
260| This alert is inaccurate or incorrect | `inaccurate` |
261| Vulnerable code is not actually used | `not_used` |
262
263A dismissed vulnerability that a later scan finds again stays dismissed
264until someone reopens it. The activity log keeps who dismissed it, when,
265the reason and the comment.
266
267### When there is no fix yet
268
269When no patched version has been published, the alert links the advisory
270and nothing is opened for it. You can dismiss it as **Risk is tolerable to
271this project**. g1t checks your dependencies again every day, fetching
272OSV's record for each advisory without a fix again, and opens a security
273update as soon as a fix is published.
274
275## Security updates
276
277With **Security updates** on, g1t opens a pull request to upgrade each
278vulnerable dependency that has a fix. It lands through your branch's
279required checks. A [dependency update file](/guides/dependency-updates/#security-updates-follow-the-same-file)
280can skip packages, group fixes into one pull request, and set the title's
281prefix, assignees and reviewers.
282
2831. g1t starts a sandbox, which raises the package to the fixed version in
284 each lockfile that resolves it, using the ecosystem's own tool.
2852. It commits as `g1t <g1t@users.noreply.g1t.sh>` and pushes a branch named
286 `g1t/security/<package>-<version>`.
2873. It opens a pull request, authored by `g1t`, that names the advisories it
288 fixes, the old and new versions, and the lockfiles it changed.
2894. The pull request merges through the branch's
290 [required status checks](/guides/pull-requests/#required-status-checks)
291 and the [merge queue](/guides/merge-queue/), like any other. When it
292 merges, the next scan finds the vulnerability fixed.
293
294Each alert shows its update's status and links the pull request:
295
296| Status | Meaning |
297| --- | --- |
298| Making the change | The sandbox is raising the version. |
299| Open | The pull request is open. |
300| Merged | The pull request merged. |
301| Closed | The pull request was closed without merging. |
302| Superseded | A newer security update for the same package replaced it, or the package is no longer vulnerable. |
303| Needs code changes | Raising the version was not enough; an agent is working on it. |
304| Failed | The update could not be made. |
305
306A newer security update for the same package closes the older pull request
307as superseded. So does the package no longer being vulnerable.
308
309How each lockfile is changed:
310
311| Lockfile | How the version is raised |
312| --- | --- |
313| `package-lock.json` | `npm install --package-lock-only --ignore-scripts` for a direct dependency, keeping its range style; for an indirect one, `npm update`, then an `overrides` entry if that is not enough |
314| `pnpm-lock.yaml` | `pnpm update --lockfile-only`, then `pnpm.overrides` if needed |
315| `yarn.lock` | `yarn up` (Yarn 2 and later) or `yarn upgrade` (Yarn 1), then `resolutions` if needed |
316| `Cargo.lock` | `cargo update -p <package> --precise <version>` |
317| `go.mod`, `go.sum` | `go get <module>@<version>`, then `go mod tidy` |
318| `poetry.lock` | `poetry add <package>@^<version> --lock`, or `poetry update --lock` for an indirect one |
319| `requirements.txt` | The `==` pin is rewritten |
320
321Install scripts never run. A lockfile the sandbox cannot change this way,
322such as a `requirements.txt` that pins with `--hash`, is handled as
323[when code has to change](#when-code-has-to-change): if no branch has been
324pushed 45 minutes after the update started, g1t gets an issue for it.
325
326### When code has to change
327
328Sometimes raising the version is not enough: the bump fails, or the pull
329request's required checks fail because code must change. Then g1t opens an
330issue and assigns it to [g1t](/guides/working-with-g1t/). That session
331shows as **started by g1t**. This is the only time an agent is involved in
332a security update.
333
334Turn **Security updates** off under **Vulnerabilities** to stop g1t opening
335these pull requests for a project. Alerts are still listed.
336
337## Version updates
338
339g1t can also keep dependencies at their newest versions. Add a
340`dependabot.yml` file (version 2) at `.g1t/dependabot.yml` or
341`.github/dependabot.yml` on the default branch, and g1t opens pull requests
342that raise your npm, Cargo, Go and pip dependencies on the schedule it
343sets. The same file shapes security updates: its `ignore`, `allow`,
344`groups`, `commit-message`, `assignees` and `reviewers` apply to them.
345
346The Security section's **Dependency updates** tab shows the
347file g1t read, any problems in it, each entry's next run and what its last
348run found, and a **Check for updates** button per entry. See
349[Dependency updates](/guides/dependency-updates/).
350
351## The g1t identity
352
353Pull requests, issues and merges that g1t makes itself, such as security
354updates, are authored by `g1t`, shown with the pixel 1 avatar. `g1t` is not
355an account: no one can sign in to it, and the name cannot be registered.
356
357## API and MCP
358
359| Route | What it does | Scope |
360| --- | --- | --- |
361| `GET /repos/{owner}/{name}/security/alerts` | Lists alerts. Filter with `state` (`open`, `dismissed`, `fixed`) and `kind`. | `repo:read` |
362| `POST /repos/{owner}/{name}/security/alerts/{id}/dismiss` | Dismisses an alert, with `reason` and an optional `comment`. | `repo:admin` |
363| `POST /repos/{owner}/{name}/security/alerts/{id}/reopen` | Reopens a dismissed alert. | `repo:admin` |
364
365`reason` is one of the API values in the tables above. Over MCP, the
366[`repository` tool](/reference/mcp/#repository) has the actions
367`security_alerts`, `dismiss_alert` and `reopen_alert`.
368
369Each kind of alert also has its own routes, with filters, under the
370`security:read` and `security:write` scopes: secret scanning, code
371scanning, vulnerability alerts, the dependency graph, settings and the
372workspace's overview, and the [`security` MCP tool](/reference/mcp/#security).
373See [secret protection](/guides/security/secret-protection/#api-and-mcp),
374[code scanning](/guides/security/code-scanning/#api-and-mcp) and the
375[supply chain](/guides/security/supply-chain/#api-and-mcp). The token's scope
376does not replace the role: dismissing still needs Admin for a secret and
377Write for a dependency. See the [API reference](/reference/api/) for the
378fields of each.