fix: word-boundary severity matching for critical config keys#41
Conversation
…startswith
Critical keys with embedded sensitive terms (db_password, jwt_token,
app_secret_key, mysql_auth_url, oauth_token, connection_endpoint,
main_api_key_id) were incorrectly classified as WARNING or INFO instead
of BREAKING.
Root cause: _infer_severity_{added,removed,changed}() used
key.lower().startswith(p) which only catches keys that *begin* with
a critical prefix. Real-world config keys overwhelmingly embed the
sensitive term (e.g. 'db_password', not 'password_db'), so the heuristic
almost always missed them.
Fix: change to substring check -- p in key.lower() -- so the severity
gate fires correctly for any key containing a critical term.
Non-sensitive keys (cache_ttl, log_level, port, retry_count) are
unaffected since none of the critical terms appear as substrings.
Regression tests: 11 new cases in TestSeveritySubstringMatch, including
an end-to-end diff_configs assertion that has_breaking fires.
114/114 tests pass; ruff clean.
…itical terms Supersedes substring match (p in key.lower()) which: - Fixed nested keys like services.database.password (TRUE positive) - But over-flagged false positives: author->auth, secretary->secret, tokenizer->token New algorithm splits flattened keys into words (dot/snake/kebab/camel) and matches critical terms as contiguous word sequences. Also handles concatenated forms for multi-word terms (apikey -> api_key). +30 tests for word-boundary behavior: nested TRUE-positives, concatenated TRUE-positives, and 10 false-positive regressions. All 141 tests pass; ruff clean.
…ot on public PyPI); remove false PyPI badge
🤖 Automated Code Review✅ Ruff Lint — No issues
|
Council Gate Verdict — APPROVERisk level: high | Agreement: 1.0 | Council session: Per-model scores (rubric: correctness / safety / style / tests / complexity)
Engraphis referenceCouncil verdict persisted as
|
Council Gate Verdict — APPROVERisk level: high | Agreement: 1.0 | Council session: Per-model scores (rubric: correctness / safety / style / tests / complexity)
Engraphis referenceCouncil verdict persisted as
|
Council Merge Gate - REWORKThe model-council merge gate returned REWORK for this PR. Per repo policy, the PR is left open for rework. Council session: Council merge gate: REWORK. Merge allowed. Posted automatically by the |
Council Merge Gate - APPROVE (blocked by branch protection)The model-council merge gate returned APPROVE for this PR. However, GitHub branch protection policy prohibits the merge — the PR is left open. Council session: Next steps:
Posted automatically by the |
Summary
Fixes config-drift severity inference so critical-term detection uses word-boundary / segment matching instead of the prior substring heuristic.
_is_critical_key(key)splits flattened keys on.,_,-and camelCase into words and matches a critical term when its word-sequence is a contiguous subsequence (e.g.api_key/API_KEY/apiKey/apikey→ BREAKING).p in key.lower()(substring) which fixed nested keys (services.database.password) but over-flagged false positives (author→auth,secretary→secret,tokenizer→token)._infer_severity_*helpers to call the new matcher; kept_CRITICAL_PREFIXESas the term source.Verification
ruff check src/ tests/clean.Prior state
Branch was committed 2026-07-12 but delivery was blocked by an expired GitHub PAT fleet-wide; pushed and opened now that creds are restored.
🤖 Generated with Hermes (senior-dev-rotation-B)