noctcore-observability/no-sensitive-fields-in-logs
A name-heuristic guard against writing credentials and secrets into log sinks. Ships at
error.
Recommended preset: error · Autofix: no · Suggestions: no · Type information: not needed
Logs are long-lived, widely readable, and shipped to third-party aggregators. A password, token,
secret, or authorization header written into a log line is a credential leak that outlives the
request by months. This rule catches the most common shape — a variable, property, or object key
whose name matches a sensitive-field denylist appearing inside a logger call.
It reads names, never values, so it is a heuristic. It still ships at error: a miss puts a
credential in a long-lived log sink, while a false positive costs a rename.
Tests that log a secret on purpose (to prove a redaction boundary works) should turn the rule off for
those files in config.
What it flags
Section titled “What it flags”Inside a logger call (<logger>.<method>(...)), any identifier, member-access property, or object key
whose name matches the denylist:
// all three flaglogger.info('login', { password });logger.error('auth failed', { userPassword: pw });logger.info('session', user.token);// log who, not the credentiallogger.info('login', { userId });logger.error('auth failed', { userId, reason: 'bad-credentials' });logger.info('session', { sessionId: user.sessionId });Wrapping the value in a call does not help: { password: redact(password) } is still reported,
twice, because the key and the argument are both named password. The rule has no notion of a
redaction helper. Omit the field and log an identifier for the subject instead.
Matching is name-segment aware. A single-word denyName (token) matches a camelCase or
snake_case segment (accessToken, access_token) but not a longer word that merely contains it
(tokenize, tokenizer). A multi-word denyName (apiKey) matches the compacted name
(myApiKey → contains apikey).
What it does not flag
Section titled “What it does not flag”String literals are never inspected — only names — so a message that mentions a sensitive word is fine:
// a literal, not a valuelogger.info('password reset email sent');- A longer word that merely contains a denyName (
tokenizer,tokenize): matching is by name segment, as described above. - Sensitive names outside a logger call (
save({ password })). Logger calls areconsole,loggerorlogwithinfo,warn,errorordebug.
Options
Section titled “Options”| Option | Type | Default | Meaning |
|---|---|---|---|
denyNames |
string[] |
['password', 'token', 'secret', 'authorization', 'cookie', 'apiKey', 'ssn'] |
Field names to treat as sensitive (case-insensitive, segment-aware). |
'noctcore-observability/no-sensitive-fields-in-logs': ['error', { denyNames: ['password', 'token', 'secret', 'authorization', 'cookie', 'apiKey', 'ssn', 'iban'],}]When not to use it
Section titled “When not to use it”If your logging pipeline already redacts sensitive fields centrally (a serializer denylist), this
rule is redundant. Otherwise keep it on at error as a second line of defence.