Skip to content

redact: catch high-entropy SESSION keys (BW_SESSION) and slashed base64 values - #4

Merged
kjanat merged 7 commits into
masterfrom
fix/redact-session-keys
Jul 31, 2026
Merged

redact: catch high-entropy SESSION keys (BW_SESSION) and slashed base64 values#4
kjanat merged 7 commits into
masterfrom
fix/redact-session-keys

Conversation

@kjanat

@kjanat kjanat commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Fixes the missed redaction of Bitwarden's BW_SESSION reported via envctl redact < settings.json, where TRUENAS_API_KEY in the same file was masked but the session key passed through untouched.

Root cause

Two independent gaps, each fixed in its own commit:

  1. Key vocabulary: SESSION only counted as a secret signal when paired with KEY (e.g. SESSION_KEY). suspicious_key_name() — the gate deciding whether a value reaches the entropy detector at all — didn't include SESSION, so BW_SESSION returned "not a secret" before its value was ever examined.

  2. Slash path-guard: even with the key fixed, entropy_secret() rejected any value containing two or more / characters (a relative-path guard). Standard base64 includes / in its alphabet, so roughly 40% of real 64-byte session keys would still have leaked. Trailing = padding on a base64-charset value is decisive evidence of encoded data over a path, so slashed values are now accepted in exactly that shape.

Behavior

  • BW_SESSION=<88-char base64> masks in env, assignment, and JSON forms — including values containing /.
  • Benign session config stays untouched: SESSION_TIMEOUT=3600, SESSION_NAME=my-app-session, SESSION_ID (existing *_ID exemption), and path values like SESSION_KEY_PATH=config/keys/session.pem (no padding, fails the entropy bar).
  • Path guards keep working: API_KEY_FILE=src/main/resources/key.txt remains unmasked.

Testing

  • New regression case tests/cases/filter-session-keys.case covering the JSON form from the report, plain assignments with and without slashes, and the four benign guard shapes.
  • Full suite: 156 pass. The only failure (filter-epipe-reports-write-failure) is a fifo-timing case that fails identically on the unmodified base commit in this sandbox.

Related: #3 tracks the remaining false-negative classes found while auditing this.

kjanat added 2 commits July 31, 2026 00:54
envctl redact missed Bitwarden's BW_SESSION: SESSION only counted as a
secret signal when paired with KEY (SESSION_KEY), so suspicious_key_name
never let the high-entropy base64 session key reach the entropy detector.

Add SESSION to the suspicious segment list. The entropy bar still keeps
benign values (SESSION_TIMEOUT=3600, SESSION_NAME=my-app-session) and
the *_ID identifier exemption (SESSION_ID) unmasked.
Standard base64 includes '/', so roughly 40% of real 64-byte session
keys (Bitwarden BW_SESSION and similar) carry two or more slashes and
were rejected by entropy_secret's relative-path guard before their
entropy was measured. Trailing '=' padding on a base64-charset value is
decisive evidence of encoded data over a path, so accept slashed values
only in that shape. Path-like values keep the guard: they neither end
in '=' nor survive the entropy bar.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe205d4c-9df1-49d3-a601-2d02be92f0b5

📥 Commits

Reviewing files that changed from the base of the PR and between 140599a and 1c4a07b.

📒 Files selected for processing (2)
  • src/redact.c
  • tests/cases/filter-strong-keys.case
📜 Recent review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: windows-2025-vs2026
  • GitHub Check: Analyze (c-cpp)
  • GitHub Check: Analyze (actions)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-07-31T02:14:35.239Z
Learnt from: kjanat
Repo: kjanat/envctl PR: 2
File: tests/cases/get-env-missing.case:1-4
Timestamp: 2026-07-31T02:14:35.239Z
Learning: Test case fixtures under tests/cases are executed by tests/run.sh through env -i with a controlled PATH and only variables declared in the case's %% setenv section. Do not assume developer-shell or CI environment variables are inherited; variables omitted from %% setenv, such as NO_SUCH_VAR, are deterministically absent.

Applied to files:

  • tests/cases/filter-strong-keys.case
🔍 Remote MCP DeepWiki, GitHub Grep

Additional review context

  • Bitwarden generates BW_SESSION from a 64-byte AES-CBC/HMAC key using SymmetricCryptoKey.toBase64(), so the value is genuine Base64 key material rather than an opaque arbitrary token.
  • Bitwarden accepts the same session value both through process.env.BW_SESSION and the CLI --session option.
  • Bitwarden decodes BW_SESSION with Utils.fromB64ToArray() before constructing a SymmetricCryptoKey; therefore valid Base64 characters such as / and = must remain eligible for redaction.
  • Bitwarden’s own tests construct session keys from 64-byte arrays, providing a concrete indication that session values are typically long enough to satisfy entropy-based detection.
  • DeepWiki could not retrieve kjanat/envctl because the repository was unavailable to that source.
🔇 Additional comments (2)
src/redact.c (1)

117-117: LGTM!

Also applies to: 136-160, 430-445, 456-467, 1143-1148

tests/cases/filter-strong-keys.case (1)

34-35: LGTM!

Also applies to: 76-77


📝 Walkthrough

Update envctl redact to mask high-entropy SESSION values, including Bitwarden’s BW_SESSION keys.

  • Recognise SESSION as a suspicious key segment.
  • Allow strictly valid padded base64 values that contain /.
  • Preserve exemptions for benign configuration, identifiers, provider values, and path-like values.
  • Ignore word placeholders during token detection while retaining numeric values for connection-string detection.
  • Add regression coverage for JSON, shell assignments, slashed base64 values, and benign cases.

156 tests pass. One FIFO timing test fails on the unchanged base commit.

Walkthrough

Entropy-based redaction now recognises keys containing SESSION and OAUTH. Values with multiple slashes are rejected as path-like unless they are strictly padded base64. Placeholder assignment values no longer undergo generic token detection. New fixtures cover session keys, credentials, tokens, private keys, value boundaries, and preserved non-secret values. The README documents SESSION matching.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

  • kjanat/envctl#2 — Adds environment-entry loading that is directly related to the redaction paths tested here.

Poem

SESSION secrets now vanish from view,
OAUTH keys join the masking crew.
Padded base64 clears the gate,
Placeholder values keep their state.
Tests guard each boundary true.

🚥 Pre-merge checks | ✅ 5 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title describes the main change and uses a lowercase verb, but it is 78 characters long and does not include the required fix indication. Use a concise title under 50 characters that includes fix, such as "fix: redact high-entropy SESSION keys".
Changelog Update ⚠️ Warning src/redact.c is modified, but no CHANGELOG.md exists or is modified. No version bump is present, so the change must be listed under ## [Unreleased]. Add or update CHANGELOG.md with an entry under ## [Unreleased] describing SESSION entropy-based redaction and related detection changes.
Semver Version Bump Validation ⚠️ Warning src/redact.c changes code, but no allowed version file exists at the base or PR head, so no SemVer bump is present; this backward-compatible bug fix requires a PATCH bump. Add an allowed version file or update an existing allowed version field to the next MAJOR.MINOR.PATCH patch release.
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the BW_SESSION redaction bug, its root causes, the intended behaviour, and the regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Agents.Md Documentation Updated ✅ Passed No AGENTS.md file exists in the tracked files or filesystem, so the conditional documentation-update requirement does not apply to these behaviour changes.

Comment @coderabbitai help to get the list of available commands.

@kjanat kjanat self-assigned this Jul 31, 2026
@kjanat kjanat added the cr:review Allow CodeRabbit review label Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/redact.c`:
- Around line 430-446: Replace the weak b64/padded slash-guard exception in the
token classification loop with validation that confirms standard Base64
alphabet, valid trailing padding position and count, and length alignment before
bypassing the relative-path rejection. Keep slash-containing values rejected
unless they pass this complete Base64 validation, and add a regression case
covering a path-like high-entropy value with multiple slashes ending in “=”.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 834c3862-323c-474f-8607-a64db5b77584

📥 Commits

Reviewing files that changed from the base of the PR and between ddfc786 and f8c2716.

📒 Files selected for processing (3)
  • README.md
  • src/redact.c
  • tests/cases/filter-session-keys.case
📜 Review details
🧰 Additional context used
🔍 Remote MCP DeepWiki, GitHub Grep

Relevant review context

  • Bitwarden’s CLI generates BW_SESSION as a random 64-byte symmetric key, base64-encodes it, exposes it through the environment or --session, and later decodes it for encryption/decryption. This confirms it is a sensitive credential rather than a benign session label.
  • Official Bitwarden code sets process.env.BW_SESSION = key.toBase64() and documents exporting it after bw unlock; the key is also cleared on lock.
  • Real integrations consistently use export BW_SESSION=$(bw unlock --raw) and pass it to bw commands, including jdx/fnox documentation/tests and several shell integrations.
  • jdx/fnox tests explicitly require BW_SESSION for authenticated Bitwarden operations and validate it with bw status --session "$BW_SESSION", supporting regression coverage for environment-variable redaction.
  • The target kjanat/envctl repository was not indexed by DeepWiki, and repository-scoped GitHub Grep queries returned no results; validation of the changed implementation itself was therefore limited to the supplied PR context.
🔇 Additional comments (3)
src/redact.c (1)

117-117: LGTM!

README.md (1)

192-193: LGTM!

tests/cases/filter-session-keys.case (1)

1-21: LGTM!

Comment thread src/redact.c Outdated
The bypass accepted any authz-charset value whose last byte was '=',
so '.', '_', and '-' could appear in the "base64" body and only the
final byte was inspected. Require the real thing before trusting the
padding signal: '+/' alphabet only, 4-byte alignment, and at most two
'=' confined to the tail. A path-shaped value like
lib/session_v2/loader.so= now stays on the path side of the guard,
pinned by a new regression line.
coderabbitai[bot]

This comment was marked as resolved.

This comment was marked as resolved.

@gitguardian

This comment was marked as low quality.

@kjanat
kjanat force-pushed the fix/redact-session-keys branch from 4b8434d to f3aef4d Compare July 31, 2026 01:33
The OAUTH, BEARER, JWT, and CRED segments routed values to the entropy
detector with no test asserting that a high-entropy value under them
actually masks; only the OAUTH_CLIENT_ID exemption side was pinned.
Cover the masking side for all four, plus the *_ID exemption and a
low-entropy guard, so the suspicious-segment list cannot quietly
regress.
@kjanat
kjanat force-pushed the fix/redact-session-keys branch from f3aef4d to 45d7020 Compare July 31, 2026 01:34
coderabbitai[bot]

This comment was marked as low quality.

Pin filter-mode behavior for every detector family, in seven case
files totaling ~173 verified input behaviors:

- filter-entropy-key-segments: all ten suspicious segments masking
  high-entropy values, with a guard per segment (paths, UUIDs,
  trivial values, *_ID identifiers).
- filter-strong-keys: every strong key name masking on sight, plus
  trivial-value, pathish-suffix, and *_ID exemptions. Pins that a
  bare PASSWORD=changeme line masks as a connection-string fragment
  before the trivial exemption is reached.
- filter-token-prefixes: all known token prefixes and the AWS A3T
  shape, JWT compact form, bare-prefix and too-short guards.
- filter-value-shapes: credentialed URLs, sensitive URL params,
  Authorization values, connection strings, private JWK, inline PEM,
  and the public-PEM, path, digest, and UUID non-masking guards.
- filter-json-yaml-contexts: JSON pairs, YAML colon keys, quoted
  export lines, nested JSON, private JWK, and JSON-side exemptions.
- filter-prose-lines: secrets embedded in log and prose lines, header
  values, Set-Cookie, and prose digest/UUID/commit guards.
- filter-exemption-boundaries: entropy class length floors (hex 32,
  base64 24, opaque 16 requiring a non-base64 char), low-entropy and
  short-value guards, pathish and digest suffixes, trivial values,
  and the VAULT_SECRET_ID strong-over-identifier override.

All expectations verified against the binary line by line before
pinning; suite grows from 157 to 163 cases.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/cases/filter-strong-keys.case`:
- Around line 32-34: Update mask_tokens so ordinary KEY=VALUE tokens do not fall
through to conn_string_secret after the placeholder exemption and get
reclassified as secrets. Preserve PASSWORD=changeme unchanged in the
filter-strong-keys fixture while retaining masking for genuine connection-string
secrets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3cb18540-4406-4c13-89f8-886497f19ad3

📥 Commits

Reviewing files that changed from the base of the PR and between 4b8434d and eb76635.

📒 Files selected for processing (7)
  • tests/cases/filter-entropy-key-segments.case
  • tests/cases/filter-exemption-boundaries.case
  • tests/cases/filter-json-yaml-contexts.case
  • tests/cases/filter-prose-lines.case
  • tests/cases/filter-strong-keys.case
  • tests/cases/filter-token-prefixes.case
  • tests/cases/filter-value-shapes.case
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: windows-2025-vs2026
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (c-cpp)
🧰 Additional context used
🪛 Betterleaks (1.7.0)
tests/cases/filter-prose-lines.case

[high] 17-17: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


[high] 12-12: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

tests/cases/filter-token-prefixes.case

[high] 21-21: Discovered a DigitalOcean Personal Access Token, posing a threat to cloud infrastructure security and data privacy.

(digitalocean-pat)


[high] 27-27: Uncovered a GCP API key, which could lead to unauthorized access to Google Cloud services and data breaches.

(gcp-api-key)


[high] 30-30: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


[high] 18-18: Identified a Slack Bot token, which may compromise bot integrations and communication channel security.

(slack-bot-token)


[high] 16-16: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.

(stripe-access-token)


[high] 17-17: Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data.

(stripe-access-token)


[high] 7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

tests/cases/filter-exemption-boundaries.case

[high] 6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 9-9: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 12-12: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 28-28: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 34-34: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 37-37: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

tests/cases/filter-strong-keys.case

[high] 22-22: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

tests/cases/filter-entropy-key-segments.case

[high] 6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 10-10: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

tests/cases/filter-json-yaml-contexts.case

[high] 6-6: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 7-7: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 9-9: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 10-10: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

tests/cases/filter-value-shapes.case

[high] 8-8: Detected a MongoDB connection string with embedded credentials, potentially exposing direct database access and sensitive application data.

(mongodb-connection-string)


[high] 10-10: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 12-12: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 19-19: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 GitHub Check: GitGuardian Security Checks
tests/cases/filter-prose-lines.case

[error] 10-10: GitGuardian detected a hardcoded Basic Auth String. Revoke/rotate it and replace it with a safe test placeholder.


[error] 7-7: GitGuardian detected a hardcoded Bearer Token. Revoke/rotate it and replace it with a safe test placeholder.


[error] 12-12: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 17-17: GitGuardian detected a hardcoded JSON Web Token. Revoke/rotate it and replace it with a safe test placeholder.


[error] 13-13: GitGuardian detected a hardcoded X-API-Key Secret. Revoke/rotate it and replace it with a safe test placeholder.

tests/cases/filter-token-prefixes.case

[error] 7-7: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 16-16: GitGuardian detected a hardcoded Stripe Key. Revoke/rotate it and replace it with a safe test placeholder.


[error] 27-27: GitGuardian detected a hardcoded Google API Key. Revoke/rotate it and replace it with a safe test placeholder.


[error] 21-21: GitGuardian detected a hardcoded DigitalOcean Personal Access Token v1. Revoke/rotate it and replace it with a safe test placeholder.


[error] 30-30: GitGuardian detected a hardcoded JSON Web Token. Revoke/rotate it and replace it with a safe test placeholder.


[error] 18-18: GitGuardian detected a hardcoded Slack Bot Token. Revoke/rotate it and replace it with a safe test placeholder.

tests/cases/filter-exemption-boundaries.case

[error] 7-7: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.

tests/cases/filter-strong-keys.case

[error] 22-22: GitGuardian detected a hardcoded Generic Encryption Key. Revoke/rotate it and replace it with a safe test placeholder.

tests/cases/filter-entropy-key-segments.case

[error] 8-8: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.

tests/cases/filter-json-yaml-contexts.case

[error] 8-8: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 19-19: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 7-7: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 6-6: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 10-10: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 16-16: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 21-21: GitGuardian detected a hardcoded Generic Password. Revoke/rotate it and replace it with a safe test placeholder.


[error] 9-9: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.


[error] 17-17: GitGuardian detected a hardcoded Generic Password. Revoke/rotate it and replace it with a safe test placeholder.

tests/cases/filter-value-shapes.case

[error] 17-17: GitGuardian detected a hardcoded Generic Password. Revoke/rotate it and replace it with a safe test placeholder.


[error] 10-10: GitGuardian detected a hardcoded Generic High Entropy Secret. Revoke/rotate it and replace it with a safe test placeholder.

🔍 Remote MCP DeepWiki, GitHub Grep

Additional review context

  • Bitwarden’s official CLI generates BW_SESSION from an AES-256-CBC-HMAC key and stores the Base64 representation in the environment. It explicitly prints shell export examples and supports passing the same value via --session, confirming that BW_SESSION contains authentication material.
  • Bitwarden’s CLI also exposes --session <session> as an alternative to reading the session key from the environment.
  • Real integrations use export BW_SESSION=$(bw unlock --raw), pass it to authenticated commands, and sometimes persist it in shell or tmux state. This supports covering assignment-style, structured, and persisted configuration inputs.
  • Related code validates Bitwarden session values using a Base64-like character set including +, /, and = with a minimum length, supporting the PR’s need to handle slash-containing padded Base64 values carefully.
  • No source implementation could be retrieved for kjanat/envctl: GitHub Grep returned no matches for the target functions, and DeepWiki reports that the repository is not indexed.
🔇 Additional comments (6)
tests/cases/filter-entropy-key-segments.case (1)

6-49: LGTM!

tests/cases/filter-exemption-boundaries.case (1)

6-53: LGTM!

tests/cases/filter-json-yaml-contexts.case (1)

6-39: LGTM!

tests/cases/filter-prose-lines.case (1)

6-31: LGTM!

tests/cases/filter-token-prefixes.case (1)

6-32: LGTM!

tests/cases/filter-value-shapes.case (1)

6-57: LGTM!

Comment thread tests/cases/filter-strong-keys.case
mask_tokens re-evaluates a declined assignment as one whole token, so
PASSWORD=changeme — already exempted as a trivial placeholder by the
keyed check — fell through to conn_string_secret, which matched the
leading password= and swallowed the key as <redacted:credentials>.
Skip the whole-token fallback when the token parses as KEY=VALUE with
a trivial value. Non-trivial conn fragments (password=verysecret,
sslkey=/path), compound tokens (Server=db;Password=...), and the
value-level connection-string check keep masking.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/redact.c`:
- Around line 1137-1141: Restrict the trivial_kv bypass in the assignment
parsing flow to explicit placeholder literals only, rather than all values
accepted by is_trivial_value, so numeric credential values such as
PASSWORD=123456 still reach the generic token detector and conn_string_secret
masking. Use or add a narrower placeholder helper at this condition and add
regression coverage for signed and unsigned numeric password values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 772e9c1a-fc2b-4bd5-9216-b77ea89aa604

📥 Commits

Reviewing files that changed from the base of the PR and between eb76635 and 140599a.

📒 Files selected for processing (2)
  • src/redact.c
  • tests/cases/filter-strong-keys.case
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: windows-2025-vs2026
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (c-cpp)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-07-31T02:14:35.239Z
Learnt from: kjanat
Repo: kjanat/envctl PR: 2
File: tests/cases/get-env-missing.case:1-4
Timestamp: 2026-07-31T02:14:35.239Z
Learning: Test case fixtures under tests/cases are executed by tests/run.sh through env -i with a controlled PATH and only variables declared in the case's %% setenv section. Do not assume developer-shell or CI environment variables are inherited; variables omitted from %% setenv, such as NO_SUCH_VAR, are deterministically absent.

Applied to files:

  • tests/cases/filter-strong-keys.case
🔍 Remote MCP GitHub Grep

Additional review context

  • Bitwarden CLI generates BW_SESSION via SymmetricCryptoKey.toBase64() and assigns it to process.env.BW_SESSION, confirming the value is Base64-encoded key material.
  • Bitwarden explicitly prints BW_SESSION shell-export examples and supports the same value through --session.
  • The CLI decodes BW_SESSION with Utils.fromB64ToArray() and constructs a symmetric key from it, confirming that / and = are valid Base64 characters that must not be rejected indiscriminately.
  • Bitwarden clears BW_SESSION when locking, and its CLI option handler sets BW_SESSION from --session; both environment and argument-based usage are therefore relevant redaction inputs.
  • No source matches were available for kjanat/envctl’s implementation or fixtures through GitHub Grep, so repository-specific validation beyond the supplied PR context was unavailable.
🔇 Additional comments (2)
src/redact.c (1)

117-117: LGTM!

Also applies to: 424-439, 450-461

tests/cases/filter-strong-keys.case (1)

1-44: LGTM!

Also applies to: 45-83

Comment thread src/redact.c Outdated
is_trivial_value also accepts signed and unsigned numerics up to six
digits, so the token-fallback bypass introduced for PASSWORD=changeme
let PASSWORD=123456 — a plausible PIN — skip conn_string_secret. Split
the word list into is_trivial_word and gate the bypass on that alone;
short numerics keep reaching the shape detectors. Regression lines pin
PASSWORD=123456 masking as a connection-string fragment and the signed
+123456 masking through the keyed path (seven characters exceeds the
numeric-trivial limit).
@kjanat
kjanat merged commit e7a90a7 into master Jul 31, 2026
10 checks passed
@kjanat
kjanat deleted the fix/redact-session-keys branch July 31, 2026 02:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cr:review Allow CodeRabbit review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant