Secret protection
Custom secret patterns, pushing past push protection with a reason, delegated bypass, validity checks, and every place a secret was found.
Secret scanning finds keys and tokens in what you push and in your history; Security covers the built-in formats, push protection and dismissing alerts. This page covers what goes further: patterns of your own, getting a blocked push through with a reason, having owners approve that, and asking a secret’s issuer whether it still works.
| Feature | Public repositories | Private repositories |
|---|---|---|
| Secret scanning and push protection | Free | Free |
| Bypass with a reason | Free | Free |
| Custom patterns | Free | Security and quality activation |
| Delegated bypass | Free | Security and quality activation |
| Validity checks | Free | Security and quality activation |
See What’s free and what’s paid.
The Secret scanning page
Section titled “The Secret scanning page”g1t.sh/<owner>/<project>/security/secret-scanning lists the
repository’s secret alerts. Filter them by:
| Filter | Values |
|---|---|
| State | Open (in the history, or blocked at a push), Dismissed, Fixed |
| Type | Each kind of secret found, custom patterns by name |
| Validity | Active, Inactive, Unknown, No check |
| Bypassed | Bypassed, Not bypassed |
Each alert has its own page with every place the secret was found (file, line and commit), what happened to it, its bypass requests, and its actions: Dismiss or Reopen (Admin), Check with its issuer, Fix with g1t and, for a blocked push, Bypass.
A secret is one alert however many places hold it: the same value found in another file or another commit adds a location, not an alert.
Custom patterns
Section titled “Custom patterns”A custom pattern is a secret format of your own, such as your company’s internal API keys. Published, push protection refuses pushes that add a match and the default branch’s history is scanned again for it, exactly as for the built-in formats.
A repository’s patterns are under Custom patterns on its Secret
scanning page; a workspace’s, which cover every repository in it, are at
g1t.sh/<owner>/-/security/patterns. A repository is scanned with its own
patterns and its workspace’s, up to 100 in all.
To add one:
- Open New pattern.
- Give it a Name, such as
Acme API key. - Write the Secret format as a regular expression:
acme_[a-z0-9]{32}. - Optionally, say what must come right before and after the secret, also as regular expressions. Without them, the secret must start at the beginning of a line or after a character that is not a letter or digit, and end the same way.
- Add test strings, one a line, and Save as draft: each test string shows where the pattern matched.
- Dry run it: the pattern runs over the default branch (up to 2,000 files and 20 MB) and lists what it would find, the matches masked. Nothing is recorded. A workspace’s dry run reads up to ten of its repositories.
- Publish it.
Patterns use the Rust regex syntax. Every pattern runs in time linear in the text it reads, so no pattern can stall a push: there is no look-around and there are no back-references, which are what make other engines slow on some inputs. A pattern is refused, with the reason, when it:
- is longer than 1,000 characters,
- does not compile, or compiles to more than 1 MB,
- matches an empty string.
Matching is per line: a secret that spans lines is not found. Lines longer
than 4,000 characters (minified code) and lockfiles are skipped, as for the
built-in formats. A line with g1t:allow-secret on it is never reported.
Changing a pattern’s format while it is published scans the history again too. Deleting one keeps the alerts it found. Repository patterns take the Admin role; workspace patterns, an owner.
Pushing past push protection
Section titled “Pushing past push protection”When push protection refuses a push, git’s message has a link for each secret, to its alert:
remote: If it is not a real secret (a test fixture), or you will rotate it later:remote: - add g1t:allow-secret in a comment on its line, orremote: - bypass it with a reason at https://g1t.sh/acme/rocket/security/secret-scanning/sec_…remote: A bypass is recorded with your name and reason (or goes to an ownerremote: to approve, if your workspace asks), then the same push goes through.On the alert, choose Bypass push protection with a reason:
| Reason | API value | The alert |
|---|---|---|
| It’s a false positive | false_positive |
Closed as a false positive |
| It’s used in tests | used_in_tests |
Closed as used in tests |
| I’ll fix it later | will_fix_later |
Stays open, to be rotated; it counts as critical once it lands |
Then push again, unchanged. Anyone with Write may bypass. The bypass is
recorded on the alert (who, when, the reason and your comment) and in the
workspace’s audit log as
secret_scanning.bypass.
Delegated bypass
Section titled “Delegated bypass”With Delegated bypass on, in the workspace’s Security settings
(g1t.sh/<owner>/-/security/settings), someone who pushed a blocked
secret asks instead: Ask to bypass records a request, and the
workspace’s owners are told in their inbox. The
workspace’s owners and the repository’s admins review requests at
g1t.sh/<owner>/-/security/bypass-requests:
- Open the request.
- Approve or Deny, with a comment if you like.
- An approved request bypasses push protection as its requester asked; they are told and push again.
Nobody reviews their own request. Owners and admins bypass directly, as
without delegation. Requesters can Cancel request. Each step is in the
alert’s activity and the audit log (secret_scanning.bypass_requested,
secret_scanning.bypass_reviewed).
Validity checks
Section titled “Validity checks”With Validity checks on in the workspace’s Security settings, g1t asks a secret’s issuer whether it still works, and marks the alert:
| Validity | Meaning |
|---|---|
| Active | The issuer accepted it. Rotate it. |
| Inactive | The issuer refused it: revoked, expired or never real. |
| Unknown | The issuer could not say (an error, a rate limit), or the secret never landed, so it cannot be read again. |
| No check | There is no safe way to ask about this kind of secret. |
Each check is the issuer’s own read-only identity call, made over HTTPS by the service that read the secret from your repository. The secret goes nowhere else, and is never stored:
| Secret | Check |
|---|---|
| GitHub token | GET https://api.github.com/user |
| GitLab personal access token | GET https://gitlab.com/api/v4/personal_access_tokens/self |
| Stripe live key | GET https://api.stripe.com/v1/balance (a restricted key without access to it is still active) |
| Slack token | POST https://slack.com/api/auth.test |
| npm token | GET https://registry.npmjs.org/-/whoami |
| OpenAI API key | GET https://api.openai.com/v1/models |
| Anthropic API key | GET https://api.anthropic.com/v1/models |
| SendGrid key | GET https://api.sendgrid.com/v3/scopes |
Other formats have no check: an AWS access key cannot be checked without its secret key, a Slack webhook address would post a message, and the rest have no read-only identity call. Custom pattern matches are never checked.
Open alerts are checked again weekly, and on request with Check with its issuer on the alert.
Fix with g1t
Section titled “Fix with g1t”Fix with g1t on a secret alert opens an issue for g1t to take the secret out of the code and read it from configuration instead (an environment variable, or the repository’s Actions secrets). Its pull request lands through your required checks. Taking a secret out of the code does not make it safe: it is in the history, so whoever owns it still rotates it with its issuer and marks the alert Revoked. The agent’s run is charged as agent usage.
API and MCP
Section titled “API and MCP”| Route | What it does | Scope |
|---|---|---|
GET /repos/{owner}/{name}/secret-scanning/alerts |
Lists alerts; filter with state, secret_type, validity, bypassed. |
security:read |
GET /workspaces/{workspace}/secret-scanning/alerts |
The same across a workspace. | security:read |
GET /repos/{owner}/{name}/secret-scanning/alerts/{id} |
One alert with its locations, activity and requests. | security:read |
PATCH /repos/{owner}/{name}/secret-scanning/alerts/{id} |
Dismisses (state dismissed, reason, comment) or reopens (state open). Admin. |
security:write |
GET /repos/{owner}/{name}/secret-scanning/alerts/{id}/locations |
Where the secret was found. | security:read |
POST /repos/{owner}/{name}/secret-scanning/alerts/{id}/bypass |
Bypasses push protection with reason, or asks to. |
security:write |
POST /repos/{owner}/{name}/secret-scanning/alerts/{id}/validity |
Checks with the issuer. | security:write |
GET /workspaces/{workspace}/secret-scanning/bypass-requests |
Bypass requests; filter with state, repo. |
security:read |
PATCH /workspaces/{workspace}/secret-scanning/bypass-requests/{id} |
decision approve, deny or cancel. |
security:write |
GET, POST /repos/{owner}/{name}/secret-scanning/custom-patterns |
Lists or creates patterns (also under /workspaces/{workspace}). |
security:read, security:write |
PATCH, DELETE …/custom-patterns/{id} |
Changes, publishes or deletes one. | security:write |
POST …/custom-patterns/dry-run |
Runs a pattern over the default branch without saving it. | security:write |
Over MCP, the security tool has the actions
secret_alerts, secret_alert, update_secret_alert, secret_locations,
bypass, check_validity, bypass_requests, review_bypass, patterns,
create_pattern, update_pattern, delete_pattern and
dry_run_pattern. g1t’s own agents never bypass, dismiss or change
patterns. Webhooks: secret_scanning_alert.created, .fixed,
.dismissed, .reopened, and secret_scanning.bypass_requested,
secret_scanning.bypass_reviewed; see Webhooks.