Skip to content

secrets: detect credential fields in structured config (JSON) - #43

Merged
nmatt0 merged 1 commit into
masterfrom
feat/config-credential-json
Oct 30, 2026
Merged

nmatt0 merged 1 commit into
masterfrom
feat/config-credential-json

Conversation

@nmatt0

@nmatt0 nmatt0 commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Problem

The generic-secret content rule anchors on password / passwd / secret /
api_key, but match_generic floors the value at 16 characters and the engine
applies a >=3.5 bits/byte entropy gate. A short, low-entropy credential assigned
to a credential-named field in a config file (a device PIN, a base64-wrapped
passcode) therefore falls through by design. The rule's own comment says as
much: it is precision-first on a flat byte stream, and a short value is the
accepted false-negative.

Lowering those floors is not an option. On a flat stream nothing separates a
real "passwd":"<value>" from passwd=%s, oldpasswddir=..., or
"password":"", so it would bring back a false-positive storm.

Change

A structure-aware pass, scan_config_credentials (src/configcred.cpp), wired
into the secrets pass in walk.cpp. It parses the file as JSON first, so the
key name, the value, and the key/value relationship are exact. The parse is
itself a strong gate: binary, code, and malformed text never enter.

It fires only when:

  • the key is in a small curated strong-credential set (pin-like:
    passwd/pin/psk/wpakey/...; token-like: secret/apikey/token/...,
    which additionally require length >= 8 or a successful decode),
  • the value is not empty, a known placeholder, an enum/boolean, or a single
    repeated character,
  • after auto-decoding base64/hex and re-judging the decoded form.

A long, high-entropy value under a key the flat-stream rule already anchors is
deferred to that rule, so a credential is reported once, not twice. Findings use
a new category config-credential; confidence is Pattern for a raw value and
Structural when the value decoded; values are redacted.

JSON only in this change; ini/env/xml are natural follow-ons behind the same
key/value judgment.

Tests

tests/test_config_cred.py asserts the positives (base64-encoded passcodes, a
Wi-Fi PSK, a numeric PIN, an array-inherited key), the negatives that must stay
silent (empty / placeholder / enum / numeric token-key / non-credential key, a
non-JSON document, a plaintext file), no double-report of a high-entropy value,
and value redaction. Full suite green (ctest 15/15). Also validated across a
test set of extracted firmware: the targeted config is caught and config-heavy
images produce no false positives.

The generic-secret content rule anchors on password/passwd/secret/api_key but
match_generic floors the value at 16 chars and the engine gates on >=3.5
bits/byte of entropy, so a short low-entropy credential assigned to a
credential-named config field (a device PIN, a base64-wrapped passcode) is a
deliberate false-negative. Lowering those floors on the flat byte stream would
reintroduce a false-positive storm, because nothing there separates a real
passwd field holding a short value from passwd=%s or an empty password.

Add a structure-aware pass (scan_config_credentials) that parses the file as
JSON first, so the key, the value, and their relationship are exact; the parse
is itself a strong gate, since binary, code, and malformed text never enter. It
fires only on a curated strong credential key (pin-like: passwd/pin/psk/
wpakey...; token-like: secret/apikey/token... which also require length >= 8 or
a decode), rejects empty / placeholder / enum / single-character values,
auto-decodes base64/hex and re-judges the decoded form, and defers a long
high-entropy value under a key the flat-stream rule already anchors to that
rule so a credential is reported once. Findings use category "config-credential";
confidence is Pattern for a raw value and Structural when it decoded; values are
redacted.

JSON only for now; ini/env/xml are follow-ons behind the same key/value
judgment. Adds tests/test_config_cred.py (positives, placeholder/enum/non-JSON
negatives, no-double-report, redaction).
@nmatt0
nmatt0 merged commit 9d5440d into master Oct 30, 2026
4 checks passed
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.

1 participant