Skip to content

fix(media): reject Windows device names and \??\ paths as media sources - #85

Draft
Hao0321 wants to merge 1 commit into
mainfrom
security/windows-device-paths
Draft

Hao0321 wants to merge 1 commit into
mainfrom
security/windows-device-paths

Conversation

@Hao0321

@Hao0321 Hao0321 commented Oct 6, 2026

Copy link
Copy Markdown
Owner

What changed

#47 made the Windows media-path guard reject UNC paths, \\.\ paths and \\?\ device paths. It still accepted two other forms:

  • DOS device names used as a file name. Examples: C:\media\NUL, nul.txt, NUL .txt, CON, AUX, PRN, COM1–COM9, LPT1–LPT9, the superscript forms COM¹²³ and LPT¹²³, CONIN$ and CONOUT$. Win32 opens a device for these instead of a file. Matching ignores letter case, extensions, trailing spaces, and : (as in C:NUL).
  • The NT object-manager prefix \??\, for example \??\UNC\host\share\x. It hands the path to the object manager without normal parsing, so it can still reach a network share.

Both classifiers now reject these on Windows:

  • TypeScript isWindowsNetworkPath in src/shared/localMediaPath.ts
  • Rust is_windows_network_path in src-tauri/src/main.rs, used by allow_path

Both stay behind their existing platform gates (platform === "win32" and cfg!(windows)), so nothing changes on macOS or Linux, where NUL is an ordinary file name.

Some cases are rejected deliberately even though they go beyond the minimum; each is explained in a code comment:

  • Every path component is checked, not only the last one. The existing updateCache.ts check already works this way.
  • COM0 and LPT0 are rejected.
  • \\?\C:\media\NUL is rejected.

The error message keeps its prefix and now says to copy the file under an ordinary file name.

User journey and platform

Windows only: importing, previewing or analysing media whose path uses a device name or \??\ is refused before ffprobe or the asset protocol touches it. App-generated cache paths and IDs cannot contain these names.

Validation

  • npm run typecheck, npm run build: passed.
  • npm test: 279 files passed, 1 skipped; 2212 tests passed, 5 skipped.
  • npm run source:scan and npm run source:verify:self-test: GREEN.
  • npx tsx scripts/architecture-check.ts: ALLOW.
  • npm run test:journey with FFmpeg 6.1.1: GREEN.
  • New tests:
    • 32 rejected paths and 7 allowed controls, such as null.mp4, COM10.mp4, clip.nul.mp4 and nul-cut\clip.mp4.
    • A POSIX test showing nothing is rejected on Linux or macOS.
    • Caller-level tests covering inspectMedia, analyzeSceneCuts, probeMedia, analyzeAutomaticCaptionTranscript and resolveMediaPath.
    • Before the fix, 33 of the new TypeScript tests fail and the Rust test panics.
  • Rust: on Linux with Rust 1.98.1 and the CI packages, cargo build --locked --manifest-path src-tauri/Cargo.toml --features community-desktop,tauri/custom-protocol passed. CI's own command, cargo test … windows_network_and_device_paths_are_not_local_media, passed.
  • The Rust and TypeScript classifiers give byte-identical results on 763,338 generated paths.
  • Not verified: behaviour on a real Windows host.

Security and provenance

No new dependencies, network access, process execution, file writes, permissions, media, fonts or models. No secrets or private footage.

Not covered here, as follow-ups:

  • src/application/updateCache.ts and scripts/lib/editkin-mcp-generation-contract.mjs have their own, narrower reserved-name checks.
  • Mapped network drive letters and junctions need Windows canonicalisation.

DCO

The commit has a Signed-off-by: trailer.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YQbUWKu73ka7dZ58vWVzgY


Generated by Claude Code

The guard added in #47 still accepted reserved DOS device names such as
C:\media\NUL, nul.txt, NUL .txt, COM1, LPT¹ or CONOUT$, which Win32
opens as devices instead of files, and the \??\ prefix, which hands
paths such as \??\UNC\host\share to the NT object manager unparsed.
Reject both in isWindowsNetworkPath and its Rust twin, so every
assertLocalMediaPath caller and the Tauri asset scope guard are covered.
Names are matched in every path component, in any case and ignoring an
extension or trailing spaces; COM0/LPT0 are rejected conservatively.
POSIX paths are unchanged.

Signed-off-by: Hao0321 <126182090+Hao0321@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQbUWKu73ka7dZ58vWVzgY
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