Files
openclaude/scripts
GravireiGitHubGravireiClaude Opus 4.6coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>openhands
aa936cda11 Centralize credential redaction in src/utils/redaction.ts + channel gate tests (#1711)
* feat(utils): add centralized redaction utility

Single source of truth for stripping API keys, tokens, and other
secrets from strings and JSON. Provider env-var coverage is generated
from getKnownProviderSecretEnvKeys() so adding a new provider cannot
silently create an unredacted path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(Feedback): import redactSensitiveInfo from utils

Remove the inline 40-line regex implementation in favor of the
centralized redaction utility, eliminating drift between Feedback
and the transcript share path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(submitTranscriptShare): import redactSensitiveInfo from utils

Update import path to point at the centralized utility instead of the
Feedback component. Removes the implicit re-export contract that
required Feedback.tsx to keep redactSensitiveInfo exported.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(log,debug): redact secrets in default error and debug output

Wire the centralized redaction utility into logError and logForDebugging
so secrets cannot leak into in-memory error logs or the debug file even
if a caller forgets to pass through redactSensitiveInfo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(api/logging): redact error message in logAPIError

Apply the centralized redaction utility to the error string passed to
logEvent so analytics events cannot capture unredacted credentials from
upstream API failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve merge conflict from upstream sync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(channelNotification): allow null in getEffectiveChannelAllowlist signature

ChannelsNotice.tsx passes getSubscriptionType() which returns
SubscriptionType | null, but the signature only accepted string |
undefined. Widen to string | null so the call site typechecks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(redaction): exclude specific token fields from redaction process

* fix(redaction): lower AIza minimum length to {10,}

Real GCP/Gemini keys are 39 chars total (4 prefix + 35 suffix), but
the {35} suffix bound missed short tokens like 'AIzaSyDUMMY-secret-token'
(21 chars after AIza). Lower to {10,} to match the diagnostics module
and catch any AIza-shaped value. Same precision trade-off the
diagnostics redaction makes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(redaction,log): address review feedback

- Drop quotes from ANTHROPIC/OPENAI key negative lookarounds so
  JSON-shaped values like "sk-ant-..." redact.
- Add private_key pattern to GENERIC_HEADER_FIELD_PATTERN and
  privatekey to SENSITIVE_FIELD_SUBSTRINGS.
- logError now builds a sanitized Error (redacted message + stack)
  before passing to the sink and queue, not just the in-memory log.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(redaction): consolidate into single module + add channel gate tests

Address the three P2 review findings on the central-redaction PR:

[1] Consolidate four redaction modules into src/utils/redaction.ts.
    Previously lived in:
      - src/utils/redaction.ts            (logs/bug reports/transcript shares)
      - src/utils/urlRedaction.ts         (URL display)
      - src/utils/statusRedaction.ts      (/status output)
      - src/utils/diagnostics/redaction.ts (doctor reports)
    The four surfaces share the same regex set / credential lists
    but had drifted into separate per-domain files. Merged into
    one module; deleted the three shim files. Updated six direct
    consumers (openaiShim.ts, ProviderManager.tsx, status.tsx,
    requestSizeBreakdown.ts, diagnostics/issueReport.ts,
    scripts/system-check.ts) and three test files to import from
    redaction.js.

[2] Add gateChannelServer() test coverage.
    src/services/mcp/channelNotification.test.ts: 13 cases for the
    six gate paths (capability, runtime, session, marketplace,
    plugin allowlist, server-entry dev) plus end-to-end register.
    Mocks channelAllowlist.js (GrowthBook-backed) so tests stay
    independent of feature-flag state.

[3] Apply jsonRedactor in transcript share.
    src/components/FeedbackSurvey/submitTranscriptShare.ts now does
    redactSensitiveInfo(jsonStringify(data, jsonRedactor)) — the
    key-aware redaction applies during serialization, and the text
    pass stays as defense in depth for free-form fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(channelNotification): cover findChannelEntry multi-candidate branch

Regression test for the disambiguation path in `findChannelEntry`
(channelNotification.ts:201-230): when two same-name plugin entries
exist in the allowed-channels list with different marketplaces,
`pluginSource` must select the matching entry before the marketplace
and allowlist gates evaluate.

Without this branch being exercised, the gate could lock onto
whichever entry sorts first and either skip the user's real
installation or wrongly authorize a typo-squatted one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(redaction): align URL fallback regex + add path-prefix boundary check

Two related redaction correctness fixes:

[1] URL fallback regex covers the same parameter set as the primary
    path. The malformed-URL branch in `redactUrlForDisplay` previously
    had a hand-rolled alternation of credential parameter names that
    could drift behind `SENSITIVE_URL_QUERY_PARAM_TOKENS`. New
    `MALFORMED_URL_PARAM_PATTERN` derives from that same list, so
    the two paths can never diverge. Tests cover the full credential
    set (`api_key`, `access_token`, `refresh_token`, `signature`,
    `sig`, `secret`, `password`, `apikey`) plus a non-sensitive
    `model` that must survive.

[2] `redactPathForStatus` now requires a path-separator boundary
    after the home prefix. The previous `startsWith` check matched
    `/home/alice2/project` against `/home/alice` and emitted
    `~2/project`. The fix requires the character at
    `normalizedCandidate.length` to be `/` or `\` so `alice` no longer
    matches `alice2` or `alice.bak`. Test pins the false-positive
    paths and the true-positive (`/home/alice/project` → `~/project`).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(channel,redaction): restore dev-channel warning + align URL fallback

Two related security fixes:

[1] Restore DevChannelsDialog when --dangerously-load-development-channels
    is passed and the channels feature is enabled. The previous logic
    skipped the dialog when OAuth was absent, which was safe only while
    gateChannelServer() blocked no-OAuth sessions. With the OAuth/org-
    policy gates removed in this PR, an API-key session could pass the
    flag, skip the warning, and still register the dev channel. The
    only remaining skip is the genuinely-disabled feature case
    (`!isChannelsEnabled()`), where the dialog is moot.

[2] Malformed-URL fallback now uses the same substring predicate as
    the primary `URL` parser path. The previous regex matched only
    exact parameter names (`api_key=`, `access_token=`, …), so
    `my_api_key=SECRET` and `x_access_token=TOKEN` slipped through
    unchanged even though `shouldRedactUrlQueryParam` flags them as
    sensitive. New `redactMalformedQuery` walks the query pairs and
    runs the predicate on each key. Three new tests cover prefixed
    keys, non-sensitive keys, and fragment preservation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(redaction): widen key boundary class + tighten dev-channel comment

Two small follow-ups from the latest CodeRabbit review:

[1] Boundary class on key-prefix patterns widened from `[A-Za-z0-9]`
    to `[A-Za-z0-9_-]` so a raw key embedded in a JSON string value
    (`"sk-ant-..."`, `"AIza..."`, `"ghp_..."`, etc.) is still caught.
    Quotes act as delimiters, not blockers — the previous boundary
    class was correct for unquoted text but let quoted keys slip
    through.

[2] Tighten the dev-channel dialog comment in interactiveHelpers.tsx
    so future readers don't misread the security boundary. Skip
    condition is `isChannelsEnabled()` (the channels feature flag
    gate), not KAIROS / KAIROS_CHANNELS as the previous wording
    implied. Comment now matches the code.

Skipped with reason:
- getEffectiveChannelAllowlist divergence from gateChannelServer
  allowlist — by design; the effective-list override is a UI hint
  consumed only by ChannelsNotice for the org-override indicator.
  Trust boundary is enforced by gateChannelServer() reading the
  hardcoded ledger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(redaction,channel): address P1/P2 review findings

P1 - malformed URL fallback secrets:
- Decode percent-encoded query param keys via decodeURIComponent() before
  applying shouldRedactUrlQueryParam (e.g. %74oken -> token)
- Stop userinfo regex at ? and # delimiters to avoid consuming query params
  when matching @ signs in email addresses or fragment delimiters

P2 - channel notice/gate allowlist sync:
- Remove org override path from getEffectiveChannelAllowlist() so
  ChannelsNotice startup guidance uses the same ledger source as
  gateChannelServer's runtime enforcement
- Simplify ChannelsNotice to drop unused sub/policy params and the
  source === 'org' conditional

* fix(channel): apply marketplace matching to permission relays, remove stale OAuth/org-policy blockers, add dev-channel dialog coverage

P1: Thread runtime pluginSource through filterPermissionRelayClients
so findChannelEntry disambiguates same-name plugin entries from
different marketplaces before sending permission request previews.

P2: Remove stale noAuth and policyBlocked branches from ChannelsNotice
that would render '--channels ignored' before reaching the listening
message, confusing non-OAuth users.

P2: Add test coverage that mocks isChannelsEnabled() both true and
false, verifies DevChannelsDialog appears with onAccept marking entries
dev:true in the enabled case, and verifies the disabled branch registers
entries directly without dialog.

* test(dev-channel): clarify count assertion comment + add afterEach with mock.restore()

* fix(channel): mirror marketplace gate in permission relay + restore mock

Two follow-ups from the latest review:

[1] Permission relay predicate no longer relies on findChannelEntry
    alone. After resolving the entry, the predicate now requires a
    runtime pluginSource whose marketplace matches the session
    entry's marketplace for plugin-kind entries — mirroring the
    gateChannelServer check at channelNotification.ts:303-312. A
    `plugin:slack@evilcorp` client whose session allows
    `plugin:slack@anthropic` is now rejected instead of piggy-backing
    on the approved entry to receive permission-request previews.
    Server-kind entries still match on bare name.

[2] bugfixes.test.ts now re-registers the real channelAllowlist
    module in afterEach via a cache-busted reference, so the
    neighbor channelNotification.test.ts continues to import
    getChannelAllowlist after this suite runs. mock.restore() does
    not clear module-level mock.module() overrides in bun (the
    registry is process-global). Pattern matches compact.test.ts:27-36.

Also expanded the dev-map count comment in bugfixes.test.ts to
document the security invariant (a dev entry must never be confused
with a production entry in the allowlist check) per CodeRabbit's
request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(redaction): consolidate into single module + add channel gate tests

Address the three P2 review findings on the central-redaction PR:

[1] Consolidate four redaction modules into src/utils/redaction.ts.
    Previously lived in:
      - src/utils/redaction.ts            (logs/bug reports/transcript shares)
      - src/utils/urlRedaction.ts         (URL display)
      - src/utils/statusRedaction.ts      (/status output)
      - src/utils/diagnostics/redaction.ts (doctor reports)
    The four surfaces share the same regex set / credential lists
    but had drifted into separate per-domain files. Merged into
    one module; deleted the three shim files. Updated six direct
    consumers (openaiShim.ts, ProviderManager.tsx, status.tsx,
    requestSizeBreakdown.ts, diagnostics/issueReport.ts,
    scripts/system-check.ts) and three test files to import from
    redaction.js.

[2] Add gateChannelServer() test coverage.
    src/services/mcp/channelNotification.test.ts: 13 cases for the
    six gate paths (capability, runtime, session, marketplace,
    plugin allowlist, server-entry dev) plus end-to-end register.
    Mocks channelAllowlist.js (GrowthBook-backed) so tests stay
    independent of feature-flag state.

[3] Apply jsonRedactor in transcript share.
    src/components/FeedbackSurvey/submitTranscriptShare.ts now does
    redactSensitiveInfo(jsonStringify(data, jsonRedactor)) — the
    key-aware redaction applies during serialization, and the text
    pass stays as defense in depth for free-form fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(test): align malformed URL fragment expectation with preservation behavior

* fix: address review findings P1 and P2

[P1] Enforce dev flag for server-kind entries in permission relay
predicate, matching gateChannelServer() behavior. Add coverage for
both dev and non-dev server relay paths.

[P2] Drop fragments in malformed URL fallback (redactMalformedQuery)
to match the valid-URL path, preventing credential leaks via
fragment-carried tokens. Update existing tests and add regression
for fragment-only malformed URLs.

* test(relay): add plugin-kind marketplace regression tests

* fix: address review findings P1 and P2

[P1] Add PEM private key redaction pattern to redactSensitiveInfo
so multi-line PEM values are fully consumed instead of leaking
after the first whitespace. Add [ to generic header pattern's
value exclusion set to prevent re-consuming [REDACTED] tokens.

[P2] Use truthy check (Boolean()) for claude/channel capability in
filterPermissionRelayClients to match gateChannelServer's behavior,
rejecting explicit false capabilities.

* fix(debug): redact before JSON-stringify multiline messages

Reorder logForDebugging so redactSensitiveInfo runs before jsonStringify,
ensuring PEM/private-key patterns match the raw (unescaped) message text
rather than the JSON-encoded form where colons and quotes are escaped.

* test(debug): add end-to-end regression for multiline PEM redaction in logForDebugging

Uses mock.module on process.js to capture stderr output and exercises the
full logForDebugging path with multiline PEM private_key input, verifying
the redact-before-JSON-stringify ordering produces redacted output.

* fix(test): preserve original process.env.DEBUG and process.argv in logForDebugging test hooks

* fix: address PR review findings P1-P3/P5-P7

- P1: clear isDebugMode/isDebugToStdErr memoize caches in test beforeEach
      + cache-busting query param for fresh debug.ts imports
- P2: restore mock.module afterAll instead of leaking mock
      + mutate err in-place in logError to preserve name/cause
- P3: post-processing regex absorbs trailing bracket content after [REDACTED]
- P5: (was P3) expand jsonRedactor EXCLUDED_KEYS for maxTokens etc.
- P7: capture HOME/USERPROFILE per-test instead of at module scope

* fix: address CodeRabbit review findings

- interactiveHelpers.tsx: update dev-channel comment — OAuth/org-policy
  gates removed from gateChannelServer(), org policy is not enforced
- channelNotification.test.ts: add afterAll mock.restore() to clean up
  process-global channelAllowlist.js mock
- channelNotification.ts: fix comments — isChannelsEnabled() still reads
  tengu_harbor, not always true
- log.ts: sanitize err.message and err.stack separately so message
  doesn't get replaced with full stack trace
- redaction.ts: add 'i' flag to redactHomePath regex for Windows
  case-insensitive path matching

* fix: address second review round

- interactiveHandler.ts: [P2] redact input_preview via redactSensitiveInfo
  before sending to channel servers
- log.ts: [P3] copy error via Object.assign(Object.create(err), err)
  before sanitizing instead of mutating in-place

* fix: address CodeRabbit second round

- channelPermissions.ts: redact before truncate in truncateForPreview
  so partial credentials don't leak at the 200-char boundary
- interactiveHandler.ts: remove outer redactSensitiveInfo — now
  handled inside truncateForPreview
- log.ts: derive errorInfo.error from already-sanitized sanitizedErr;
  fix Object.assign comment to accurately describe what is copies

* fix: improve permission relay client filtering and enhance redaction functions

* fix: address third review round (P1, P2, P3)

- P1: update test expectations for [REDACTED_*] output format
- P2: add total_tokens, prompt_tokens, completion_tokens to jsonRedactor EXCLUDED_KEYS
- P3: remove ) and } from GENERIC_HEADER_FIELD_PATTERN value capture to prevent content leak after embedded parens
- Fix buildKnownEnvVarPattern capture group to preserve env-var separator ([REDACTED])
- Add & to GENERIC_CREDENTIAL_ENV_PATTERN value exclusion to prevent URL query over-consumption

* fix: address latest reviewer P2/P3 findings (errorLogSink redaction, X_API_KEY/AUTHORIZATION patterns, regression tests)

* fix: address reviewer P1/P2 — bracketed values and multi-word header values

- P1: Remove  and  from value captures in X_API_KEY_PATTERN,
  AUTHORIZATION_PATTERN, GENERIC_HEADER_FIELD_PATTERN,
  GENERIC_CREDENTIAL_ENV_PATTERN so bracketed secrets like
  are fully redacted instead of passing through unchanged.

- P2: Widen header-style value captures to include spaces by removing
   from exclusions, using  as delimiter (stops at newlines
  and URL query separators). Fixes multi-word leaks:
  , ,
  , .

- GENERIC_CREDENTIAL_ENV_PATTERN: add  to negative lookbehind
   to prevent matching  inside
  when the latter is already redacted.

- GENERIC_HEADER_FIELD_PATTERN replacer: skip values starting with
   to preserve specific labels from earlier passes.

- Add 7 regression tests covering both finding categories.

* fix: address reviewer findings P1-P4

P1: Custom enumerable error properties now redacted in log.ts
  logError iterates all own enumerable properties on the original error
  and applies redactSensitiveInfo to string values and jsonRedactor to
  object values, preventing credential-bearing custom fields from leaking
  through the sanitized error. Regression tests added in log.test.ts.

P2: Soften single-source-of-truth claim; migrate easy call sites
  Header comment in redaction.ts updated to acknowledge that specialized
  scanners (secretScanner.ts, xaa.ts) are intentional exceptions.
  src/services/mcp/client.ts and src/services/mcp/auth.ts now use
  jsonRedactor for header redaction instead of ad-hoc key checks.

P3: Fix mock.restore cleanup in channelNotification.test.ts
  Cache-bust the real channelAllowlist module at describe-entry and
  re-register it in afterAll, following the pattern from bugfixes.test.ts.
  mock.restore alone does not clear mock.module overrides in Bun.

P4: Remove unused ChannelGateResult kinds
  Removed 'auth' and 'policy' from the skip kind union and removed
  corresponding dead branches in useManageMCPConnections.ts.

* fix: extract sanitizeError() to fix CI test fragility

The logError tests were failing in CI due to parallel test execution
racing on the module-level errorLogSink singleton. Extract the inline
sanitization logic into an exported sanitizeError() helper and test
that directly — it's pure, has no env-var or sink dependencies, and
doesn't interact with shared mutable state.

* fix: use Object.getPrototypeOf(err) instead of err as prototype in sanitizeError

Object.create(err) sets the original error instance as the prototype of the
sanitized copy, leaking non-enumerable own properties through the prototype
chain. Use Object.getPrototypeOf(err) instead so the prototype is the error
constructor's prototype (e.g. TypeError.prototype), preserving instanceof
checks without exposing the original error's non-enumerable fields.

Add a regression test verifying non-enumerable properties do not leak and
update the prototype-chain test to assert Object.getPrototypeOf result.

* fix: apply key-aware redaction and fail closed on non-serializable error props

- String properties: use jsonRedactor(key, value) instead of
  redactSensitiveInfo(value) so keys like apiKey with innocuous values
  (e.g. 'my-key') are still caught via SENSITIVE_FIELD_SUBSTRINGS.
- Object path: catch now replaces non-serializable/circular references
  with '[REDACTED]' instead of leaving the original object reference.
- Add 2 regression tests for key-aware redaction and fail-closed behavior.

* Update src/utils/log.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: redact bare auth header keys in JSON/header objects

- Add 'auth' to SENSITIVE_FIELD_SUBSTRINGS in src/utils/redaction.ts:109 to match URL/diagnostic redactors treatment of auth
- Add regression test for bare auth header keys in src/utils/diagnostics/redaction.test.ts:88

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: narrow auth matching, redact nested transcript JSONL, fix channel skip message

* fix: address CodeRabbit nits — comment, hint, JSONL fallback redaction

* fix: key-aware malformed JSONL fallback and auth/x-auth in free-form text

* fix: strengthen redactJsonLines trailing rest redaction and auth test assertions

* fix: preserve non-JSON prefix in redactJsonLines fallback and redact it

* fix: tighten redactJsonLines prefix test to exact output assertion

* fix: redact MCP log sink payloads and errorStr before writing to disk

* fix: address P1 findings — URL #-in-password, ;-delimited query params, split channel trust-boundary

- Allow  in URL userinfo password on malformed-URL fallback path
  (new URL() fails when password contains fragment delimiter).
- Redact -delimited sensitive query params by splitting on both & and ;
  in redactMalformedQuery, plus redactSemicolonQueryParams post-processor
  for valid-URL output.
- Restore channelNotification.ts to upstream/main to fully split
  OAuth/org-policy trust-boundary changes from credential redaction PR.

* fix: update callers to match upstream/main function signatures

channelNotification.ts was restored to upstream/main to split
trust-boundary changes from the redaction PR. This commit updates
the three caller sites that previously passed extra arguments:

- ChannelsNotice.tsx: pass getSubscriptionType() + undefined to
  getEffectiveChannelAllowlist (needs 2 args upstream)
- interactiveHandler.ts, channelNotification.test.ts: drop 3rd
  pluginSource arg from findChannelEntry (takes 2 args upstream)

* fix: address reviewer findings — OAuth mock, notice states, marketplace disambiguation

P1: Mock getClaudeAIOAuthTokens and getSubscriptionType in channel
notification tests so they pass on CI where no real OAuth exists.

P2: Restore blocked-auth/org-policy notice states in ChannelsNotice.tsx
so the UI shows the correct blocker when gateChannelServer rejects
unauthenticated users or orgs without channelsEnabled.

P2: Add pluginSource disambiguation to findChannelEntry so same-name
plugin entries from different marketplaces are matched by runtime
source rather than first-match order. Add regression test with
non-matching marketplace first to cover the bug.

* fix: address reviewer findings — relay gate parity and allowlist regression test

- Replace filterPermissionRelayClients in interactiveHandler with inline
  gateChannelServer call so the relay predicate checks ALL gates including
  disabled-channel, auth, org policy, and approved-plugin allowlist, not
  just session entry + marketplace.
- Clean up unused imports (getAllowedChannels, parsePluginIdentifier,
  findChannelEntry, filterPermissionRelayClients).
- Add regression test: gateChannelServer rejects marketplace-matched
  plugin not on approved allowlist (full-gate path).

* fix: redact mixed semicolon secrets in valid-URL path and route OpenAI shim through centralized redactor

P1: Pre-redact semicolon-delimited sensitive query params from the raw
query string in redactUrlForDisplay BEFORE URLSearchParams encodes
; as %3B. Previously model=ok;token=SECRET leaked because
parsed.toString() reserialized to model=ok%3Btoken%3DSECRET, making
it invisible to the post-process pass.

P1: Route openaiShim's redactUrlForDiagnostics through the centralized
redactUrlForDisplay so the semicolon fix, malformed-URL fallback, and
all future redaction improvements apply to OpenAI-compatible
diagnostic logs too. Keep redactSecretValueForDisplay as an additional
safety net after the centralized pass.

Add 3 regression tests for mixed-separator queries.

* Update src/utils/redaction.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: add fragment-query credential regression test and correct dev-channel gate comments

P2: Add regression test for redactUrlForDisplay with query-like credential
in fragment (e.g. #debug?token=SECRET). Fix raw-query pre-processing to
only extract query before the first #, preventing fragment content from
being treated as query parameters.

P3: Update comments in interactiveHelpers.tsx to match the actual gate
order — OAuth and org-policy gates still exist in gateChannelServer()
after restoring to upstream/main; the --dangerously-load-development-
channels flag only bypasses the allowlist gate.

* fix: add port+fragment+@ fallback test and restructure dev-channels dialog tests

* fix: registerDevChannels seam, bare-host #-in-password heuristic, and coverage restructure

* fix: add OAuth and org-policy gate test coverage

- Refactor auth module mock to use mutable variables per test
- Auth gate test: empty OAuth tokens -> kind:auth
- Policy gate test: team subscription without channelsEnabled -> kind:policy

* fix: prefer exact server channel entries before plugin disambiguation

- Return exact server-kind candidate first when candidates include both server and plugin entries with same name
- Added regression test covering mixed server/plugin --channels entries to ensure exact server opt-in is not overridden by plugin candidate
- This prevents a plugin marketplace mismatch from incorrectly rejecting a server the user explicitly selected via server:plugin:slack

* fix: only trust exact [REDACTED] placeholder in generic header field pattern

- Changed GENERIC_HEADER_FIELD_PATTERN to only bypass exact '[REDACTED]' canonical placeholder
- Prevents non-canonical placeholders like '[REDACTED_API_KEY]' or '[REDACTED_actual_secret]' from leaking through
- Updated tests to expect canonical '[REDACTED]' output for generic pattern

* fix: handle bare hosts in malformed URL userinfo fallback

- Added regex to recognize bare hostnames (with optional port) in the fragment heuristic
- Added tests for //alice:sec#ret@host and //alice:sec#ret@host:443

* fix: add relay dispatch path test for non-allowlisted plugin

- Added test using full gateChannelServer predicate in filterPermissionRelayClients
- Mirrors the exact relay dispatch path used in interactiveHandler
- Ensures marketplace-matched plugin not on allowlist is excluded from permission preview

* fix: enhance URL redaction logic to handle valid hosts before fragment

* fix: refine URL redaction logic to ensure valid host checks before fragment

* fix: enhance redaction logic to handle embedded URLs in free-form text

* fix: update redaction logic to remove user info from OpenAI base URL in diagnostic report

* fix: ensure findChannelEntry returns undefined when no exact matches are found

* fix: improve URL redaction logic to remove user info and ensure proper formatting

* fix: enhance redactDiagnosticUrl to preserve query-param values and trailing slashes

* fix: refine redaction logic to preserve meaningful path segments and handle trailing slashes correctly

* fix: enhance redactDiagnosticUrl to preserve literal path segments and handle trailing slashes correctly

* fix: preserve semicolon-delimited query params during redaction

* fix: update redaction logic to support semicolon-delimited query parameters

* fix: enhance redactUrlForDisplay to handle bare hosts and improve fragment redaction

* fix: enhance redactUrlForDisplay to correctly handle username-only userinfo with fragments

* fix: address privacy findings — URL redaction in jsonRedactor, base URL redaction, diagnostic object collapsing, structural channel previews, pluginSource telemetry

* fix: preserve falsey env-presence values in diagnostic redaction

- false, "", and 0 under isEnvPresenceKey keys are now preserved as-is
  instead of misrepresented as "[set]"
- Added regression test for absent/falsey env-presence inputs

* fix: address CodeRabbit findings — sync describe, heartbeat emitter, responsesBody filtering, dev entry precedence

* chore: remove stray Windows path artifact

* fix: update redaction import path in taskReport module

* fix: address CodeRabbit P1-P3 findings and rebase regressions

- F1: rebase onto upstream/main, fix taskReport.ts import path
- F2: Ollama native chat code recovered via rebase (6 functions)
- F3: &-truncation in credential regexes fixed via post-processing pass
- F4: 'tokens' added to jsonRedactor EXCLUDED_KEYS
- F5: redactHomePath case-sensitivity aligned with redactPathForStatus
- F6: credential metadata object preserved in issue report (sensitive-key check
  moved inside type branches)
- F7: heartbeat tests updated for pre-drain write behavior
- F8: reportTask test expects [REDACTED] (matches centralized output)
- rm: stray C:\repo\ Windows path artifact

* fix: address reviewer findings — generic regex &-handling and diagnostic secret-key masking

- Remove & from excluded char classes in 4 generic patterns so they consume
  full secret values (URL-query &-splitting belongs in redactUrlForDisplay).
- Remove now-obsolete &-tail post-processor pass.
- Remove credential from DIAGNOSTIC_SECRET_KEY_PATTERN so issue report
  credential metadata objects are traversed, not collapsed.
- Restore broad isDiagnosticSecretKey check before type dispatch in
  redactDiagnosticObjectInternal so objects/arrays under secret-marked keys
  (auth, password, token, etc.) are masked.
- Update issue report test baseUrl expectation (no trailing &mode=test after
  generic redactor consumes past &).

* fix: address reviewer findings — URL delimiter safety, jsonRedactor #-drop, embedded URL query redaction

- Restore &#; delimiters in generic pattern value classes (F1) so safe
  query tails (&mode=test) survive. Re-add &-tail post-processor for
  non-URL abc&def case.
- Gate redactUrlForDisplay in jsonRedactor to https?:// strings only (F2)
  to prevent #-drop on ordinary text like 'fails after #setup'.
- Add URL query redaction step to redactSensitiveInfo (F3) that extracts
  https?:// URLs from free-form text and routes them through
  redactUrlForDisplay, catching signature/sig params that generic patterns
  miss. Skip already-redacted URLs to avoid double-redaction.

* fix: add Cookie/Set-Cookie semicolon-safe redaction pass, tighten &-tail regex

* fix: COOKIE_PATTERN consume comma-joined multi-cookie values

* fix: address P2 findings — URL redact skip, pre-drain write promise, permission truthy check

* fix: update log.test.ts expectation, add protocol-relative URL support

* fix: enhance redaction for provider env-vars in URLs, preserve safe query params

* fix: enhance redaction for uppercase provider keys and cookie query params

* fix: enhance redaction for bare Bearer and JWT tokens in sensitive info

* fix: update report task test expectations for new redaction format

* fix: limit token exemption to numeric values, protect semicolon cookie query tails

* test: add tests for truncateForPreview to ensure sensitive data redaction

---------

Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-07 22:01:40 +08:00
..