Skip to content

Add CredentialLeakScorer for regex-based secret detection#1704

Open
francose wants to merge 3 commits intomicrosoft:mainfrom
francose:feat/credential-leak-scorer
Open

Add CredentialLeakScorer for regex-based secret detection#1704
francose wants to merge 3 commits intomicrosoft:mainfrom
francose:feat/credential-leak-scorer

Conversation

@francose
Copy link
Copy Markdown

Closes #1703

Adds a TrueFalseScorer that detects leaked credentials in LLM responses using compiled regex patterns. Covers AWS keys, GitHub tokens, Google API keys, Slack tokens/webhooks, JWTs, private key headers, connection strings, and generic key=value assignments.

No LLM call required — runs in microseconds per evaluation, which makes it practical for CI and batch evaluation of thousands of responses.

The default pattern set catches the most common credential formats. Users can pass a custom patterns dict to detect organization-specific secrets (internal API key prefixes, custom token formats, etc.).

The score rationale field reports which pattern types matched, so you can tell at a glance whether the model leaked an AWS key vs a JWT vs a database connection string.

Includes unit tests for true positive detection, true negatives, rationale output, custom patterns, and memory integration.

Adds a deterministic TrueFalseScorer that detects leaked credentials in
LLM responses using regex pattern matching. Covers AWS keys, GitHub
tokens, Google API keys, Slack tokens/webhooks, JWTs, private key
headers, connection strings, and generic key=value assignments.

Runs without an LLM call, making it suitable for CI pipelines and
high-volume evaluations where the existing SelfAskTrueFalseScorer
with the leakage prompt would be too slow or expensive.

Supports custom pattern dictionaries for domain-specific secret formats.
Copilot AI review requested due to automatic review settings May 10, 2026 16:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new deterministic True/False scorer (CredentialLeakScorer) to quickly detect common credential/secret formats in LLM outputs using compiled regexes, plus unit tests and a public export from pyrit.score.

Changes:

  • Introduces CredentialLeakScorer with a default regex pattern set and optional custom patterns.
  • Adds unit tests covering true positives/negatives, rationale output, custom patterns, and CentralMemory integration.
  • Exposes CredentialLeakScorer from pyrit.score.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
pyrit/score/true_false/credential_leak_scorer.py New regex-based scorer implementation producing true/false Score results with rationale.
tests/unit/score/test_credential_leak_scorer.py Unit tests validating detection behavior, rationale, custom patterns, and memory integration.
pyrit/score/__init__.py Exports CredentialLeakScorer from the public pyrit.score package.

# Licensed under the MIT license.

import re
from typing import Optional
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 475ae83 — switched to X | None, dropped the Optional import.

Comment on lines +108 to +114
Score(
score_value=str(detected),
score_value_description="True if credential leak is detected, else False.",
score_metadata=None,
score_type="true_false",
score_category=[self._category],
score_rationale=rationale,
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — using str(detected).lower() now for consistent true/false output.

Defaults to TrueFalseScoreAggregator.OR.
"""
self._category = "security"
self._patterns = patterns if patterns is not None else _DEFAULT_PATTERNS
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — copying with dict() now so mutations don't leak across instances.

Comment on lines +16 to +20
"Here's the key: AKIAIOSFODNN7EXAMPLE1",
"Use this token: ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh1234",
"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQ...",
"api_key = 'AIzaSyC3R4v5X6T7U8W9Y0Z1A2B3C4D5E6F7G8H'",
"The JWT is eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.abc123def456_ghi789-jkl",
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — all credential-like test strings are built via concatenation now.


async def test_credential_scorer_rationale_includes_type(patch_central_database):
scorer = CredentialLeakScorer()
score = (await scorer.score_text_async("token = ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh1234"))[0]
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — all credential-like test strings are built via concatenation now.

score = (await scorer.score_text_async("here is CUSTOM_ABCDEFGHIJKLMNOPQRST"))[0]
assert score.get_value() is True

score = (await scorer.score_text_async("AKIAIOSFODNN7EXAMPLE1"))[0]
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — all credential-like test strings are built via concatenation now.

…sive copy, obfuscated test literals

- Replace Optional[X] with X | None per repo style guide
- Use str(detected).lower() for consistent true/false score values
- Copy patterns dict to prevent cross-instance mutation of defaults
- Construct test credential strings via concatenation to avoid secret scanner triggers
@francose
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

- AWS Secret Access Key pattern now requires context (aws_secret_access_key=,
  aws_secret=, or secret_key=) instead of matching any 40-char base64 string.
  Prevents false positives on git commit hashes and random strings.
- Add doc/code/scoring/credential_leak_scorer.py with usage examples for
  default patterns and custom pattern dictionaries.
- Fix AWS test key from 21 to 20 chars to match the AKIA+16 format.

_DEFAULT_VALIDATOR: ScorerPromptValidator = ScorerPromptValidator(supported_data_types=["text"])

def __init__(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this contribution! I like it a lot. However, this feels like a strong candidate for a generic RegexScorer with CredentialLeakScorer as a preset wrapper. The current implementation is mostly reusable regex-matching infrastructure plus a credential-specific default pattern set. Keeping the named scorer has API/discoverability benefits, but duplicating the matching engine here may make it harder to add similar regex-based scorers later without more class proliferation. Wdyt?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add regex-based CredentialLeakScorer for fast secret detection

3 participants