Skip to content

fix(logs): sanitize per-server log filename for namespaced registry names (MCP-1111)#604

Merged
Dumbris merged 6 commits into
mainfrom
fix/mcp-1111-registry-log-sanitize
Jun 5, 2026
Merged

fix(logs): sanitize per-server log filename for namespaced registry names (MCP-1111)#604
Dumbris merged 6 commits into
mainfrom
fix/mcp-1111-registry-log-sanitize

Conversation

@Dumbris
Copy link
Copy Markdown
Member

@Dumbris Dumbris commented Jun 5, 2026

Summary

Backend half of #598 (child of MCP-1090). Official modelcontextprotocol/registry server names are namespace/name (e.g. io.github.evidai/polymarket-guard). The per-server log filename is derived from the server name, so the unsanitized / turned server-io.github.evidai/polymarket-guard.log into a nested directory instead of a single flat log file.

What changed

  • internal/logs/logger.go: new serverLogFilename / sanitizeServerLogName helpers. All three derivation sites now route through them so the writers and the tail reader agree on one flat path:
    • CreateUpstreamServerLogger
    • CreateCLIUpstreamServerLogger
    • ReadUpstreamServerLogTail
    • Sanitization: any char outside [A-Za-z0-9._-]_. Result is always a single path element, so it can't escape the log dir or create nested dirs.
  • docs/configuration.md, docs/cli/management-commands.md: note the sanitization next to the server-{name}.log description (ENG-9).

Registry-add percent-decode (already on main)

The issue's first bullet — percent-decode the registry Add route server name — is already on origin/main via decodePathParam in internal/httpapi/server.go (PR #591 / MCP-1056), covered by TestAddFromRegistry_SlashServerIDUnescaped. No change needed there; this PR completes the remaining log-path half.

Tests (TDD, written first, watched fail)

internal/logs/logger_test.go:

  • TestServerLogFilename_SanitizesPathSeparators — table: plain, namespaced /, Windows \, :, spaces; asserts result is a single path element.
  • TestCreateUpstreamServerLogger_NamespacedNameFlatFile — end-to-end regression with io.github.evidai/polymarket-guard: a flat server-io.github.evidai_polymarket-guard.log is created, no nested server-io.github.evidai/ dir, and ReadUpstreamServerLogTail round-trips the same raw name back to it.

Verification

  • go test ./internal/logs/... -race
  • go build ./cmd/mcpproxy
  • ./scripts/run-linter.sh ✅ (0 issues)
  • gofmt clean

Open PR — not merging (Gate 3). QA self-dispatch to follow.

Related #598

Official modelcontextprotocol/registry server names are namespace/name
(e.g. io.github.evidai/polymarket-guard). The per-server log filename is
derived from the server name, so an unsanitized "/" turned
server-<ns>/<name>.log into a nested directory instead of a single flat
log file.

Add serverLogFilename/sanitizeServerLogName and route all three
derivation sites (CreateUpstreamServerLogger, CreateCLIUpstreamServerLogger,
ReadUpstreamServerLogTail) through it so writes and the tail reader agree
on one flat path. Characters outside [A-Za-z0-9._-] become "_".

The companion registry-add percent-decode is already on main
(decodePathParam in internal/httpapi/server.go, PR #591 / MCP-1056,
covered by TestAddFromRegistry_SlashServerIDUnescaped), so this change
completes the remaining log-path half.

Related #598
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Jun 5, 2026

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 41f94ea
Status: ✅  Deploy successful!
Preview URL: https://2d5ab194.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-mcp-1111-registry-log-sa.mcpproxy-docs.pages.dev

View logs

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 5, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 72.00000% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/mcpproxy/upstream_cmd.go 0.00% 3 Missing ⚠️
internal/server/server.go 0.00% 3 Missing ⚠️
internal/logs/logger.go 93.75% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

The lumberjack writer keeps the per-server log file open for the
logger's lifetime, and Windows cannot remove an open file. t.TempDir's
cleanup asserts RemoveAll succeeds, failing the Cross-Platform Logging
Tests job on windows-latest. Switch to os.MkdirTemp with a best-effort
cleanup (mirroring TestE2E_LogRotation) and stop asserting on Sync().

Related #598
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

📦 Build Artifacts

Workflow Run: View Run
Branch: fix/mcp-1111-registry-log-sanitize

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (24 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27012727518 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

Dumbris added 4 commits June 5, 2026 13:50
…lper

Addresses Codex review on PR #604: the writer sanitized per-server log
filenames but two READ sites still derived the raw server-%s.log path,
so GET /api/v1/servers/{id}/logs and no-daemon CLI reads still 404'd for
namespace/name registry servers. Export ServerLogFilename and route both
read sites through it; tie the read-path filename to the writer's flat
file in the regression test.

Related #598
Third read site for the namespaced-name log fix (Codex review round 2 on
PR #604): the daemon-backed CLI logs client built /servers/%s/logs with the
raw name, so 'mcpproxy upstream logs io.github.x/name' missed the chi route.
PathEscape the name (matching the registry client) + regression test.

Related #598
Closes the daemon server-side half of the namespaced-name log fix (Codex
review round 3 on PR #604): the client now encodes the name, but the handler
read chi.URLParam("id") raw, so %2F reached the lookup un-decoded. Route it
through decodePathParam (matching handleAddFromRegistry) + router regression
test proving /servers/io.github.evidai%2Fpolymarket-guard/logs decodes.

Related #598
CodeQL go/path-injection flagged the daemon log reader
(internal/server/server.go) where a user-controlled server name flows into
the log file path. logs.ServerLogFilename already sanitizes the name to a
single path element, so the flow is safe, but CodeQL does not recognize the
strings.Map char-whitelist as a barrier. Add an explicit filepath.Clean +
prefix containment guard at both log-read sites (daemon reader and the
no-daemon CLI reader, which feeds tail) so the resolved path can never escape
the log directory. Defense-in-depth + clears the alert without weakening any
check. Valid namespaced names (io.github.owner/repo) are unaffected — their
sanitized filename stays a single element under logDir.

Related #604
Comment thread internal/server/server.go Dismissed
@Dumbris Dumbris merged commit 8b7b156 into main Jun 5, 2026
31 checks passed
@Dumbris Dumbris deleted the fix/mcp-1111-registry-log-sanitize branch June 5, 2026 12:18
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.

3 participants