Skip to main content
Guides

Secret detection

Keep includes built-in secret detection powered by gitleaks, an open-source library with approximately 160 regex patterns for known credential formats. This guide covers enabling secret detection in redact rules, using hasSecrets() in CEL expressions, and understanding what the detector catches.

What it detects

The gitleaks pattern set covers credentials from major cloud providers, SaaS platforms, and common secret formats:

  • AWS access keys and secret keys
  • GitHub personal access tokens, fine-grained tokens, and OAuth tokens
  • Stripe API keys (live and test)
  • Google Cloud API keys and service account credentials
  • Private keys (RSA, DSA, EC, PGP)
  • Generic API keys, passwords in URLs, and high-entropy strings
  • Tokens for Slack, GitLab, npm, PyPI, Twilio, SendGrid, and many others

The full list is maintained in the gitleaks default config.

Redacting secrets

Add secrets: true to a redact block. The target field is scanned and any detected secrets are replaced with [REDACTED:<rule-id>], where <rule-id> identifies the gitleaks pattern that matched (for example, [REDACTED:aws-access-token]).

rules:
  - name: redact-secrets-in-tool-results
    match:
      operation: "llm.tool_result"
    action: redact
    redact:
      target: "params.content"
      secrets: true

This rule scans every tool result before it reaches the model. If the tool returned text containing AKIAIOSFODNN7EXAMPLE, the model sees [REDACTED:aws-access-token] instead.

Combining with custom patterns

When a redact rule has both secrets: true and custom patterns, gitleaks patterns run first. Custom regex patterns then run on the already-redacted text. This means custom patterns will not interfere with secret placeholders, and you can layer additional redaction on top.

rules:
  - name: redact-all-sensitive
    match:
      operation: "llm.tool_result"
    action: redact
    redact:
      target: "params.content"
      secrets: true
      patterns:
        - match: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"
          replace: "[REDACTED:email]"

Checking for secrets with hasSecrets()

The hasSecrets() CEL function detects secrets without redacting them. It takes a string and returns true if gitleaks finds any matches. This is useful for deny rules that should block calls containing leaked credentials rather than silently redacting them.

rules:
  - name: deny-leaked-credentials
    match:
      operation: "llm.text"
      when: "hasSecrets(params.text)"
    action: deny
    message: "Request contains credentials. Remove secrets before sending."

Example: full rule file

This rule file redacts secrets from both user text and tool results, and logs all LLM operations for auditing:

scope: my-gateway
mode: enforce

rules:
  - name: redact-secrets-in-text
    match:
      operation: "llm.text"
    action: redact
    redact:
      target: "params.text"
      secrets: true

  - name: redact-secrets-in-tool-results
    match:
      operation: "llm.tool_result"
    action: redact
    redact:
      target: "params.content"
      secrets: true

  - name: audit-all
    match:
      operation: "llm.*"
    action: log

Verify it works

Test secret detection by writing a fixture that sends a known secret and expects redaction:

# fixtures/secret-test.yaml
scope: my-gateway
tests:
  - name: "redacts AWS key in tool result"
    call:
      operation: "llm.tool_result"
      params:
        content: "key is AKIAIOSFODNN7REALKEY"
    expect:
      decision: "redact"

Run the fixture with keep test:

$ keep test ./rules --fixtures ./fixtures/secret-test.yaml

The test passes when the redact action fires, replacing the AWS key with [REDACTED:aws-access-token].

Limitations

  • Pattern-based, not semantic. Detection relies on regex patterns matching known credential formats. Secrets that do not match a gitleaks pattern — such as custom internal tokens or credentials with non-standard formats — are not detected.
  • False positives on test data. Strings that happen to match credential formats (for example, test fixtures or documentation examples) may be flagged. Gitleaks has built-in allowlists for known example values like AKIAIOSFODNN7EXAMPLE, but not all test data is excluded.
  • No cross-field analysis. Each field is scanned independently. The detector does not correlate values across multiple fields.
  • Local detection only. Secrets are detected at evaluation time in the Keep process. There is no external service call or database lookup involved.