Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
db92b8b
test(security): define path authority regression contract
seonghobae Aug 15, 2026
b62c102
fix(security): define filesystem path authority primitives
seonghobae Aug 15, 2026
1bb6002
docs(security): record path authority evidence
seonghobae Aug 15, 2026
4189050
test(security): cover path authority primitives
seonghobae Aug 15, 2026
2658279
test(security): bind RED contract to path authority helper
seonghobae Aug 15, 2026
b8258d1
fix(security): enforce analysis filesystem authority
seonghobae Aug 15, 2026
2344d78
style(path-authority): satisfy Ruff line limits
seonghobae Aug 15, 2026
1240220
style(path-authority): satisfy Ruff line limits
seonghobae Aug 15, 2026
4eafd7d
test(security): require actionable source path rejection
seonghobae Aug 15, 2026
f4af9b4
fix(path-authority): preserve worker-owned missing-file handling
seonghobae Aug 15, 2026
2804aef
test(path-authority): cover native resolution boundaries
seonghobae Aug 15, 2026
7447418
docs(path-authority): preserve missing-file orchestration contract
seonghobae Aug 15, 2026
f0fd2dc
test(path-authority): require actionable job failures
seonghobae Aug 15, 2026
ca79abe
fix(path-authority): preflight native request authority
seonghobae Aug 15, 2026
8b6e74f
test(path-authority): cover request preflight branches
seonghobae Aug 15, 2026
ed0b3a3
test(path-authority): reject file roots before cache IO
seonghobae Aug 15, 2026
7863368
test(path-authority): enforce writable root type at IO boundary
seonghobae Aug 15, 2026
7211cfe
fix(path-authority): require writable directory roots
seonghobae Aug 15, 2026
6bffd96
docs(changelog): record filesystem authority hardening
seonghobae Aug 15, 2026
9be6d69
docs(security): define writable root type contract
seonghobae Aug 15, 2026
f764d62
test(path-authority): cover Win32 filename device aliases
seonghobae Aug 15, 2026
6ba7ac0
fix(path-authority): reject Win32 device and stream aliases
seonghobae Aug 15, 2026
2761782
docs(security): record Win32 device alias boundary
seonghobae Aug 15, 2026
b226b12
test(path-authority): fail foreign-host source before progress
seonghobae Aug 15, 2026
c0a8ef4
fix(security): reject foreign-host paths during request preflight
seonghobae Aug 15, 2026
e064f01
test(security): cover path authority preflight and I/O races
seonghobae Aug 15, 2026
eb206d0
test(security): cover path resolution failure branches
seonghobae Aug 15, 2026
7655f6c
docs(security): align path authority evidence with native preflight
seonghobae Aug 15, 2026
3a18ca3
test: reject escaping cache file symlinks
seonghobae Aug 15, 2026
bb2df34
fix: revalidate exact cache file authority
seonghobae Aug 15, 2026
1b81c5d
docs: record exact cache link authority
seonghobae Aug 15, 2026
40bb115
docs: note cache link hardening
seonghobae Aug 15, 2026
065bbf0
chore: stage bounded formatter repair
seonghobae Aug 16, 2026
7a27b5a
style: format path authority API
github-actions[bot] Aug 16, 2026
6133f80
docs: clarify foreign-host path rejection
seonghobae Aug 16, 2026
7b89b7e
test(path): reject Win32 leading-space normalization
seonghobae Aug 16, 2026
8baa5d8
fix(path): reject Win32 leading-space normalization
seonghobae Aug 16, 2026
501bb65
docs(path): record Win32 leading-space normalization
seonghobae Aug 16, 2026
e020e4c
docs(changelog): note Win32 normalization hardening
seonghobae Aug 16, 2026
21c7937
test(security): reject Win32 console device aliases
seonghobae Aug 16, 2026
432771e
fix(security): reject Win32 console device aliases
seonghobae Aug 16, 2026
f423bb4
docs(security): cite Win32 console device authority
seonghobae Aug 16, 2026
ce44b56
docs(changelog): record console-device path hardening
seonghobae Aug 16, 2026
ba68ed6
test(security): reject mixed Windows namespace prefixes
seonghobae Aug 16, 2026
70e0fbf
fix(security): normalize Windows namespace prefix separators
seonghobae Aug 16, 2026
1e8e5ce
docs(security): record mixed Windows namespace boundary
seonghobae Aug 16, 2026
bc81d6c
docs(changelog): record mixed namespace rejection
seonghobae Aug 16, 2026
8a9321d
fix(security): authorize stem-work json sidecar before write
cursoragent Aug 16, 2026
30293ac
fix(security): bind stem-work sidecar writes to authorized arrays path
cursoragent Aug 16, 2026
c6287bb
test(security): lock orchestration log privacy
seonghobae Aug 20, 2026
bf7414d
fix(security): redact orchestration failure logs
seonghobae Aug 20, 2026
bedc206
fix(security): restore API documentation while redacting logs
seonghobae Aug 20, 2026
718d9d5
merge(develop): refresh path-authority branch
seonghobae Aug 28, 2026
f1f5e87
test(api): match redacted stem failure logging
seonghobae Aug 28, 2026
197664b
test(security): preserve path-log privacy succession contract
seonghobae Sep 1, 2026
bbea517
fix(security): log traversal without path disclosure
seonghobae Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
### Fixed

- Upgraded the local score PDF parser to `pdfjs-dist` 6.2.108, pinned Undici 7.29.0 across the workspace, and constrained PDF loading to copied in-memory bytes with a same-origin bundled worker and npm-generated lock provenance.
- Hardened local analysis filesystem authority so source/cache/temp inputs reject ambiguous, foreign-host absolute, homogeneous or mixed-separator network/device namespaces, Win32 console-device aliases (`CONIN$`/`CONOUT$`), traversal, Win32 leading/trailing ASCII-space or trailing-period normalization, direct-symlink, non-directory writable-root, pre-existing derived-child, exact feature-cache file, atomic-write temporary-file, and stem-work `{digest}.json` sidecar symlink escape cases before repository-owned I/O. The parent helper now binds stem-work sidecar writes to the authorized arrays path rather than a worker-supplied `arraysPath`, and late path-authority failures stay `invalid_request` instead of `engine_unavailable`.

## [0.1.3] - 2026-04-29

Expand Down
76 changes: 76 additions & 0 deletions docs/doctoring/filesystem-path-authority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Filesystem path authority

## Status

**Active Draft PR evidence.** This record documents the security model for issue #852 and PR #858. It is not protected-`develop` shipped truth until the implementation is merged and revalidated on the protected branch.

## Decision boundary

BandScope accepts local audio and owns cache/temporary filesystem locations for analysis. A path string is therefore an authority-bearing input rather than an inert label. The implementation separates two checks:

1. **Lexical path-shape policy** detects relative authority, parent/current-directory segments, Windows drive-relative forms, network/UNC roots, Win32 device namespaces, legacy DOS device aliases, alternate data streams, and other Win32 filename-normalization ambiguities without depending on the host operating system.
2. **Native filesystem authority** requires the current host to recognize a request path as absolute before analysis progress begins, then repeats canonical file-type or containment checks at the repository-owned read/write boundary. Pure lexical validation remains separately callable so cross-platform syntax can be tested without pretending a foreign-host path is usable by the current runtime.

This separation is intentional. A Windows path such as `C:\Music\rehearsal.wav` can be recognized as a fully qualified local Windows path by the lexical contract even when a Linux CI runner is evaluating syntax, while normal Linux request preflight must reject that string before emitting decode progress because Linux I/O cannot treat it as a native absolute path.

## Evidence and rationale

Microsoft documents that a drive designator without a following backslash, such as `C:tmp.txt`, is relative to the current directory for that drive, and specifically gives `C:..\tmp.txt` as a relative-path form. It also distinguishes UNC names beginning with two backslashes and the Win32 device namespaces addressed through `\\?\` and `\\.\`. Those forms carry authority outside BandScope's local-file contract and are rejected rather than normalized into a different meaning.

Windows path APIs and path abstractions admit slash spellings in contexts where backslash is the canonical namespace separator. BandScope therefore canonicalizes `/` to `\` **only while classifying the leading authority prefix**. This prevents mixed forms such as `/\server\share`, `\/server\share`, `/\?\C:\...`, or `\/.\pipe\...` from bypassing the UNC/device rejection merely because no homogeneous `\\` or `//` prefix is present. The original string is left unchanged for subsequent `PurePosixPath`/`PureWindowsPath` parsing and for payload-safe diagnostics, so this is a refusal rule rather than an authority-changing path rewrite.

Microsoft's current Win32 naming guidance also reserves `CON`, `PRN`, `AUX`, `NUL`, `COM1`-`COM9`, `LPT1`-`LPT9`, and the documented ISO/IEC 8859-1 superscript-digit variants even when an extension follows. Microsoft's `RtlIsDosDeviceName_U` documentation additionally identifies `CONIN$` and `CONOUT$` as valid DOS device names, and the `CreateFile` contract opens those names as console input/output handles. BandScope therefore rejects them as device authority rather than treating them as regular local files. The Win32 naming guidance also reserves `:` within ordinary filename components and documents alternate data streams as a separate stream mechanism. It further warns against names ending in a space or period. Microsoft also documents that the Windows Object Manager removes an ASCII space (0x20) at the beginning or end of a file or folder name during creation. BandScope therefore rejects a Windows path component that begins or ends in an ASCII space, as well as one ending in a period, rather than validating a string that Windows can normalize to a different authority-bearing name such as ` NUL.wav` → `NUL.wav`.

MITRE CWE-22 treats untrusted pathname construction that can resolve outside an intended restricted location as path traversal. Its observed examples include failures to handle the Windows backslash separator and cases where an absolute input resets a joined path. BandScope therefore does not rely on a single `".."` substring rule.

MITRE CWE-59 separately defines link-following weakness as accessing a file by name without preventing that name from identifying a link or shortcut to an unintended resource. That distinction matters for BandScope's derived cache files: a digest-derived filename can still carry unintended authority when an already-present symbolic link occupies that exact child path. The cache contract therefore canonicalizes the exact feature-cache targets and repository-owned atomic-write temporary siblings, not only their parent directory.

Python's current `pathlib` documentation states that `Path.resolve()` makes paths absolute, resolves symbolic links, and eliminates `..` components; in non-strict mode it resolves as far as possible and appends a non-existing remainder. Accordingly, this slice uses canonical resolution to detect already-present symlink escapes but does **not** claim descriptor-level protection against a privileged local actor swapping filesystem entries after validation and before a later open.

## Implemented/required controls for PR #858

- Reject empty and NUL-containing path strings.
- Reject lexical `.` and `..` components using both slash conventions while preserving benign repeated separators.
- Reject Windows drive-relative paths such as `C:relative.wav` and `C:..\relative.wav`.
- Reject UNC/network and Win32 device namespaces for `sourcePath`, `cacheRoot`, and `tempRoot`, including mixed `/` and `\` separator spellings at the authority prefix.
- Reject legacy DOS device aliases in fully qualified Windows path components, including `CONIN$`/`CONOUT$`, documented superscript COM/LPT variants, and extension-bearing aliases such as `NUL.wav`.
- Reject Win32 reserved punctuation/control characters, alternate-stream `:` syntax outside the drive designator, components beginning or ending in an ASCII space, and components ending in a period; these are normalization or namespace forms outside the regular-file contract.
- Keep host-independent lexical validation available for POSIX/Windows syntax tests, but require normal request preflight to recognize each accepted local path as native absolute authority before any analysis progress is emitted.
- Keep validation errors payload-safe: identify the field, not the supplied path value.
- Resolve the selected local source on the native host before separation and reject a direct symlink.
- Require an existing source to be a regular file, while preserving the established orchestration contract in which an authorized but missing path reaches the separation worker and returns the stable payload-safe `Audio source file not found.` result.
- Require an existing `cacheRoot` or `tempRoot` to be a directory, and repeat that type check at the derived-path I/O boundary so a regular file cannot be silently accepted as an app-owned writable root.
- Derive cache and stem-work child names only from repository-controlled directory names and SHA-256 digests.
- Resolve already-existing child symlinks before checking that derived cache/temp paths remain within the canonical authorized root.
- Resolve the exact derived feature-cache metadata and array files before any cache read so an already-present file symlink cannot escape `cacheRoot` merely because its parent directory is authorized.
- Resolve repository-owned atomic-write temporary siblings before either metadata or array cache file is opened; resolve both feature-cache temporary paths before the first write so one invalid sibling cannot cause a partial external write.
- Resolve the exact stem-work `{digest}.json` sidecar before the parent helper writes worker metadata, and fail the job as `invalid_request` before progress when that sidecar already escapes `tempRoot`.
- Bind the parent-helper sidecar write and array reload to the already-authorized `arrays_path` argument. A worker-supplied `arraysPath` is not filesystem authority; a missing or mismatched path is rejected as `tempRoot` before any write or unlink.
- Translate a late cache/temp canonicalization failure into one payload-safe `invalid_request` status rather than emitting progress and later misclassifying the failure as `engine_unavailable`.
- Preserve the existing privacy boundary: persisted cache metadata must not include the original absolute source path.

## Residual risk and follow-up boundary

Canonicalization is a point-in-time check. A sufficiently privileged local process can race a later path open by replacing a filesystem entry after validation. Eliminating that class completely requires descriptor/handle-relative open semantics and platform-specific no-follow/reparse-point controls across every downstream decoder/write boundary. PR #858 must not claim that stronger property unless it is implemented and tested. The current bounded objective is to eliminate ambiguous path syntax, foreign-host runtime authority, device/stream aliases, direct source symlinks, invalid writable-root types, and already-present cache/temp symlink escapes without expanding filesystem authority.

## Verification contract

The exact PR head must exercise POSIX and Windows lexical adversarial cases, benign repeated separators, homogeneous and mixed-separator UNC/device namespace prefixes, Windows reserved/device/stream/normalization cases independently of the CI host, including Win32 console devices `CONIN$` and `CONOUT$`, leading-ASCII-space normalization, explicit host-independent lexical acceptance, foreign-host request-preflight rejection before progress, native absolute success paths, direct source symlink rejection, existing-directory rejection, missing-file orchestration compatibility, existing file rejection for writable cache/temp roots at both preflight and derived-path I/O boundaries, cache/temp fixed-subdirectory symlink escapes both before and after validation, exact feature-cache metadata/array file symlink escapes, pre-existing atomic-write temporary-file symlink escapes with outside sentinels unchanged, stem-work `{digest}.json` sidecar symlink write-follow with the outside sentinel unchanged, worker-supplied `arraysPath` rejection that leaves an outside sidecar sentinel unchanged, job-level stem-work sidecar rejection before progress, late stem-work authority failures remaining `invalid_request` after progress, payload-safe failures, native root and fixed-child resolution failures, late API authority-failure translation, focused API behavior, and the full analysis-engine suite. New production code remains subject to the repository's exact 100% owned statement/branch coverage and public-docstring gates, plus repository CI, SAST, security, supply-chain, SBOM, current automated review, and protected-branch approval rules.

## References

Microsoft. (n.d.). *File path formats on Windows systems*. Microsoft Learn. Retrieved August 15, 2026, from https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats

Microsoft. (n.d.). *Naming files, paths, and namespaces*. Microsoft Learn. Retrieved August 15, 2026, from https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file

Microsoft. (n.d.). *Support for whitespace characters in file and folder names for Windows*. Microsoft Learn. Retrieved August 16, 2026, from https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/file-folder-name-whitespace-characters

Microsoft. (2023, September 14). *RtlIsDosDeviceName_U function*. Microsoft Learn. https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlisdosdevicename_u

Microsoft. (n.d.). *CreateFileA function (fileapi.h)*. Microsoft Learn. Retrieved August 16, 2026, from https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea

MITRE. (2026). *CWE-22: Improper limitation of a pathname to a restricted directory ('Path Traversal') (Version 4.20)*. Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/22.html

MITRE. (2026). *CWE-59: Improper link resolution before file access ('Link Following') (Version 4.20)*. Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/59.html

Python Software Foundation. (2026). *pathlib — Object-oriented filesystem paths* (Python 3.14.6 documentation). https://docs.python.org/3.14/library/pathlib.html
Loading
Loading