Skip to content

docx-mcp: harden vitest setup against env-only PATH_NOT_ALLOWED failures #104

@stevenobiajulu

Description

@stevenobiajulu

Background

The docx-mcp test suite uses openDocument, which gates file reads through enforceReadPathPolicy against SAFE_DOCX_ALLOWED_ROOTS. When the env var is unset, the runtime defaults to [$HOME, os.tmpdir()]. A vitest setupFiles entry — packages/docx-mcp/src/testing/setup-path-roots.ts — is already responsible for adding cwd and the repo path so tests work outside of $HOME.

In practice we still hit env-only test failures when the suite runs from a worktree under /tmp (which is a symlink to /private/tmp on macOS). Two contributing factors:

  1. The setup currently resolves "repo root" as path.resolve(__dirname, '..', '..', '..') from src/testing/, which lands at packages/ rather than the actual workspace root.
  2. Setup adds raw paths (e.g. /tmp/worktree-foo) but never realpaths them. The policy realpaths the requested file (/tmp/x/private/tmp/x), and resolveAllowedRoots() realpaths the env entries lazily, but a single canonicalization at setup makes the behavior obvious and avoids relying on lazy resolution timing.

We also lose ~10 minutes every time we hit a PATH_NOT_ALLOWED because assertSuccess(result) only does expect(result.success).toBe(true) — the assertion failure shows "expected true received false" and discards the structured result.error.code/hint.

Reproduction

git worktree add /tmp/repro main
cd /tmp/repro
npm run test:run --workspace=packages/docx-mcp
# Expect: assertions like `open` fail without surfacing `PATH_NOT_ALLOWED`.

Proposed Fixes

Three layered fixes, in increasing depth:

  1. Test setup self-configures the allowlist robustly. In setup-path-roots.ts:

    • Resolve the workspace root with the correct number of segments (packages/docx-mcp/src/testing → up 4 to the workspace root, not 3).
    • Run each candidate through fs.realpathSync before joining into SAFE_DOCX_ALLOWED_ROOTS, so /tmp/x and /private/tmp/x collapse to the same canonical form. This matches what enforceReadPathPolicy does at request time.
    • Keep process.cwd(), os.tmpdir(), and $HOME as candidates.
  2. Verify symlink-equivalent root handling end-to-end. path_policy.ts already realpaths both sides, but we currently lack a regression test that pins this on macOS-style /tmp/private/tmp semantics. Add one so we don't regress.

  3. Better assertion fingerprint. Update assertSuccess in packages/docx-mcp/src/testing/session-test-utils.ts to include result.error.code and result.error.message in both the expect() message and the thrown Error. A failing openDocument should immediately tell us PATH_NOT_ALLOWED (or FILE_NOT_FOUND, etc.) instead of "expected true received false".

These are low-risk and self-contained.

Acceptance

  • setup-path-roots.ts realpaths each candidate root and uses the actual workspace root.
  • A regression test ensures path policy treats /tmp and /private/tmp as equivalent on macOS-style filesystems.
  • assertSuccess includes error.code and error.message in failure output.
  • npm run test:run --workspace=packages/docx-mcp passes from a /tmp worktree without setting SAFE_DOCX_ALLOWED_ROOTS manually.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions