Skip to content

fix(mcp): reject local targets over HTTP transport#196

Open
rodboev wants to merge 1 commit into
NVIDIA:mainfrom
rodboev:pr/mcp-http-local-target-guard
Open

fix(mcp): reject local targets over HTTP transport#196
rodboev wants to merge 1 commit into
NVIDIA:mainfrom
rodboev:pr/mcp-http-local-target-guard

Conversation

@rodboev

@rodboev rodboev commented Jun 24, 2026

Copy link
Copy Markdown

Summary

HTTP MCP callers can currently pass local filesystem targets to scan_skill, and the server forwards those paths into the normal scan graph. This adds an MCP transport guard so routable HTTP use rejects local paths and file:// targets before the graph can read them, while CLI and stdio MCP scans keep their existing local-path behavior.

Closes #191

Root cause

scan_skill forwards the caller-provided target to run_scan without transport context. The input resolver then treats local files and directories as valid scan targets, which is correct for trusted local use but too broad for unauthenticated HTTP exposure.

Diff Notes

  • Add an MCP-layer local-target guard for HTTP server calls.
  • Preserve local filesystem scans for CLI and stdio/default run_scan use.
  • Keep existing URL allowlist and private-IP protections in the input layer unchanged.
  • Add focused MCP unit tests for local-target rejection, UNC-style path classification, remote-target allowance, and default local-target compatibility.

Scope

This does not add MCP authentication, change the generic input resolver, or alter report output. PR #193 covers the documentation warning slice; this PR covers the code guard.

Verification

  • python -m pytest tests/unit/test_mcp_server.py -v -> 18 passed, 1 warning
  • uv run ruff check src/ tests/ -> passed
  • uv run ruff format --check src/skillspector/mcp_server.py tests/unit/test_mcp_server.py -> 2 files already formatted
  • uv run ruff format --check src/ tests/ -> still reports pre-existing drift outside the target scope in src/skillspector/nodes/analyzers/static_runner.py, tests/nodes/analyzers/test_binary_and_pe3_filtering.py, tests/nodes/analyzers/test_mp2_regex_backtracking.py, and tests/nodes/test_llm_analyzer_base.py
  • CI Lint & Test (Python 3.12), Lint & Test (Python 3.13), and DCO Check - pending maintainer approval if fork checks are gated

Notes

Follow-up to maintainer review on #36 (review).

Signed-off-by: Rod Boev <rod.boev@gmail.com>

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving — solid, fail-closed hardening: HTTP transport sets allow_local_targets=False and _is_local_target() rejects file://, absolute/drive, and UNC paths before the graph runs, while remote schemes pass through. Test coverage is thorough (local/file:// rejected, remote allowed, UNC + relative-path classification, transport policy stdio=allow/http=disallow, and default-compat).

Non-blocking notes:

  • _is_local_target() calls Path(target).expanduser().exists() for the relative/no-scheme case, i.e. an unauthenticated HTTP caller can make the server stat() arbitrary relative paths (a minor file-existence oracle). The read itself is still blocked, so impact is low, but consider dropping the .exists() probe and classifying unknown bare strings as non-local.
  • Touches mcp_server.py alongside #204, and the README note in #193 documents this behavior — expect a merge-order/rebase with those.

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.

[Security] mcp server HTTP transport exposes local file read and SSRF when bound to a routable interface

2 participants