Skip to content

fix(transport): redact rpcUrl credentials before logging them - #5586

Merged
senamakel merged 2 commits into
tinyhumansai:mainfrom
ntdatt812:fix/transport-log-rpcurl-redaction
Sep 11, 2026
Merged

senamakel merged 2 commits into
tinyhumansai:mainfrom
ntdatt812:fix/transport-log-rpcurl-redaction

Conversation

@ntdatt812

@ntdatt812 ntdatt812 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

A connection profile's rpcUrl is stored verbatim — normalizeRpcUrl deliberately keeps query and hash "byte-for-byte intact" and does not touch userinfo — so it can carry user:pass@ or ?token=. That is precisely what redactRpcUrlForLog exists for, and what its own test already pins:

expect(redactRpcUrlForLog('https://user:pass@host.example/rpc?token=secret#/token'))
  .toBe('https://host.example/rpc');

Four construction-time log lines passed the raw URL instead of the redacted one:

file line
services/transport/CloudHttpTransport.ts 38
services/transport/LanHttpTransport.ts 37
services/transport/TransportManager.ts 83, 93

coreRpcClient.ts and configPersistence.ts already route through the helper; these four were the ones left out.

What makes it clear-cut

transport:cloud was already careful with the other secret on the same line — it reports the bearer token by presence, never by value:

log('[transport:cloud] created rpcUrl=%s token=%s', rpcUrl, bearerToken ? 'set' : 'none');
//                                                  ^^^^^^ raw          ^^^^^^^^ masked

So the token is protected and the URL beside it is not.

Reproduction

Captured from the real debug namespaces on this branch's parent, with
rpcUrl = https://svc:HUNTER2@core.example.com/rpc?token=SUPERSECRET#/tok:

transport:cloud [transport:cloud] created rpcUrl=%s token=%s
    https://svc:HUNTER2@core.example.com/rpc?token=SUPERSECRET#/tok set

Both the password and the query token are in the log line. Anyone with DEBUG=transport:* — or anyone reading a log a user pastes into an issue — gets them.

After the change the same line reads https://core.example.com/rpc.

Verification

app/src/services/transport/logRedaction.test.ts — four tests that capture what the namespaces actually emit (by swapping debug.log) rather than asserting on the source. They cover both transports and both TransportManager selection branches, assert the secrets are absent, and assert the origin+path survives so the log stays useful.

Reverting only the three src files, keeping the test:

Tests  4 failed (4)     <- without the fix
Tests  4 passed (4)     <- with it

All four are bug proofs; none of them pass on the old code.

Wider run, using the repo's own config:

pnpm exec vitest run --config test/vitest.config.ts \
  src/services/transport src/utils/__tests__/configPersistence.test.ts \
  src/services/__tests__/coreRpcClient.test.ts
-> Test Files 10 passed (10)   Tests 241 passed (241)

prettier --check ., eslint src and tsc --noEmit each exit 0.

Scope

Deliberately narrow: only the redaction of an already-logged value changes. No log line is added or removed, no behaviour outside logging is touched, and redactRpcUrlForLog itself is unchanged.

Summary by CodeRabbit

  • Bug Fixes

    • Improved connection logging to hide credentials, tokens, and other sensitive URL details.
    • Preserved safe connection information and token-presence reporting in diagnostic logs.
    • Applied protection consistently across cloud, LAN, and transport selection logs.
  • Tests

    • Added coverage verifying that sensitive connection details never appear during transport setup.

A connection profile's rpcUrl is stored verbatim -- normalizeRpcUrl keeps
userinfo, query and hash -- so it can carry `user:pass@` or `?token=`.
That is what redactRpcUrlForLog exists for, and its own test pins exactly
that shape:

  redactRpcUrlForLog('https://user:pass@host.example/rpc?token=secret#/token')
    === 'https://host.example/rpc'

Four construction-time log lines passed the raw URL instead:
CloudHttpTransport, LanHttpTransport, and both TransportManager
selection branches. With DEBUG=transport:* the credential lands in the
log verbatim.

transport:cloud makes the gap plain: it already reports the bearer token
by presence only ('set' / 'none'), then printed the URL beside it in
full.

Four tests capture what the debug namespaces actually emit and assert
the secrets are absent while the origin+path survives. All four are red
without the src change and green with it.

241 tests pass across services/transport, configPersistence and
coreRpcClient. prettier, eslint and tsc all exit 0.
@ntdatt812
ntdatt812 requested a review from a team August 19, 2026 08:47

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

$0.0000 · 0 in / 0 out · 234 embedded · openrouter/openai/text-embedding-3-small

@tinysweeper

tinysweeper Bot commented Aug 19, 2026

Copy link
Copy Markdown

How this change flows

1 changed behaviour across 7 relationships. 4 surrounding behaviours are shown (60 graph nodes walked). 45 further behaviours left out to keep the diagram readable.

flowchart LR
  n0["TransportManager<br/>changed"]:::changed
  n1["getTransport"]:::impacted
  n2["CoreTransport"]:::impacted
  n3["manager"]:::impacted
  n4["manager"]:::impacted
  n0 -->|uses| n2
  n1 -->|uses| n2
  n3 -->|calls| n0
  n3 -->|uses| n0
  n3 -->|tests| n0
  n4 -->|calls| n0
  n4 -->|tests| n0
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading

Green: changed behaviour. Grey: surrounding behaviour. Arrows name the call, use, implementation, or test relationship. Orange: has findings. Red: has a finding that blocks the merge.

tinysweeper 0.1.0

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Aug 19, 2026
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 1b7c9aaf-df2f-432a-9e5d-7e56b2cac867

📥 Commits

Reviewing files that changed from the base of the PR and between fa044d3 and 1ed57ec.

📒 Files selected for processing (4)
  • app/src/services/transport/CloudHttpTransport.ts
  • app/src/services/transport/LanHttpTransport.ts
  • app/src/services/transport/TransportManager.ts
  • app/src/services/transport/logRedaction.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/services/transport/TransportManager.ts
  • app/src/services/transport/LanHttpTransport.ts
  • app/src/services/transport/logRedaction.test.ts
  • app/src/services/transport/CloudHttpTransport.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Transport constructors and selection logs now redact credentials and tokens from RPC URLs. New tests verify safe URL logging and secret removal for cloud, LAN, and manager-based transport creation.

Changes

Transport log URL redaction

Layer / File(s) Summary
Apply URL redaction to transport logs
app/src/services/transport/CloudHttpTransport.ts, app/src/services/transport/LanHttpTransport.ts, app/src/services/transport/TransportManager.ts
Cloud and LAN transport logs, including manager selection logs, now redact RPC URLs. Cloud logs still report whether a bearer token is set.
Validate secret-free logging
app/src/services/transport/logRedaction.test.ts
Tests capture debug output and verify that credentials and tokens are absent while the safe URL portion remains logged.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 1ed57

This change redacts credentials from transport URLs while preserving useful origin and path information; no actionable merge-blocking risk remains after normal checks and review.

Poem

A rabbit guards the logs tonight,
No secret hops into the light.
URLs keep their safer face,
Tokens vanish without a trace.
Tests thump paws: “The path is right!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: redacting RPC URL credentials before logging them.
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.
  • Fix all pre-merge checks with AI

Warning

Your free Security trial is over. An organization admin can upgrade to Advanced for continuous pull request security review or dismiss this notice.


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

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot removed the bug label Sep 1, 2026

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.0231 · 64,501 in / 13,105 out · 14,152 cached (22%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2 · 234 embedded
critique:    $0.0017 · 23,139 in / 136 out    · 0 cached (0%)       · deepseek/deepseek-v4-flash
security:    $0.0017 · 23,097 in / 130 out    · 0 cached (0%)       · deepseek/deepseek-v4-flash
tests:       $0.0186 · 12,801 in / 12,626 out · 9,975 cached (78%)  · z-ai/glm-5.2
description: $0.0011 · 5,464 in  / 213 out    · 4,177 cached (76%)  · z-ai/glm-5.2

Comment on lines +23 to +40
/** Collect everything the `debug` namespaces emit while `fn` runs. */
function captureDebug(fn: () => void): string {
const lines: string[] = [];
const previous = debug.disable();
const previousLog = debug.log;
debug.enable('transport:*');
debug.log = (...args: unknown[]) => {
lines.push(args.map(String).join(' '));
};
try {
fn();
} finally {
debug.log = previousLog;
debug.disable();
if (previous) debug.enable(previous);
}
return lines.join('\n');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium tests likely

Await async callbacks in captureDebug so async logs are captured

captureDebug calls fn() synchronously and restores debug.log in a finally block. getTransport() is async — it awaits rpcUrl/sessionToken resolution before logging — so the TransportManager log line fires in a microtask after captureDebug has restored debug.log and disabled the namespace. The two TransportManager tests therefore capture empty output, and expectNoSecrets(output) passes trivially. They would still pass if the redaction in TransportManager.ts were reverted, because the secret-leaking log line is never captured.

Making captureDebug async and awaiting fn() keeps the capture window open. All four callers must then await captureDebug(...), and the two TransportManager tests must await getTransport() inside the callback, e.g.:

const output = await captureDebug(async () => {
  await createTransportManager(profile('cloud')).getTransport();
});

The CloudHttpTransport and LanHttpTransport callers only need a leading await added.

Suggested change
/** Collect everything the `debug` namespaces emit while `fn` runs. */
function captureDebug(fn: () => void): string {
const lines: string[] = [];
const previous = debug.disable();
const previousLog = debug.log;
debug.enable('transport:*');
debug.log = (...args: unknown[]) => {
lines.push(args.map(String).join(' '));
};
try {
fn();
} finally {
debug.log = previousLog;
debug.disable();
if (previous) debug.enable(previous);
}
return lines.join('\n');
}
/** Collect everything the `debug` namespaces emit while `fn` runs. */
async function captureDebug(fn: () => void | Promise<void>): Promise<string> {
const lines: string[] = [];
const previous = debug.disable();
const previousLog = debug.log;
debug.enable('transport:*');
debug.log = (...args: unknown[]) => {
lines.push(args.map(String).join(' '));
};
try {
await fn();
} finally {
debug.log = previousLog;
debug.disable();
if (previous) debug.enable(previous);
}
return lines.join('\n');
}

[RULE] test-asserts-nothing ·

@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. labels Sep 1, 2026
@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Maintainer review (merge-readiness sweep).

Disclosure first: earlier in this sweep I ran GitHub's "Update branch" on this PR, which pushed a merge commit — 1ed57ecc "Merge branch 'main' into fix/transport-log-rpcurl-redaction", authored by the fleet account — onto your branch. No content of mine, no rebase, no force-push, your commits untouched. The sweep was re-scoped to review-only for external contributions afterwards, so nothing further will be pushed here. Flagging it so the commit is not a mystery.

Assessment: sound

Checked against current main, not the original base:

  • Still needed. All three log sites are unredacted on main today — CloudHttpTransport.ts:38, LanHttpTransport.ts:37, TransportManager.ts:83 and :93 all interpolate the raw rpcUrl.
  • The helper it imports exists and is the established one. app/src/utils/redactRpcUrlForLog.ts is already on main and already used by coreRpcClient.ts (:479, :655, :779), so this is closing the gap in the one module that was missed rather than introducing a parallel mechanism. That is the right fix and the right helper.
  • No duplicate coverage. logRedaction.test.ts is new; the existing CloudHttpTransport.test.ts / LanHttpTransport.test.ts / TransportManager.test.ts do not cover redaction.
  • The comment on the cloud site — noting the bearer token was already presence-only while the URL was not — is the sort of thing that stops the line regressing later. Worth keeping.

Small and correct; the 93 lines of test for 14 lines of production change are well spent given this is a credential-leak path.

State

MERGEABLE. The CANCELLED checks in the list are superseded runs from the branch update, not failures — the live run against today's main is green so far. The BLOCKED merge state is the repo's two-approval gate, which a maintainer has to clear; I am not approving anything in this sweep.

Nothing needed from you.

@senamakel
senamakel merged commit 961040e into tinyhumansai:main Sep 11, 2026
35 of 43 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Sep 11, 2026
senamakel added a commit to HDZTony/openhuman that referenced this pull request Sep 11, 2026
…-rpcurl-redaction\n\nfix(transport): redact rpcUrl credentials before logging them\n
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants