feat(evaluators): add yelp.detect_secrets contrib evaluator#196
Open
feat(evaluators): add yelp.detect_secrets contrib evaluator#196
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Adds the 10 gap categories flagged in review, with given/when/then
behavioral style:
- parametric failure-mode matrix: every ScanFailureCode x {allow, deny}
plus evaluator-layer failures (normalization_error, payload_too_large)
- FAILURE_MESSAGES drift pin against ScanFailureCode enum
- normalization edge cases: top-level set, NaN/+-inf primitives,
empty dict / list, boolean/None dict keys, tuple dict keys
- runtime-error paths: get_runtime_info failure during non-None
evaluate (previously only reached via None short-circuit),
RuntimeConfigConflictError from get_runtime
- exclude_lines_regex on structured payloads: blanking suppresses
findings on matched lines and preserves pointers for unmatched ones
- max_bytes boundary: exactly-at-limit accepted, one-byte-over rejected
- multi-line string with distinct findings preserves line numbers
- list with scalar element maps pointer to index
- concurrent evaluate() on one cached instance stays correct
- _safe_structured_pointer returns None for missing location
- _key_name_is_secret_like for None and non-identifier/scalar-like keys
- entry-point .load() round-trips to DetectSecretsEvaluator
- config validator edges: explicit None enabled_plugins, whitespace-only
entry rejected, whitespace strip + dedup, positive-int bounds on
timeout_ms / max_bytes, Literal validation on on_error
Coverage: 93% -> 98% (config 96 -> 100, evaluator 94 -> 98,
normalization 92 -> 98). 39 -> 90 passing tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New contrib evaluator that scans selector-selected payloads for potential secrets using
Yelp
detect-secrets, wired through thedetect-secrets-asyncsubprocess-pool runtime.Registered under the entry point
yelp.detect_secrets.Why this matters for agent workflows
Agent payloads move secrets around constantly without anyone meaning to. Concrete leak surfaces
in selector-selected step payloads:
Agents chain tool calls, so a leaked token in one step becomes input to the next — that's the
blast radius this evaluator is here to cap. Agent Control's evaluator layer is the natural gate:
it runs after the selector narrows the payload to the relevant field and before the control's
policy decision is committed.
Yelp
detect-secretscontributes a battle-tested detector set — AWS keys, GitHub tokens, Basicauth, private keys, high-entropy blobs, and keyword patterns — with per-request plugin narrowing
when you want fewer false positives on a specific control.
Why a separate async runtime
detect-secretsis synchronous and configures itself via process-global settings, which makesasyncio.to_thread(theatrical timeouts — the scan keeps running) and a serialising lock (killsthroughput) both poor fits for Agent Control. The external
detect-secrets-asyncpackage wraps it in abounded pool of long-lived subprocess workers with real timeouts, per-request plugin isolation,
and automatic worker replacement on timeout/crash/cancellation. This PR is the thin Agent Control
adapter on top of that runtime.
What the evaluator does
None→ no matchstr→ scanned directlydict/list→ deterministic pretty JSON with RFC 6901 pointer mappingint/float/bool→ JSON scalar textexclude_lines_regex(blanked, so line numbers stay stable).max_bytescap on post-filter UTF-8 bytes.detect-secrets-asyncusing the shared host-level runtime.EvaluatorResult:strpayloads getline_number.json_pointer, conservatively truncated at any secret-lookingsegment in the path so a token appearing as a key name never leaks through the pointer.
hashed_secretare never surfaced.Example
A control fragment that scans the
outputfield for GitHub and AWS credentials, failing open onevaluator errors:
{ "selector": { "path": "output" }, "evaluator": { "name": "yelp.detect_secrets", "config": { "timeout_ms": 10000, "on_error": "allow", "enabled_plugins": ["GitHubTokenDetector", "AWSKeyDetector"] } } }For a plain-string payload
"github_token = 'ghp_abc...'":For a structured payload
{"response": {"headers": {"authorization": "ghp_abc..."}}}:A dict keyed by a secret-looking string reports the safe ancestor instead of leaking the key:
Config
timeout_ms10_000on_error"allow"allow) or fail-closed (deny) on evaluator failure.max_bytes1_048_576enabled_pluginsNonedetect_secrets_async.get_runtime_info().available_plugin_names.Noneuses the pinned upstream default set.exclude_lines_regex[]Failure handling
Every failure maps to a stable
metadata["failure_mode"]:normalization_error,payload_too_large,queue_full,queue_timeout,worker_startup_error,worker_timeout,worker_crash,worker_protocol_error,runtime_error.on_errorcontrols the fallback inEvaluatorResult:allow→matched=False,metadata["fallback_action"]="allow"deny→matched=True,metadata["fallback_action"]="deny"Consumers should branch on
metadata["failure_mode"]+metadata["fallback_action"], not onmatchedalone, to distinguish a real finding from a fail-closed evaluator failure.Dependencies
detect-secrets-async>=0.2.0,<0.3.0— theasync subprocess-pool runtime over Yelp
detect-secrets.google-re2>=1.1— matches existing Agent Control regex policy.Validation
make checkinevaluators/contrib/detect_secrets:and structured payloads, plugin validation (strip + dedup + unknown rejection),
max_bytesboundary behavior, recursive / NaN / empty-container / unsupported-type normalization paths,
timeout short-circuit, every
failure_modepath ×{allow, deny}(16 runtime + 4evaluator-layer combinations),
FAILURE_MESSAGESdrift pin against theScanFailureCodeenum,concurrent dispatch on a cached evaluator instance, and entry-point
.load()round-trip.config.py100%,evaluator.py98%,normalization.py98%).