Skip to content

Log request paths with the query string filtered - #484

Merged
lindesvard merged 1 commit into
mainfrom
agent/redact-query-params-in-request-logs
Sep 4, 2026
Merged

Log request paths with the query string filtered#484
lindesvard merged 1 commit into
mainfrom
agent/redact-query-params-in-request-logs

Conversation

@lindesvard

@lindesvard lindesvard commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

What changed and why

Request logging recorded request.url verbatim. The logger's redaction only matches object keys, so a URL held as a plain string went into the log line untouched. Any route that accepts something sensitive in the query string ended up with that value in the logs; the MCP endpoint accepts ?token=, so its tokens were the concrete case.

This adds sanitizeUrl, which keeps the path and every ordinary parameter and replaces the value of any parameter whose lowercased name matches the sensitive-key list the logger already uses. Matching is substring and case-insensitive, the same rule redactSensitive applies to object keys. A URL with no query string, or an empty one, comes back unchanged, and a malformed query string does not throw. The query is rebuilt from the raw text rather than through URLSearchParams so untouched values keep their original encoding.

The filter is applied at the three places a raw URL is logged, and inside redactSensitive itself for string values under a key containing url. The second one is the backstop: code that logs a URL later is covered without having to remember the helper.

SENSITIVE_KEY_PATTERNS is now exported from packages/logger so there is one list, not two copies. apps/api/src/utils/sanitize-url.ts re-exports the implementation under the name the API side uses.

Docs and the MCP settings page led with the query-parameter form of the endpoint. They now lead with Authorization: Bearer and show the query form as the fallback for clients that cannot set headers. Query-parameter auth still works exactly as before; packages/mcp/src/auth.ts is untouched.

Where the raw URL was logged

  • apps/api/src/hooks/request-logging.hook.ts:49url: request.url in the non-tRPC branch. The tRPC branch at line 31 already dropped the query and is unchanged.
  • apps/api/src/app.ts:417url: request.url in the error handler's request context. That context is now built by buildErrorRequestContext in apps/api/src/utils/errors.ts so it can be tested without standing up the whole app; the fields are the same. Its query and headers are objects, so the logger already redacted those by key.
  • apps/api/src/utils/rate-limiter.ts:52url: req.url in the rate-limit warning.
  • packages/logger/index.ts:25 — the SENSITIVE_KEY_PATTERNS list, and redactSensitive at lines 49-82, which only ever looked at keys.

Docs and UI: apps/public/content/docs/mcp/index.mdx (authentication, token format, Claude Desktop and Claude Code sections), apps/public/content/features/mcp.json (setup step 3 and the authentication FAQ answer), apps/start/src/routes/_app.$organizationId.$projectId.settings._tabs.mcp.tsx:35 and :177.

Tests

  • apps/api/src/utils/sanitize-url.test.ts — replacement and path preservation, other parameters kept, TOKEN/Token/accessToken, repeated parameters, several sensitive parameters in one URL, no query string, empty query string, malformed query string, encoding preserved.
  • apps/api/src/hooks/request-logging.hook.test.ts — the logged object for a non-tRPC request contains no substring of the token value; the tRPC branch still logs the bare path; /track bodies are still attached now that the URL is filtered.
  • apps/api/src/utils/errors.test.ts and apps/api/src/utils/rate-limiter.test.ts — the other two payloads, so all three sites are pinned.
  • packages/logger/index.test.tsredactSensitive({ url: '/x?token=abc&foo=1' }) replaces the token and keeps foo=1, a non-string url value still goes through the existing recursion, key-based redaction is unchanged. Needed a vitest.config.ts in that package, matching the other packages.

Ran: vitest run in packages/logger (6 tests) and in apps/api for src/utils, src/hooks, src/bots (11 files, 81 tests) — all pass. tsc --noEmit is clean for @openpanel/logger, @openpanel/api and start after pnpm db:codegen. The API integration suites that need Postgres, ClickHouse and Redis were not run; nothing was available to connect to in this environment.

Left out on purpose

  • Query-parameter auth stays. Existing client configurations depend on it, and removing it is a separate decision.
  • The Raycast snippet on the settings page still puts the token in the URL. Its install form takes a URL only; the other five clients now use a headers block.
  • SENSITIVE_KEY_PATTERNS includes short entries like ip, so a parameter named e.g. recipient will be replaced too. That is the existing behaviour for object keys and now applies to query parameters as well. Narrowing the list is a change with its own blast radius and is not part of this.
  • Formatting and lint complaints that biome check already reports on the files touched here were left alone.

Summary by CodeRabbit

  • Security

    • Sensitive credentials are now redacted from request URLs in error, warning, and request logs.
    • Error logging preserves the most accurate request body available.
  • Authentication

    • MCP setup guidance now recommends Authorization: Bearer headers instead of embedding tokens in URLs.
    • Client configuration examples use headers by default, with URL tokens retained as a fallback.
  • Documentation

    • Updated MCP documentation and feature guidance explain safer authentication options and token exposure risks.

Request logging records `request.url` verbatim, and the logger's redaction
only matches object keys, so a URL held as a string passes through untouched.
Routes that accept credentials in the query string (the MCP endpoint takes
`?token=`) therefore wrote those values into log lines.

Filter sensitive parameters out of the URL at the three places a raw URL is
logged, and apply the same filtering inside the logger's own redaction so
future callers are covered without having to remember the helper. The
sensitive-parameter list stays a single definition in the logger package.

Docs and the MCP settings page now lead with the `Authorization: Bearer`
form and present the query parameter as the fallback for clients that
cannot set headers. Query-parameter auth itself is unchanged.

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

coderabbitai Bot commented Sep 4, 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: defaults

Review profile: CHILL

Plan: Team

Run ID: 5e55a4e6-bc5d-4cc9-945a-b9e83d3cc1a3

📥 Commits

Reviewing files that changed from the base of the PR and between 15794bd and a63865d.

📒 Files selected for processing (15)
  • apps/api/src/app.ts
  • apps/api/src/hooks/request-logging.hook.test.ts
  • apps/api/src/hooks/request-logging.hook.ts
  • apps/api/src/utils/errors.test.ts
  • apps/api/src/utils/errors.ts
  • apps/api/src/utils/rate-limiter.test.ts
  • apps/api/src/utils/rate-limiter.ts
  • apps/api/src/utils/sanitize-url.test.ts
  • apps/api/src/utils/sanitize-url.ts
  • apps/public/content/docs/mcp/index.mdx
  • apps/public/content/features/mcp.json
  • apps/start/src/routes/_app.$organizationId.$projectId.settings._tabs.mcp.tsx
  • packages/logger/index.test.ts
  • packages/logger/index.ts
  • packages/logger/vitest.config.ts

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


📝 Walkthrough

Walkthrough

The change adds shared URL query sanitization, applies it to API request and error logging, adds focused tests, and updates MCP documentation and client examples to prefer bearer-header authentication.

Changes

Credential redaction and MCP authentication

Layer / File(s) Summary
Shared URL sanitization
packages/logger/index.ts, packages/logger/index.test.ts, packages/logger/vitest.config.ts
The logger exports sensitive-key patterns, adds sanitizeUrlQuery, and sanitizes URL-valued fields during object redaction. Tests cover sensitive parameters, encoding, malformed queries, and recursive values.
API log context sanitization
apps/api/src/utils/sanitize-url.ts, apps/api/src/utils/errors.ts, apps/api/src/app.ts, apps/api/src/hooks/*, apps/api/src/utils/rate-limiter.ts, apps/api/src/utils/*.test.ts
Request logs, error contexts, and rate-limit warnings use sanitized URLs. Error contexts prefer rawBody when available. Tests cover redaction, tRPC paths, request bodies, and rate-limit warnings.
MCP authentication updates
apps/public/content/docs/mcp/index.mdx, apps/public/content/features/mcp.json, apps/start/src/routes/...mcp.tsx
MCP guidance and client snippets use Authorization: Bearer headers as the primary method. Token query parameters remain available as a fallback.

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

Merge Risk: 🟡 Moderate · up to a6386

MCP setup now prioritizes bearer authentication, but self-hosted HTTP endpoints may expose copied bearer credentials in transit because the UI does not establish or warn about an HTTPS requirement. Resolve the endpoint scheme contract or add an HTTP safeguard before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 13 files. (2 skipped:… 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 summarizes the main change: filtering query strings from request paths before logging.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 13 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/redact-query-params-in-request-logs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lindesvard
lindesvard merged commit 33925f0 into main Sep 4, 2026
13 checks passed
@lindesvard
lindesvard deleted the agent/redact-query-params-in-request-logs branch September 4, 2026 09:42
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