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