Naar de hoofdinhoud

Secretscanning

fremforge zoekt op drie plekken met gitleaks naar gecommitte inloggegevens:

WaarWat het doetKan het uit?
Push-beschermingEen pre-receive-hook scant elke push en weigert hem directNee
Controle op pull requestsfremforge/secret-scan laat een pull request die een secret toevoegt falen, ook in archieven en Git LFS-bestandenJa, per organisatie
Doorzoeking van de geschiedenisEén keer per week leest gitleaks git de volledige geschiedenis van elke repository en vermeldt wat al gecommit isNee

Bevindingen uit alle drie staan onder Beveiliging › Bevindingen › Secretscan (/<org>/_admin/security/findings/secrets), en op GET /_app/api/v1/orgs/{org}/code-security/secrets/findings.

Een secret dat al op de branch staat waarin de pull request wordt samengevoegd, laat de controle niet falen; zie Controles op pull requests: alleen nieuwe bevindingen blokkeren. Het wordt nog steeds vermeld, en de wekelijkse doorzoeking en push-bescherming blijven ongewijzigd.

Europese cloudproviders

De eigen regels van gitleaks dekken enkele honderden formaten van inloggegevens, maar geen enkel formaat van de Europese cloudproviders. fremforge voegt daar regels voor toe, en die draaien op alle drie de plekken hierboven:

ProviderRegel-idWat hij vindt
Hetzner Cloudhetzner-cloud-api-tokenEen API-token onder een naam met HCLOUD_ of HETZNER_, waaronder TF_VAR_hcloud_token
Scalewayscaleway-secret-keyEen geheime sleutel onder een naam met SCW_ of SCALEWAY_
Scalewayscaleway-key-pairEen access key gevolgd door zijn secret_key, zoals in ~/.config/scw/config.yaml
OVHcloudovh-application-secretOVH_APPLICATION_SECRET, OVH_CONSUMER_KEY of OVH_CLIENT_SECRET
OVHcloudovh-conf-application-secretapplication_secret, consumer_key of client_secret in een ovh.conf-bestand
IONOS Cloudionos-cloud-tokenEen token onder een naam met IONOS_
T Cloud / Open Telekom Cloudotc-secret-access-keyEen secret access key onder een naam voor een geheime sleutel met OTC_, OS_, HW_ of OBS_, of een naam op *_SK die een daarvan bevat
T Cloud / Open Telekom Cloudotc-terraform-provider-secret-keysecret_key rechtstreeks uitgeschreven in een providerblok opentelekomcloud
T Cloud / Open Telekom Cloudotc-credentials-csvHet bestand credentials.csv dat de console downloadt als je een access key aanmaakt

Deze tokens hebben geen vast voorvoegsel, dus een regel matcht alleen waar de naam van de provider of het bestandsformaat zegt wat de waarde is. Een losse string van 64 tekens of een UUID elders wordt niet gemeld. Placeholders (xxxx…, de nul-UUID) en verwijzingen naar variabelen (${HCLOUD_TOKEN}) worden genegeerd. Voor een inloggegeven dat deze regels niet dekken, voeg je een eigen patroon toe.

Meer detectoren

fremforge voegt ook regels voor deze inloggegevens toe, naast de eigen regels van gitleaks:

ProviderRegel-idWat hij vindtBij het pushen
Supabasesupabase-access-tokenEen persoonlijk toegangstoken, klassiek of met scope, of een OAuth-toegangstoken: sbp_, sbp_oauth_ of sbp_v0_ gevolgd door 40 hexadecimale tekensBlokkeert
Supabasesupabase-secret-keyEen geheime projectsleutel (sb_secret_…), die Row Level Security omzeilt. Publiceerbare sleutels (sb_publishable_…) zijn bedoeld voor browsers en worden niet gemeld, net zomin als de gepubliceerde lokale standaardsleutel van de Supabase CLIBlokkeert
Lovablelovable-api-keyEen API-sleutel van een workspace (lov_…)Blokkeert
Pydanticpydantic-logfire-tokenEen schrijftoken of API-sleutel van Logfire, of een sleutel van de Pydantic AI Gateway (pylf_v…)Blokkeert
Cloudsmithcloudsmith-api-keyEen API-sleutel: 40 hexadecimale tekens onder een Cloudsmith-naam zoals CLOUDSMITH_API_KEYAlleen gemeld

Elke regel matcht het eigen vaste voorvoegsel of formaat van het token, met grenzen voor lengte en entropie, dus hij blokkeert bij het pushen zoals elke andere regel, en de routes voor uitzonderingen en omzeilingen gelden ervoor. Een Cloudsmith-sleutel heeft geen voorvoegsel, dus die regel wordt alleen gemeld. Een Cloudsmith-sleutel die aan een naam als CLOUDSMITH_API_KEY is toegewezen, wordt meestal toch geblokkeerd, door de generieke API-sleutelregel van gitleaks.

Dit zijn alleen detectieregels. fremforge biedt er geen geldigheidscontrole voor.

Wachtwoorden in configuratiebestanden

fremforge-password-config vindt een wachtwoord dat rechtstreeks in een configuratie- of scriptbestand staat, bijvoorbeeld DB_PASSWORD=… in een .env-bestand, password: … in YAML, spring.datasource.password=… of ENV REDIS_PASS=… in een Dockerfile. Hij leest .env-bestanden, YAML, .properties, .ini, .conf, .cfg, .toml, shell-, PowerShell- en batchscripts, Dockerfiles en compose-bestanden. Hij zoekt naar een sleutel die naar een wachtwoord is genoemd (password, passwd, passphrase, pwd, pass) met een kale waarde die de regel afsluit. Hij laat weg:

  • verwijzingen naar een andere waarde (${DB_PASSWORD}, %(password)s, {{ … }});
  • placeholders (changeme, example, xxx);
  • vertaalcatalogi;
  • instellingen die een hash of algoritme noemen (root_password_sha2, password.encoding.alg).

Het is een heuristiek, geen tokenformaat. We hebben hem gemeten op CredData, een openbare, gelabelde set inloggegevens in opensourcerepository’s, tegenover de generieke API-sleutelregel van gitleaks op dezelfde bestanden. Hij was preciezer en vond meer van de echte wachtwoorden, op gegevens die buiten het afstemmen waren gehouden (precisie 0,97 tegenover 0,95, recall 8 % tegenover 5 % van alle gelabelde wachtwoorden). Hij wordt alleen gemeld. Hij leest geen broncode, en een waarde tussen aanhalingstekens in YAML (password: "…") valt er niet onder: die vormen zijn ook gemeten en deden het niet beter dan de generieke regel.

Wachtwoorden gevonden door een classifier (de eigen organisaties van fremforge)

Een tweede stap leest code- en configuratiebestanden op hardgecodeerde wachtwoorden zonder tokenformaat, zoals db_password = "…" in Python of password: "…" in YAML. Die draait vandaag alleen op de eigen organisaties van fremforge. Hij is nog niet beschikbaar voor organisaties van klanten; daarover wordt apart besloten na twee weken aan gegevens over vals-positieven.

Hij werkt in twee stappen, na de regels hierboven, in de controle op pull requests:

  1. Een trefwoordfilter houdt alleen de regels over die een wachtwoord noemen of user:password@ in een URL bevatten, ongeveer 1 % van de regels.
  2. Een machinelearning-classifier geeft elk van die regels een score. Het is het model van CredSweeper (Samsung, MIT-licentie), opnieuw getraind door fremforge. Het draait in de scanner zonder netwerktoegang, en er verlaat geen code of waarde de scanner.

Een bevinding heeft het regel-id fremforge-password-ml. Een bevinding in een testpad (test/, __tests__/, *.test.ts, test_*.py, *_test.go en vergelijkbaar) wordt bewaard met het regel-id fremforge-password-ml-test-path en gelabeld als waarschijnlijk een fixture. Beide worden alleen gemeld.

Hij laat weg:

  • documentatie, databestanden en alles wat geen code of configuratie is;
  • vertaalcatalogi;
  • constanten die een reguliere expressie over wachtwoorden bevatten, zoals PASSWORD_RE;
  • regels die al door een andere regel zijn gemeld.

Als de classifier niet kan draaien of langer dan 120 seconden duurt, meldt de controle zoals gewoonlijk de bevindingen van de andere regels.

Hoe het is gemeten. We hebben het model opnieuw getraind op de ontwikkelrepository’s van CredData, alleen die onder een permissieve licentie, en het één keer gescoord op repository’s die buiten de training waren gehouden. De drempel lag vast voordat die score werd bepaald.

BestandenPrecisieRecall
Code (waarden tussen aanhalingstekens)0,940,98
Configuratie0,960,99

Wachtwoorden in URL’s en in andere bestanden haalden geen precisie van 0,9 en worden niet gemeld. De trainingsrepository’s en hun licenties staan in het bestand NOTICE-training-data.md van de scanner.

Regels die alleen melden

cloudsmith-api-key, fremforge-password-config, fremforge-password-ml en fremforge-password-ml-test-path worden alleen gemeld. Hun bevindingen staan onder Beveiliging › Bevindingen › Secretscan, en die van de twee gitleaks-regels ook in de doorzoeking van de geschiedenis (de classifier draait alleen in de controle op pull requests). In een pull request verschijnen ze in fremforge/secret-scan als advisory (not blocking): ze laten de controle nooit falen en blokkeren nooit een push. Elke andere regel blokkeert.

De instelling

Pull requests blokkeren die een secret toevoegen bepaalt of de controle op pull requests draait. Zolang een organisatie geen keuze heeft opgeslagen, draait de controle en blokkeert ze. Staat de instelling uit, dan draait de controle niet en wordt haar status uit de branchbescherming verwijderd; push-bescherming en de wekelijkse doorzoeking blijven draaien. Stel het in onder Beveiliging › Bevindingen › Secretscan of met PUT /_app/api/v1/orgs/{org}/code-security/secrets/settings en {"enabled": true} (policy:write).

Eigen patronen

Je organisatie kan eigen secretformaten toevoegen: een tokenvorm die alleen jij uitgeeft, een intern sleutelvoorvoegsel, een inloggegeven van een partner. Elk patroon wordt een extra gitleaks-regel op alle drie de plekken hierboven, dus een match wordt geblokkeerd bij het pushen, laat de controle op pull requests falen en wordt gevonden in de geschiedenis. Een bevinding van een eigen patroon heeft het regel-id custom-<name> en toont de naam van het patroon.

Voeg ze toe onder Beveiliging › Beleid › Push-bescherming › Eigen geheimpatronen, of met POST /_app/api/v1/orgs/{org}/code-security/secrets/custom-patterns (policy:write; GET toont de lijst, PUT en DELETE op /custom-patterns/{id} wijzigen en verwijderen er een).

VeldVerplichtWat het doet
namejaWordt bij elke bevinding getoond. Hoogstens 80 tekens.
regexjaRE2-syntaxis, hoogstens 300 tekens.
keywordsneeHoogstens 10. Het patroon draait alleen op inhoud die een ervan bevat, wat pushes snel houdt.
entropynee0 tot 8. Matches met minder willekeur dan dit, zoals placeholders, worden niet gemeld.
allowlist_pathsneeHoogstens 20 padglobs (*, **, ?) waar het patroon niet wordt gemeld. Een glob matcht op elk mapniveau.
repositoryneeEén repository van de organisatie, op naam. Laat het weg voor elke repository.
repository_idneeHetzelfde, op repository-ID. Geef repository of repository_id, niet allebei.
sampleneeEen voorbeeld dat het patroon moet matchen. Wordt bij het opslaan gecontroleerd, nooit bewaard.

Stuur "dry_run": true mee om te valideren zonder op te slaan.

Eén repository of allemaal. Een patroon zonder repository geldt voor elke repository van de organisatie. Een patroon met een repository geldt alleen voor die repository, op alle drie de plekken, samen met de patronen voor de hele organisatie. Kies in de console de repository uit de lijst, of Alle repository’s. Het patroon is gekoppeld aan de repository zelf, niet aan de naam: als je de repository hernoemt, blijft het patroon erop staan. Een repository die is verwijderd en later onder dezelfde naam opnieuw is aangemaakt, is een nieuwe repository en erft het patroon niet. Een patroon waarvan de repository is verwijderd of naar een andere organisatie is verplaatst, geldt nergens en wordt zo getoond totdat je het wijzigt of verwijdert. Antwoorden bevatten zowel repository_id als repository, de huidige naam van de repository.

# Validate only
curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "Acme deploy key", "regex": "acme_live_[A-Za-z0-9]{24}", "sample": "KEY=acme_live_abcdefghijklmnopqrstuvwx", "dry_run": true}'
# {"valid": true, "rule_id": "custom-acme-deploy-key"}

# Create (201)
curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "Acme deploy key", "regex": "acme_live_[A-Za-z0-9]{24}", "keywords": ["acme_live_"], "allowlist_paths": ["test/fixtures/**"]}'

# List
curl -sS "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN"

# Replace (the whole pattern) and delete
curl -sS -X PUT "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns/$ID" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "Acme deploy key", "regex": "acme_live_[A-Za-z0-9]{32}"}'
curl -sS -X DELETE "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns/$ID" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN"

Een geweigerd patroon krijgt 400 met een problem+json-body waarvan het type de reden noemt: invalid-repository, invalid-regex, regex-too-complex, regex-matches-empty, regex-too-broad, sample-not-matched, invalid-name, invalid-keywords, invalid-entropy of invalid-allowlist-paths. Een tweede patroon met dezelfde naam krijgt 409 duplicate-name, een 101e too-many-patterns, en een 21e zonder trefwoorden too-many-without-keywords.

Met de Terraform/OpenTofu-provider:

resource "fremforge_secret_custom_pattern" "acme_deploy_key" {
  org             = "acme"
  name            = "Acme deploy key"
  regex           = "acme_live_[A-Za-z0-9]{24}"
  keywords        = ["acme_live_"]
  allowlist_paths = ["test/fixtures/**"]
  # repository    = "infra"   # one repository only (provider 0.7.0 and later)
}

Waarom alleen RE2. Een patroon draait in de push-hook bij elke push. RE2 kent geen backtracking, dus de looptijd groeit lineair met de invoer en geen enkel patroon kan een push laten vastlopen. Backreferences (\1) en lookarounds ((?=, (?!, (?<=) hebben backtracking nodig en worden geweigerd. Een patroon wordt ook geweigerd als het:

  • een lege string matcht, of gewone tekst zoals const x = 1;;
  • meer dan 256 keer herhaalt ({n,m}), of groepen meer dan 6 niveaus diep nest.

Een patroon testen voordat je het opslaat

Testen op repository’s in het toevoegformulier, of de dry-run-API, laat een kandidaatpatroon los op de laatste commit van de standaardbranch van elke repository en toont waar het zou matchen. Er wordt niets opgeslagen: het patroon wordt niet bewaard, er ontstaat geen bevinding, en het resultaat blijft 24 uur bewaard.

Het toont de repository, het bestand, de regel, de commit en de vingerafdruk van elke match, nooit de gematchte tekst. Elke repository wordt één keer geteld, in een van drie groepen:

  • gecontroleerd;
  • niet te controleren: klonen mislukt, time-out, scan mislukt, of niet bereikt binnen de limiet van 10 minuten;
  • overgeslagen: gearchiveerd, een mirror, leeg, te groot (meer dan 100 MB, of een checkout van meer dan 256 MB), of boven de limiet van 200 repository’s.

Een patroon dat tot één repository is beperkt, wordt alleen op die repository getest.

# Start one (202); the body is the same as creating a pattern
curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns/dry-runs" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "Acme deploy key", "regex": "acme_live_[A-Za-z0-9]{24}", "keywords": ["acme_live_"]}'
# {"id": "…", "status": "queued", …}

# Read it back until status is done (or failed)
curl -sS "https://frem.sh/_app/api/v1/orgs/$ORG/code-security/secrets/custom-patterns/dry-runs/$ID" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN"
# {"status": "done", "repositories": {"total": 118, "scanned": 70, "not_checked": 48},
#  "matches": [{"repository": "web", "path": "config/partner.txt", "line": 1, "commit": "…", "fingerprint": "…"}], …}

Beide vereisen policy:write, omdat het resultaat bestanden noemt in elke repository van de organisatie. Per organisatie draait er één dry run tegelijk, en er kan er één per 10 minuten starten. Een run die binnen 2 minuten door geen scanner wordt opgepakt, eindigt als failed met error: expired; start hem een paar minuten later opnieuw. Het starten wordt in het auditlog vastgelegd als code-security.secret_scan.custom_pattern.dry_run_started. Een geweigerde start krijgt dezelfde types als het aanmaken van een patroon, of dry-run-in-progress (409), dry-run-cooldown (429) of dry-run-unavailable (503).

Limieten. Een organisatie kan hoogstens 100 patronen hebben, die voor één repository meegerekend. Hoogstens 20 daarvan mogen geen keywords hebben. Het antwoord van de lijst geeft beide als limit en limit_without_keywords.

De reden is de duur van een push. Een patroon met trefwoorden draait alleen op inhoud die een ervan bevat, en kost dus bijna niets. Een patroon zonder trefwoorden draait op elk stuk van elke push. Gemeten met onze push-hook maakten 200 patronen zonder trefwoorden van een push van 100 commits er 17 seconden van in plaats van ongeveer 2. Een trefwoord is meestal het vaste voorvoegsel van het secret.

Het opslaan of verwijderen van een patroon zet een nieuwe scan van de volledige geschiedenis van elke repository in de wachtrij, zodat een nieuw patroon ook vindt wat al gecommit is. Dat duurt meestal tot 15 minuten. Wijzigingen worden in het auditlog vastgelegd als code-security.secret_scan.custom_pattern.created, .updated en .deleted.

Als de patronen bij het pushen niet kunnen worden geladen, bijvoorbeeld als de api even onbereikbaar is, wordt de push toch gescand met de ingebouwde regels, en legt een auditregel vast dat de eigen patronen niet zijn toegepast. De doorzoeking van de geschiedenis vindt dan wat is gemist zodra het de standaardbranch bereikt.

Geldigheidscontroles

Een secret dat nog werkt, is urgenter dan een dat al is geroteerd. Een geldigheidscontrole vraagt de dienst die een gevonden secret uitgaf of het nog actief is, en labelt de bevinding als Actief, Inactief of Onbekend. Actieve bevindingen staan bovenaan, in de console en in de API (validity, validity_checked_at).

Elke provider is een eigen instelling onder Beveiliging › Beleid › Push-bescherming › Geldigheidscontroles, of PUT /_app/api/v1/orgs/{org}/code-security/secrets/validity-settings/{provider} met {"enabled": true} (policy:write). Voordat je er een aanzet, noemt de pagina de host die de controle benadert en of de provider Door de VS beheerd, Door de EU beheerd, Door het VK beheerd of Door Canada beheerd wordt. De API meldt hetzelfde als operator_jurisdiction (us, eu, uk, ca, of null voor de interne controle). Het Verenigd Koninkrijk en Canada hebben allebei een adequaatheidsbesluit van de EU; geen enkele provider die vanuit een ander land buiten de EU wordt beheerd, wordt aangeboden op GET .../validity-settings.

Deze tabel is de lijst met providers waarnaar de verwerkersovereenkomst (bijlage B §B.10) verwijst. fremforge biedt alleen een controle aan voor een provider in deze lijst, telkens met één alleen-lezen verzoek naar een vaste host, of, waar een provider zijn sleutels vanaf regionale hosts bedient (SendGrid, Mailgun, Datadog, Intercom, Postman, Square, HCP Terraform, Typeform), naar een vaste lijst hosts die op volgorde worden bevraagd; je kunt geen eigen URL toevoegen. Een provider die later wordt toegevoegd, staat eerst uit.

ProviderGecontroleerde regelsWaar de controle heen gaatHet verzoekBeheerdStandaard
fremshAPI-tokens van frem.sh (ffp_)Binnen frem.sh. Geen externe aanroepDe hash van het token tegen de tokenopslag van frem.sh—Aan
githubGitHub-tokensapi.github.comGET /rate_limitDoor de VS beheerdUit
gitlabPersoonlijke toegangstokens van GitLab.com (een token van een zelfbeheerde server leest Onbekend)gitlab.comGET /api/v4/personal_access_tokens/selfDoor de VS beheerdUit
awsAWS-access keyssts.amazonaws.comGetCallerIdentity, waarvoor geen rechten nodig zijnDoor de VS beheerdUit
slackSlack-tokensslack.comPOST /api/auth.test, zonder bodyDoor de VS beheerdUit
stripeStripe-API-sleutelsapi.stripe.comGET /v1/balanceDoor de VS beheerdUit
hetznerHetzner Cloud-API-tokensapi.hetzner.cloudGET /v1/locationsDoor de EU beheerdUit
scalewayGeheime sleutels van Scalewayapi.scaleway.comGET /account/v3/projects?page_size=1Door de EU beheerdUit
ionosIONOS Cloud-API-tokensapi.ionos.comGET /cloudapi/v6/Door de EU beheerdUit
npmnpm-toegangstokensregistry.npmjs.orgGET /-/whoamiDoor de VS beheerdUit
openaiOpenAI-API-sleutelsapi.openai.comGET /v1/modelsDoor de VS beheerdUit
anthropicAnthropic-API-sleutels (geen Admin API-sleutels)api.anthropic.comGET /v1/modelsDoor de VS beheerdUit
huggingfaceHugging Face-toegangstokens (geen verouderde api_org_-tokens)huggingface.coGET /api/whoami-v2Door de VS beheerdUit
digitaloceanDigitalOcean-tokensapi.digitalocean.comGET /v2/accountDoor de VS beheerdUit
sendgridSendGrid-API-sleutelsapi.sendgrid.com, daarna api.eu.sendgrid.com, tot er een antwoordtGET /v3/scopesDoor de VS beheerdUit
mailgunPrivé-API-sleutels van Mailgunapi.mailgun.net, daarna api.eu.mailgun.net, tot er een antwoordtGET /v3/domainsDoor de VS beheerdUit
datadogDatadog-API-sleutelsDe zes sites van Datadog in deze volgorde, tot er een antwoordt: api.datadoghq.com, api.datadoghq.eu, api.us3.datadoghq.com, api.us5.datadoghq.com, api.ap1.datadoghq.com, api.ddog-gov.comGET /api/v1/validateDoor de VS beheerdUit
azuredevopsPersoonlijke toegangstokens van Azure DevOps. Alleen Actief of Onbekendapp.vssps.visualstudio.comGET /_apis/profile/profiles/meDoor de VS beheerdUit
ovhAPI-inloggegevens van OVHcloud. Alleen Actief of Onbekendeu.api.ovh.comGET /1.0/auth/currentCredential, ondertekendDoor de EU beheerdUit
adafruitAdafruit IO-sleutelsio.adafruit.comGET /api/v2/userDoor de VS beheerdUit
airtablePersoonlijke toegangstokens van Airtableapi.airtable.comGET /v0/meta/whoamiDoor de VS beheerdUit
asanaPersoonlijke toegangstokens van Asanaapp.asana.comGET /api/1.0/users/meDoor de VS beheerdUit
dopplerPersoonlijke tokens van Dopplerapi.doppler.comGET /v3/meDoor de VS beheerdUit
dropboxDropbox-toegangstokensapi.dropboxapi.comPOST /2/users/get_current_account, dat het account leestDoor de VS beheerdUit
easypostEasyPost-API-sleutelsapi.easypost.comGET /v2/addresses?page_size=1Door de VS beheerdUit
fastlyFastly-API-tokensapi.fastly.comGET /tokens/selfDoor de VS beheerdUit
herokuHeroku-API-sleutelsapi.heroku.comGET /accountDoor de VS beheerdUit
intercomIntercom-toegangstokensapi.intercom.io, daarna api.eu.intercom.io, daarna api.au.intercom.io, tot er een antwoordtGET /meDoor de VS beheerdUit
lobLob-API-sleutelsapi.lob.comGET /v1/addresses?limit=1Door de VS beheerdUit
notionIntegratietokens van Notionapi.notion.comGET /v1/users/meDoor de VS beheerdUit
postmanPostman-API-sleutelsapi.getpostman.com, daarna api.eu.postman.com, tot er een antwoordtGET /meDoor de VS beheerdUit
readmeReadMe-API-sleutelsdash.readme.comGET /api/v1Door de VS beheerdUit
rubygemsRubyGems-API-sleutelsrubygems.orgGET /api/v1/gems.jsonDoor de VS beheerdUit
shippoShippo-API-tokensapi.goshippo.comGET /addresses/?results=1Door de VS beheerdUit
squareSquare-toegangstokensconnect.squareup.com, daarna connect.squareupsandbox.com, tot er een antwoordtGET /v2/merchants/meDoor de VS beheerdUit
brevoBrevo-API-sleutels (Sendinblue)api.brevo.comGET /v3/accountDoor de EU beheerdUit
gocardlessGoCardless-toegangstokens (live)api.gocardless.comGET /creditors?limit=1Door het VK beheerdUit
duffelDuffel-toegangstokensapi.duffel.comGET /air/airlines?limit=1Door het VK beheerdUit
cohereCohere-API-sleutelsapi.cohere.comPOST /v1/check-api-key, de eigen sleutelcontrole van CohereDoor Canada beheerdUit
hcpterraformAPI-tokens van HCP Terraform. Alleen Actief of Onbekendapp.terraform.io, daarna app.eu.terraform.io, tot er een antwoordtGET /api/v2/account/detailsDoor de VS beheerdUit
pulumiPulumi-toegangstokens. Alleen Actief of Onbekendapi.pulumi.comGET /api/userDoor de VS beheerdUit
sentryGebruikerstokens van Sentry. Alleen Actief of Onbekendsentry.ioGET /api/0/Door de VS beheerdUit
messagebirdMessageBird-toegangssleutels. Alleen Actief of Onbekendrest.messagebird.comGET /balanceDoor de EU beheerdUit
typeformPersoonlijke toegangstokens van Typeform. Alleen Actief of Onbekendapi.typeform.com, daarna api.eu.typeform.com, tot er een antwoordtGET /meDoor de EU beheerdUit

Persoonlijke toegangstokens van Azure DevOps, Datadog-API-sleutels en persoonlijke toegangstokens van Asana worden gevonden door drie regels van fremforge, azure-devops-pat, datadog-api-key en asana-personal-access-token, die op alle drie de plekken hierboven draaien.

Sommige controles kunnen aantonen dat een secret actief is, nooit dat het dat niet is. Azure DevOps weigert een actief token dat tot één organisatie is beperkt of de profile-scope mist, dus een weigering daar leest Onbekend, nooit Inactief. Een verzoek aan OVHcloud moet worden ondertekend met de application key, het application secret en de consumer key samen. De controle draait alleen als de doorzoeking van de geschiedenis alle drie in hetzelfde bestand vindt, zoals een ovh.conf, en elk ander antwoord dan succes leest Onbekend. Tokens van HCP Terraform, Pulumi en Sentry kunnen horen bij een zelf gehoste Terraform Enterprise, Pulumi of Sentry, en worden dan door de openbare dienst geweigerd. Sleutels van het nieuwere Bird-platform kunnen worden geweigerd door de oorspronkelijke API van MessageBird. Een Typeform-token zonder de account-scope kan op dezelfde manier worden geweigerd als een dood token. Daarom lezen deze vijf Actief of Onbekend, nooit Inactief.

Een secret is alleen inactief als elke host dat zegt. Een SendGrid-sleutel die aan de EU-regio is gebonden, faalt op de globale host, een Datadog-sleutel hoort bij een van de sites van Datadog, en een sleutel van Intercom, Postman of Typeform bij een van hun regio’s; een sandbox-token van Square faalt in productie. Daarom bevraagt de controle de hosts in de getoonde volgorde en stopt bij de eerste die antwoordt dat het secret actief is. Ze leest alleen Inactief als elke host het weigert.

Geen automatische controle: Atlassian, Twilio, Flutterwave, Mailchimp, Telegram, zelfbeheerde GitLab en T Cloud. Hun bevindingen hebben alleen Zelf controleren. Een Atlassian-token heeft ook de site en het e-mailadres van het account nodig, en een Twilio-API-sleutel zijn SID. De Flutterwave-regel vindt alleen testsleutels. Een Mailchimp-sleutel noemt zijn eigen datacenterhost, dus er is geen vaste host om te controleren. Telegram wordt beheerd vanuit een land buiten de EU, het VK, Canada en de VS. Een zelfbeheerde GitLab-server, of een andere host die jij kiest, is een bestemming waarmee fremforge niet namens jou contact opneemt. T Cloud is de cloud waarop fremforge zelf draait, en de identiteitsdienst ervan is bewust niet bereikbaar voor de controles van fremforge. Voor een T Cloud-sleutel vraagt het commando OBS om je buckets te tonen: een onbekende sleutel krijgt InvalidAccessKeyId.

Een sleutel die actief is maar de leesactie van de controle niet mag uitvoeren, telt als Actief: een Stripe-sleutel die het saldo wordt geweigerd, een Scaleway-sleutel die de projecten wordt geweigerd, een Anthropic-sleutel die wordt geweigerd met permission_error, een OpenAI-sleutel die wordt geweigerd wegens ontbrekende rechten, een Dropbox-token dat wordt geweigerd met missing_scope, of een sleutel van Heroku, RubyGems, Square, Asana, GoCardless of Duffel die wordt geweigerd met 403. Voor de providers die op 2026-10-24 zijn toegevoegd, leest alleen het exacte antwoord dat elk ervan geeft op een sleutel die niet bestaat Inactief; elk ander antwoord leest Onbekend. invalid_api_key van OpenAI is het enige antwoord van OpenAI dat als Inactief wordt gelezen. Admin API-sleutels van Anthropic en verouderde organisatietokens van Hugging Face worden niet gecontroleerd, omdat de endpoints hierboven niet kunnen zien of ze actief zijn.

Hoe een controle werkt, en wat er wordt opgeslagen. Elke controle is alleen-lezen. Ze vraagt van wie een token is of of het geldig is, en verandert nooit iets bij de provider. Het secret wordt nog steeds nooit opgeslagen. Terwijl de doorzoeking van de geschiedenis een repository leest, versleutelt de scanner elke gematchte waarde met een sleutel die de api voor die ene scan aanmaakt. De api ontsleutelt de waarde, voert de controle uit, bewaart alleen de uitkomst en het tijdstip van de controle, en gooit de waarde weg. De sleutel wordt verwijderd als de scan eindigt. De scanner zelf heeft geen netwerktoegang tot de providers.

Een frem.sh-token wordt gecontroleerd tegen de eigen tokenopslag van frem.sh, en de status ervan wordt live gelezen: als je het token intrekt, staat de bevinding direct op Inactief. Voor de andere providers komt de status van de laatste doorzoeking die het secret las.

Onbekend betekent dat de provider geen duidelijk antwoord gaf, bijvoorbeeld door een time-out of een rate limit, een AWS-sleutel waarvan de geheime helft niet binnen vijf regels ervan is gevonden, of OVHcloud-inloggegevens waarvan de drie delen niet samen zijn gevonden. Onbekend wordt nooit als Inactief getoond.

Een provider aanzetten scant de geschiedenis van elke repository opnieuw, omdat de waarde van een gevonden secret alleen bestaat zolang de doorzoeking die leest. Een provider uitzetten wist de uitkomsten van die provider. Wijzigingen worden in het auditlog vastgelegd als code-security.secret_scan.validity.updated.

Zelf controleren, en dan markeren

Je hoeft geen geldigheidscontrole aan te zetten om te weten of een secret nog werkt. Voor een secret van een van de providers hierboven, of van Atlassian, Twilio, Flutterwave, Mailchimp, Telegram, een zelfbeheerde GitLab-server of T Cloud, heeft de bevinding Zelf controleren: het eigen alleen-lezen verzoek van de provider als commando om in je terminal uit te voeren, bijvoorbeeld voor een GitHub-token:

read -rs TOKEN && curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOKEN" https://api.github.com/rate_limit

Het commando bevat nooit het secret. Het wacht tot je het typt of plakt, toont het niet en houdt het uit je shellgeschiedenis. Het gaat alleen naar de provider, nooit naar fremforge. De bevinding zegt wat het commando afdrukt als het secret actief is (hier 200) en als het dat niet is (401).

Leg daarna vast wat je vond met Markeren als actief of Markeren als geroteerd in het menu van de bevinding, of met PUT /_app/api/v1/orgs/{org}/code-security/secrets/findings/{id}/mark en {"mark": "active"}, {"mark": "rotated"} of {"mark": null} (findings:write). Een bevinding die als actief is gemarkeerd, staat bij de actieve. Elke locatie wordt apart gemarkeerd: een markering hoort bij één bevinding (één bestand, regel en commit) en blijft behouden over doorzoekingen van de geschiedenis heen, net als het negeren van een bevinding. Is hetzelfde secret in twee commits gevonden, markeer dan beide. fremforge bewaart niets wat van het secret is afgeleid en de locaties aan elkaar zou kunnen koppelen. Een markering negeert de bevinding niet. Elke markering wordt in het auditlog vastgelegd als code-security.secrets.marked. De bevindingen-API geeft de markering terug als mark en het commando als self_check.

Standaard roteren, en welke eerst

Behandel elk secret dat hier wordt gevonden als gecompromitteerd, en roteer het, tenzij de provider of jij hebt bevestigd dat het niet meer werkt. Een secret uit de code verwijderen trekt het niet in: het blijft in de geschiedenis staan, waar iedereen met leesrechten het kan vinden.

Om te helpen bepalen welke je eerst roteert als geen provider heeft geantwoord, worden bevindingen gesorteerd op hoe waarschijnlijk het is dat het secret nog actief is. De schatting gebruikt alleen wat fremforge al weet, nooit het secret zelf, en neemt met niemand contact op:

SignaalEffect
Exact dezelfde regel staat nog in de huidige code van de standaardbranchWaarschijnlijk actief
Push-bescherming heeft in de afgelopen 90 dagen hetzelfde soort secret in deze repository geblokkeerdWaarschijnlijk actief
De regel is later op de standaardbranch gewijzigd of verwijderdMogelijk actief: verwijderen heeft het niet ingetrokken
De commit staat alleen op een andere branch dan de standaardbranchMogelijk actief
Gecommit in de afgelopen 90 dagenVerhoogt de schatting
Meer dan twee jaar geleden gecommit, en niet in de huidige codeMinder waarschijnlijk actief

Het eigen antwoord van een provider (Actief of Inactief) en je eigen markering gaan altijd voor. De schatting wordt getoond, met hoogstens twee van de redenen, bij elke bevinding waarop geen provider heeft geantwoord en die je niet hebt gemarkeerd. De signalen uit de code worden vastgelegd door de wekelijkse doorzoeking van de geschiedenis, dus een bevinding die vóór de doorzoeking die ze toevoegde is gevonden, toont tot de volgende doorzoeking alleen het signaal van push-bescherming. De bevindingen-API geeft de schatting terug als likely_live (level en reasons), samen met committed_at. Ze is null als een provider heeft geantwoord of de bevinding is gemarkeerd. Het is een schatting, geen controle: “Minder waarschijnlijk actief” betekent nog steeds roteren.

Wat er wordt opgeslagen

Alleen een vingerafdruk van de match wordt opgeslagen, nooit de gematchte tekst. De regel, het pad, het regelnummer en de commit zijn genoeg om het bestand bij die commit te openen. Een geldigheidscontrole voegt de uitkomst en het tijdstip toe. Voor een frem.sh-token wordt ook het id van het bijbehorende tokenrecord opgeslagen, nooit het token zelf.

Als er een secret wordt gevonden

Roteer het inloggegeven eerst bij de bron. De regel verwijderen maakt het lek niet ongedaan: wie de geschiedenis heeft, heeft de waarde nog. Haal het daarna uit de code: Fix-PR openen in de kolom Fix van de bevinding verplaatst een inloggegeven in Go, Python, JavaScript of TypeScript naar een omgevingsvariabele, zonder AI; zie Fixes op basis van regels. De pull request verwijdert de waarde niet uit de geschiedenis, dus roteren komt hoe dan ook eerst.

Zie ook