@j0hanz/filesystem-mcp 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +32 -26
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +10 -1
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +10 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/concurrency.d.ts +4 -4
  9. package/dist/core/concurrency.d.ts.map +1 -1
  10. package/dist/core/concurrency.js +1 -1
  11. package/dist/core/concurrency.js.map +1 -1
  12. package/dist/core/config.d.ts +2 -0
  13. package/dist/core/config.d.ts.map +1 -1
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/cursor.d.ts +8 -0
  16. package/dist/core/cursor.d.ts.map +1 -1
  17. package/dist/core/cursor.js +14 -0
  18. package/dist/core/cursor.js.map +1 -1
  19. package/dist/core/file-uri.d.ts +12 -3
  20. package/dist/core/file-uri.d.ts.map +1 -1
  21. package/dist/core/file-uri.js +35 -7
  22. package/dist/core/file-uri.js.map +1 -1
  23. package/dist/core/fmt.d.ts +2 -0
  24. package/dist/core/fmt.d.ts.map +1 -1
  25. package/dist/core/fmt.js +16 -0
  26. package/dist/core/fmt.js.map +1 -1
  27. package/dist/core/fs.d.ts +16 -1
  28. package/dist/core/fs.d.ts.map +1 -1
  29. package/dist/core/fs.js +191 -72
  30. package/dist/core/fs.js.map +1 -1
  31. package/dist/core/glob.d.ts +8 -0
  32. package/dist/core/glob.d.ts.map +1 -1
  33. package/dist/core/glob.js +84 -11
  34. package/dist/core/glob.js.map +1 -1
  35. package/dist/core/input-required.d.ts +7 -0
  36. package/dist/core/input-required.d.ts.map +1 -1
  37. package/dist/core/input-required.js +13 -0
  38. package/dist/core/input-required.js.map +1 -1
  39. package/dist/core/path-completer.d.ts.map +1 -1
  40. package/dist/core/path-completer.js +5 -3
  41. package/dist/core/path-completer.js.map +1 -1
  42. package/dist/core/path-utils.d.ts +51 -12
  43. package/dist/core/path-utils.d.ts.map +1 -1
  44. package/dist/core/path-utils.js +69 -29
  45. package/dist/core/path-utils.js.map +1 -1
  46. package/dist/core/path.d.ts +108 -21
  47. package/dist/core/path.d.ts.map +1 -1
  48. package/dist/core/path.js +436 -83
  49. package/dist/core/path.js.map +1 -1
  50. package/dist/core/read.d.ts +13 -3
  51. package/dist/core/read.d.ts.map +1 -1
  52. package/dist/core/read.js +46 -40
  53. package/dist/core/read.js.map +1 -1
  54. package/dist/core/schema.d.ts +9 -6
  55. package/dist/core/schema.d.ts.map +1 -1
  56. package/dist/core/schema.js +22 -19
  57. package/dist/core/schema.js.map +1 -1
  58. package/dist/core/search.d.ts +6 -0
  59. package/dist/core/search.d.ts.map +1 -1
  60. package/dist/core/search.js +159 -33
  61. package/dist/core/search.js.map +1 -1
  62. package/dist/core/sensitive.d.ts.map +1 -1
  63. package/dist/core/sensitive.js +71 -29
  64. package/dist/core/sensitive.js.map +1 -1
  65. package/dist/core/store.d.ts +1 -0
  66. package/dist/core/store.d.ts.map +1 -1
  67. package/dist/core/store.js +3 -0
  68. package/dist/core/store.js.map +1 -1
  69. package/dist/core/util.d.ts +12 -1
  70. package/dist/core/util.d.ts.map +1 -1
  71. package/dist/core/util.js +24 -1
  72. package/dist/core/util.js.map +1 -1
  73. package/dist/prompts.d.ts +1 -1
  74. package/dist/prompts.d.ts.map +1 -1
  75. package/dist/prompts.js +1 -1
  76. package/dist/prompts.js.map +1 -1
  77. package/dist/resources.d.ts +1 -2
  78. package/dist/resources.d.ts.map +1 -1
  79. package/dist/resources.js +20 -13
  80. package/dist/resources.js.map +1 -1
  81. package/dist/server.js +2 -2
  82. package/dist/server.js.map +1 -1
  83. package/dist/tools/create.d.ts.map +1 -1
  84. package/dist/tools/create.js +122 -45
  85. package/dist/tools/create.js.map +1 -1
  86. package/dist/tools/define.d.ts +8 -4
  87. package/dist/tools/define.d.ts.map +1 -1
  88. package/dist/tools/define.js +36 -9
  89. package/dist/tools/define.js.map +1 -1
  90. package/dist/tools/delete.d.ts.map +1 -1
  91. package/dist/tools/delete.js +57 -17
  92. package/dist/tools/delete.js.map +1 -1
  93. package/dist/tools/diff.js +1 -1
  94. package/dist/tools/diff.js.map +1 -1
  95. package/dist/tools/edit.d.ts.map +1 -1
  96. package/dist/tools/edit.js +14 -12
  97. package/dist/tools/edit.js.map +1 -1
  98. package/dist/tools/find-files.d.ts.map +1 -1
  99. package/dist/tools/find-files.js +20 -20
  100. package/dist/tools/find-files.js.map +1 -1
  101. package/dist/tools/index.d.ts +1 -1
  102. package/dist/tools/index.d.ts.map +1 -1
  103. package/dist/tools/index.js +1 -1
  104. package/dist/tools/index.js.map +1 -1
  105. package/dist/tools/list.d.ts.map +1 -1
  106. package/dist/tools/list.js +142 -73
  107. package/dist/tools/list.js.map +1 -1
  108. package/dist/tools/move.d.ts.map +1 -1
  109. package/dist/tools/move.js +81 -36
  110. package/dist/tools/move.js.map +1 -1
  111. package/dist/tools/patch.js +13 -6
  112. package/dist/tools/patch.js.map +1 -1
  113. package/dist/tools/read.d.ts.map +1 -1
  114. package/dist/tools/read.js +114 -45
  115. package/dist/tools/read.js.map +1 -1
  116. package/dist/tools/replace-text.d.ts.map +1 -1
  117. package/dist/tools/replace-text.js +49 -35
  118. package/dist/tools/replace-text.js.map +1 -1
  119. package/dist/tools/search-text.d.ts.map +1 -1
  120. package/dist/tools/search-text.js +20 -49
  121. package/dist/tools/search-text.js.map +1 -1
  122. package/dist/tools/stat.d.ts.map +1 -1
  123. package/dist/tools/stat.js +26 -8
  124. package/dist/tools/stat.js.map +1 -1
  125. package/dist/transport/http-policy.d.ts +3 -2
  126. package/dist/transport/http-policy.d.ts.map +1 -1
  127. package/dist/transport/http-policy.js +3 -2
  128. package/dist/transport/http-policy.js.map +1 -1
  129. package/dist/transport/http.d.ts.map +1 -1
  130. package/dist/transport/http.js +2 -2
  131. package/dist/transport/http.js.map +1 -1
  132. package/dist/transport/stdio.js +1 -1
  133. package/dist/transport/stdio.js.map +1 -1
  134. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/core/path.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Stats } from 'node:fs';\nimport { lstat, readlink, realpath, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';\n\nimport { runExclusive, withAbort } from './concurrency.ts';\nimport { cli } from './config.ts';\nimport {\n classifyCauseChain,\n ErrorCode,\n FsError,\n isFsError,\n isNotFoundErrno,\n isSkippableErrno,\n rethrowIfAborted,\n SKIPPABLE_FS_CODES,\n} from './errors.ts';\nimport { Logger } from './observability.ts';\nimport { findProjectRoot, isUnsafeCwdPath, resolveConfiguredDirs } from './path-discovery.ts';\nimport {\n getReservedDeviceNameForPath,\n IS_WINDOWS,\n isPathInsideDirectory,\n isPathWithinDirectories,\n isSamePath,\n isWindowsDriveRelativePath,\n normalizeAllowedDirectory,\n normalizePath,\n parseTrueEnvFlag,\n toPosixPath,\n} from './path-utils.ts';\nimport { SensitiveMatcher } from './sensitive.ts';\nimport { ROOTS_TIMEOUT_MS } from './util.ts';\n\n// Allowed-directory assembly and the PathGuard that enforces it. The\n// sensitive-file denylist lives in sensitive.ts, and the character-level\n// primitives (IS_WINDOWS, isAlpha, isSlash, toPosixPath) live in\n// path-utils.ts.\n\nexport interface ValidatedPathDetails {\n requestedPath: string;\n resolvedPath: string;\n isSymlink: boolean;\n}\n\nexport interface ServerOptions {\n allowCwd?: boolean;\n cliAllowedDirs?: string[];\n readOnly?: boolean;\n}\n\n/**\n * The real path of `normalizedPath`, or the one it would have if it does not\n * exist yet: realpath of its nearest existing ancestor plus the missing suffix.\n * Lets a missing root be checked against a realpath-canonicalized boundary even\n * when the boundary itself is reached through a symlink. `undefined` when an\n * ancestor exists but cannot be resolved (EACCES and friends).\n */\nasync function projectRealPath(\n normalizedPath: string,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n const missing: string[] = [];\n let current = normalizedPath;\n for (;;) {\n try {\n signal?.throwIfAborted();\n const real = await withAbort(realpath(current), signal);\n return normalizePath(missing.length === 0 ? real : join(real, ...missing.reverse()));\n } catch (error) {\n rethrowIfAborted(error);\n if (!isNotFoundErrno(error)) return undefined;\n const parent = dirname(current);\n if (parent === current) return undefined;\n missing.push(basename(current));\n current = parent;\n }\n }\n}\n\n/**\n * Every root — configured or granted — must fall under FS_ROOT_BOUNDARY. An\n * existing root must *resolve* inside the boundary; a missing root is projected\n * through its nearest existing ancestor before comparison so symlinked boundary\n * spellings still compare against realpath-canonicalized bounds.\n */\nasync function partitionRootsWithin(\n roots: readonly string[],\n bounds: readonly string[],\n signal?: AbortSignal,\n): Promise<{ kept: string[]; dropped: string[] }> {\n const normalizedBounds = normalizeAllowedDirectories(bounds);\n const kept: string[] = [];\n const dropped: string[] = [];\n for (const root of roots.map(normalizePath)) {\n const real = await projectRealPath(root, signal);\n (real !== undefined && isPathWithinDirectories(real, normalizedBounds) ? kept : dropped).push(\n root,\n );\n }\n return { kept, dropped };\n}\n\n/** `path.relative` with forward slashes, so displayed paths match across platforms. */\nexport function toPosixRelative(from: string, to: string): string {\n return toPosixPath(relative(from, to));\n}\n\n// ---------------------------------------------------------------------------\n// Resolver pipeline\n// ---------------------------------------------------------------------------\n\nexport function normalizeAllowedDirectories(dirs: readonly string[]): string[] {\n return [...new Set(dirs.map(normalizeAllowedDirectory).filter((entry) => entry.length > 0))];\n}\n\nasync function resolveRealPath(normalized: string, signal?: AbortSignal): Promise<string | null> {\n try {\n signal?.throwIfAborted();\n const realPath = await withAbort(realpath(normalized), signal);\n return normalizeAllowedDirectory(realPath);\n } catch (error) {\n rethrowIfAborted(error);\n // Only suppress ENOENT — the path genuinely does not exist.\n // EACCES, EIO, and other unexpected errors are rethrown so callers\n // cannot silently operate with a narrowed allowed-directory set.\n if (isNotFoundErrno(error)) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function resolveAllowedDirectoriesState(\n dirs: readonly string[],\n signal?: AbortSignal,\n): Promise<string[]> {\n const primary = normalizeAllowedDirectories(dirs);\n const reals = await Promise.all(primary.map((dir) => resolveRealPath(dir, signal)));\n return [\n ...new Set(\n primary.flatMap((dir, i) => {\n const real = reals[i];\n return real && !isSamePath(real, dir) ? [dir, real] : [dir];\n }),\n ),\n ];\n}\n\n/**\n * TOCTOU note: validation returns a plain string and the fs operation is a\n * later syscall, so a symlink swapped in between could redirect it. Reads and\n * appends in core/fs.ts and core/read.ts open with O_NOFOLLOW (final component)\n * and re-check the fd's real path on Linux (ancestor swap). Residual: write\n * paths that resolve a parent by name (`rename`, `mkdir`, `rm`, `cp`) would\n * need openat()-relative-to-dirfd, which Node lacks; non-Linux skips the fd\n * re-check.\n */\nexport class PathGuard {\n private allowedDirectoriesState: string[] | undefined;\n /** One entry per configured or granted root, without the realpath aliases. */\n private rootsState: string[] | undefined;\n private readonly sensitive = new SensitiveMatcher();\n /**\n * Directories added by an accepted access grant (R8) — from the tool\n * executor's grant round-trip, or from a client's declared workspace roots on\n * the legacy stdio leg. Held separately from the configured baseline because\n * `recomputeAllowedDirectories` rebuilds that baseline from CLI args and env\n * on every call: anything merged into it would be recomputed away.\n *\n * These are NOT filtered against the baseline. A granted directory is\n * out-of-baseline by definition, so a baseline filter dropped every one while\n * `applyGrant` still reported success — the whole round-trip prompted the\n * user and then changed nothing. FS_ROOT_BOUNDARY and the unsafe-path denylist\n * remain the limits, both checked in `applyGrant` — the only writer of this\n * field — so the recompute can rebuild from it without re-filtering.\n */\n private grantedDirectories: string[] = [];\n private rootBoundaries: string[] = [];\n\n private readonly options: ServerOptions | undefined;\n\n constructor(options?: ServerOptions) {\n this.options = options;\n }\n\n /**\n * `expanded` is the containment set (each root plus its realpath alias);\n * `roots` is what a caller choosing \"the root\" sees, one entry per root.\n */\n initialize(expanded: readonly string[], roots: readonly string[] = expanded): void {\n this.allowedDirectoriesState = normalizeAllowedDirectories(expanded);\n this.rootsState = normalizeAllowedDirectories(roots);\n }\n\n isInitialized(): boolean {\n return this.allowedDirectoriesState !== undefined;\n }\n\n getAllowedDirectories(): string[] {\n if (!this.allowedDirectoriesState) {\n return [];\n }\n return [...this.allowedDirectoriesState];\n }\n\n /**\n * The allowed directory that contains `resolvedPath` — the longest match,\n * so a nested root wins over its parent — or `undefined` when none does.\n * Walks use it as the `.gitignore` ceiling: rules above an allowed root are\n * never read.\n */\n allowedRootContaining(resolvedPath: string): string | undefined {\n const normalized = normalizePath(resolvedPath);\n let best: string | undefined;\n for (const dir of this.getAllowedDirectories()) {\n if (\n isPathInsideDirectory(dir, normalized) &&\n (best === undefined || dir.length > best.length)\n ) {\n best = dir;\n }\n }\n return best;\n }\n\n /**\n * The roots as configured or granted, one per root. getAllowedDirectories()\n * also holds each root's realpath alias for containment, so an aliased root\n * (symlink, junction, 8.3 short name) appears there twice.\n */\n getRoots(): string[] {\n return this.rootsState ? [...this.rootsState] : [];\n }\n\n isSensitive(filePath: string): boolean {\n return this.sensitive.isSensitive(filePath);\n }\n\n /**\n * True when `entryPath` is both within the guard's allowed directories and\n * not sensitive, checking the requested AND resolved paths. EVERY entry is\n * realpath-resolved via validateExistingPathDetailed: a symlinked ancestor\n * directory pointing outside the sandbox would otherwise pass the lexical\n * containment check (fs.glob follows symlinks and yields external entries as\n * non-symlink dirents). validateExistingPathDetailed re-checks containment on\n * the real path against this.allowedDirectoriesState (the guard's full\n * allowed set — a superset of the single-root `bounds` callers pass) and\n * re-checks sensitivity on the resolved target, throwing ACCESS_DENIED for\n * escapes/sensitive, which the catch below turns into a filter. Skippable\n * errno/fs errors return false (the entry is filtered, not fatal). The\n * accepted TOCTOU window is documented at the class docstring above.\n */\n async isEntryAccessible(entryPath: string): Promise<boolean> {\n const isSensitive = (requestedPath: string, resolvedPath: string): boolean =>\n this.isSensitive(requestedPath) || this.isSensitive(resolvedPath);\n try {\n const validated = await this.validateExistingPathDetailed(entryPath);\n return !isSensitive(validated.requestedPath, validated.resolvedPath);\n } catch (error) {\n if (isFsError(error)) {\n if (SKIPPABLE_FS_CODES.has(error.code)) return false;\n throw error;\n }\n if (isSkippableErrno(error)) return false;\n throw error;\n }\n }\n\n async validateExistingPath(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n return details.resolvedPath;\n }\n\n /**\n * True when a grant must never admit `targetDir` — an unsafe path (home,\n * /etc, C:\\Windows, ...). Checked on the lexical path AND on the one it\n * resolves to: `resolveAllowedDirectoriesState` pushes each root's realpath\n * into the allowed set, so a lexical-only check let a symlink or junction\n * aliasing $HOME/C:\\Windows in under an innocuous name — and the confirmation\n * prompt showed the alias, not the target. A target that cannot be resolved\n * is judged lexically; it does not exist to escape into.\n */\n private async isUnsafeGrantTarget(targetDir: string): Promise<boolean> {\n const normalized = normalizePath(targetDir);\n if (isUnsafeCwdPath(normalized)) return true;\n let resolved: string;\n try {\n resolved = normalizePath(await realpath(normalized));\n } catch {\n return false;\n }\n return !isSamePath(resolved, normalized) && isUnsafeCwdPath(resolved);\n }\n\n /** Walk up from a blocked path to the closest existing ancestor directory. */\n private async resolveGrantTargetDir(blockedPath: string): Promise<string> {\n let targetDir = blockedPath;\n for (;;) {\n const parent = dirname(targetDir);\n try {\n // An existing directory is the grant target; an existing file grants\n // its parent directory.\n return (await stat(targetDir)).isDirectory() ? targetDir : parent;\n } catch {\n // Missing — keep walking up.\n }\n if (parent === targetDir) return targetDir;\n targetDir = parent;\n }\n }\n\n /**\n * Pre-check (no mutation, no callback): given the paths a tool is about to\n * operate on, return the sorted, de-duplicated grant-target directories for\n * those that are outside the allowed roots AND grantable (within FS_ROOT_BOUNDARY\n * when it is configured). A path whose nearest existing ancestor escapes the\n * boundary is NOT grantable and is omitted — the operation will fail with\n * ACCESS_DENIED for it rather than prompt. The caller returns an\n * `input_required` result carrying exactly this set (R7), and on retry applies\n * each accepted grant via {@link applyGrant} (R8). `requestState` binds this\n * set so a grant accepted for X cannot authorize Y (R9).\n */\n async precheckAccess(paths: readonly string[]): Promise<string[]> {\n if (!this.allowedDirectoriesState || paths.length === 0) {\n return [];\n }\n const allowedDirs = this.allowedDirectoriesState;\n const grantDirs: string[] = [];\n for (const requested of paths) {\n if (!requested) {\n continue;\n }\n const normalized = normalizePath(requested);\n if (isPathWithinDirectories(normalized, allowedDirs)) {\n continue;\n }\n const targetDir = normalizePath(await this.resolveGrantTargetDir(normalized));\n // Never offer a grant into a bare filesystem root or an unsafe path (home,\n // /etc, C:\\Windows, ...). Without FS_ROOT_BOUNDARY, isWithinBoundary returns\n // true for everything, so this is the one guard that still rejects roots a\n // misleading grant could reach. Mirrors the check gating --allow-cwd.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n continue;\n }\n if (grantDirs.includes(targetDir)) {\n continue;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n continue;\n }\n grantDirs.push(targetDir);\n }\n return grantDirs.sort();\n }\n\n /**\n * Apply an accepted access grant: enforce FS_ROOT_BOUNDARY again (a TOCTOU\n * re-check against the boundary resolved at config time), then extend the\n * allowed roots for the remainder of the session (R8, A4). Returns false when\n * the boundary or the unsafe-path denylist blocks the grant, AND when the\n * recompute did not actually admit the directory — the return value now\n * reports what the guard's allowed set really holds, not merely that the\n * pre-checks passed. The caller leaves a refused path to fail with\n * ACCESS_DENIED during the operation. Idempotent: re-granting an already\n * allowed directory is a no-op via the dedup in `initialize`.\n */\n async applyGrant(targetDir: string): Promise<boolean> {\n // Defense-in-depth: even a tampered/accepted grant cannot extend roots into\n // an unsafe path. The precheckAccess guard already refuses to offer these;\n // this catches a grant that arrived by another route — the legacy stdio\n // `roots/list` seeding, which has no confirmation round-trip at all.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n return false;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n return false;\n }\n // Read + write under the mutation lock so a concurrent grant cannot\n // interleave and lose this grant (GRANT-1). runExclusive is not reentrant:\n // nothing in this body may take the lock again.\n return runExclusive(this, async () => {\n const previous = this.grantedDirectories;\n this.grantedDirectories = [...previous, normalizePath(targetDir)];\n try {\n await this.recomputeAllowedDirectories();\n } catch (error) {\n // A failed recompute leaves the guard with its previous, consistent view.\n this.grantedDirectories = previous;\n throw error;\n }\n // Verify rather than assume: a boundary that no longer covers this\n // directory would drop it in the recompute above, and reporting success\n // for a grant that did not land is exactly the failure this replaces.\n if (isPathWithinDirectories(normalizePath(targetDir), this.getAllowedDirectories())) {\n return true;\n }\n this.grantedDirectories = previous;\n await this.recomputeAllowedDirectories();\n return false;\n });\n }\n\n // One boundary rule for startup roots and grants: a missing target is\n // projected through its nearest existing ancestor, an unresolvable one is\n // refused. No boundary configured means no constraint; partitionRootsWithin\n // with no bounds would drop everything, so that case returns first.\n private async isWithinBoundary(targetDir: string): Promise<boolean> {\n if (this.rootBoundaries.length === 0) {\n return true;\n }\n const { kept } = await partitionRootsWithin([targetDir], this.rootBoundaries);\n return kept.length > 0;\n }\n\n private validateAccess(requestedPath: string): {\n normalizedRequested: string;\n allowedDirs: string[];\n accessDeniedHint: string;\n } {\n if (!this.allowedDirectoriesState) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'PathGuard not initialized. Call initialize() first.',\n requestedPath,\n );\n }\n if (isWindowsDriveRelativePath(requestedPath)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Drive-relative paths are not allowed. Use C:\\\\path or C:/path instead of C:path.',\n requestedPath,\n );\n }\n // A device name is only a device on Windows; on POSIX `aux.ts` is a file.\n const reservedDevice = IS_WINDOWS ? getReservedDeviceNameForPath(requestedPath) : undefined;\n if (reservedDevice) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `Reserved Windows device name not allowed: ${reservedDevice}.`,\n requestedPath,\n );\n }\n\n const normalizedRequested = normalizePath(requestedPath);\n const allowedDirs = this.allowedDirectoriesState;\n\n const accessDeniedHint =\n allowedDirs.length > 0\n ? `Allowed: ${allowedDirs.join(', ')}`\n : 'No allowed directories configured.';\n\n if (!isPathWithinDirectories(normalizedRequested, allowedDirs)) {\n // Out of root. The access-grant `input_required` round-trip is driven by\n // the executor's pre-check (precheckAccess) BEFORE the operation runs, so\n // by the time validation reaches here any grantable out-of-root path has\n // already been accepted and added to the allowed set. A path still out of\n // root here was either declined, ungrantable (outside FS_ROOT_BOUNDARY), or\n // never pre-checked — fail closed.\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n this.assertNotSensitiveFile(requestedPath, requestedPath);\n this.assertNotSensitiveFile(normalizedRequested, requestedPath);\n\n return {\n normalizedRequested,\n allowedDirs: this.allowedDirectoriesState,\n accessDeniedHint,\n };\n }\n\n private async handleRealpathError(\n error: unknown,\n normalizedRequested: string,\n allowedDirs: string[],\n accessDeniedHint: string,\n requestedPath: string,\n ): Promise<never> {\n if (isNotFoundErrno(error)) {\n // Resolve the nearest existing ancestor to detect out-of-sandbox symlinks.\n // e.g. if `link -> C:\\external` and path is `link\\nonexistent.txt`, the\n // ancestor resolves outside allowed dirs → ACCESS_DENIED, not NOT_FOUND.\n try {\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n } catch (ancestorErr) {\n // Rethrow any FsError — collapsing e.g. UNKNOWN to NOT_FOUND would mask\n // incomplete sandbox checks and make bugs invisible to callers.\n if (isFsError(ancestorErr)) {\n throw ancestorErr;\n }\n }\n\n throw new FsError(ErrorCode.NOT_FOUND, 'Path not found', requestedPath, error);\n }\n\n const mapped = classifyCauseChain(error);\n throw new FsError(\n mapped.code,\n 'Cannot access path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n async validateExistingPathDetailed(requestedPath: string): Promise<ValidatedPathDetails> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccess(requestedPath);\n\n let realPath: string;\n try {\n realPath = await realpath(normalizedRequested);\n } catch (error) {\n realPath = await this.handleRealpathError(\n error,\n normalizedRequested,\n allowedDirs,\n accessDeniedHint,\n requestedPath,\n );\n }\n\n const normalizedReal = normalizePath(realPath);\n\n if (!isPathWithinDirectories(normalizedReal, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Re-check the resolved real path: a symlink inside an allowed root may\n // point at a sensitive file (e.g. link -> .env). The early check above only\n // sees the requested/normalized path, not the symlink target.\n this.assertNotSensitiveFile(normalizedReal, requestedPath);\n\n return {\n requestedPath: normalizedRequested,\n resolvedPath: normalizedReal,\n isSymlink: !isSamePath(normalizedRequested, normalizedReal),\n };\n }\n\n async validateExistingDirectory(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n\n let stats: Stats;\n try {\n stats = await stat(details.resolvedPath);\n } catch (error) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot access directory',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requestedPath);\n }\n\n return details.resolvedPath;\n }\n\n resolvePathOrRoot(pathValue: string | undefined): string {\n if (pathValue && pathValue.trim().length > 0) {\n return pathValue;\n }\n const roots = this.getRoots();\n if (roots.length === 0) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'No roots configured. Use the roots tool or --allow-cwd.',\n );\n }\n if (roots.length > 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Multiple roots configured. Provide an explicit path.',\n );\n }\n const root = roots[0];\n if (!root) {\n throw new FsError(ErrorCode.ACCESS_DENIED, 'Workspace root is unexpectedly undefined');\n }\n return root;\n }\n\n isAllowedRoot(normalizedPath: string): boolean {\n // isSamePath case-folds on case-insensitive filesystems (win + darwin),\n // matching the containment checks used everywhere else in the guard. The\n // previous IS_WINDOWS-only fold left darwin doing exact-case compares here\n // while isPathInsideDirectory folded — a root matched case-insensitively\n // everywhere else was missed.\n return this.getAllowedDirectories().some((dir) => isSamePath(dir, normalizedPath));\n }\n\n // Checks ONLY the sensitive-file denylist. Root containment and symlink\n // resolution must be verified separately (e.g. via validateExistingPath).\n private assertNotSensitiveFile(checkPath: string, requestedPath: string): void {\n if (this.isSensitive(checkPath)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Sensitive file blocked by the server denylist. To unblock one built-in secret-file pattern, restart the server with --allow <pattern> (or FS_ALLOWLIST); --allow-sensitive (or FS_ALLOW_SENSITIVE=1) turns the whole built-in list off. Paths blocked with --deny or FS_DENYLIST stay blocked.',\n requestedPath,\n );\n }\n }\n\n private throwAccessDenied(requestedPath: string, hint?: string): never {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n hint ? `Outside allowed directories. ${hint}` : 'Outside allowed directories.',\n requestedPath,\n );\n }\n private async resolveNearestExistingAncestor(\n requestedPath: string,\n currentPath: string,\n ): Promise<{ realAncestor: string; resolvedTarget: string }> {\n const missingSegments: string[] = [];\n let current = currentPath;\n for (;;) {\n try {\n const realAncestor = normalizePath(await realpath(current));\n const resolvedTarget =\n missingSegments.length === 0\n ? realAncestor\n : normalizePath(join(realAncestor, ...missingSegments.reverse()));\n return { realAncestor, resolvedTarget };\n } catch (error) {\n try {\n const stats = await lstat(current);\n if (stats.isSymbolicLink()) {\n const target = await readlink(current);\n const resolvedTarget = isAbsolute(target) ? target : resolve(dirname(current), target);\n const normalizedTarget = normalizePath(resolvedTarget);\n const allowedDirs = this.getAllowedDirectories();\n if (!isPathWithinDirectories(normalizedTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath);\n }\n }\n } catch (lstatErr) {\n if (isFsError(lstatErr) && lstatErr.code === ErrorCode.ACCESS_DENIED) {\n throw lstatErr;\n }\n // ENOENT is expected during ancestor walk — the entry simply doesn't exist.\n // Any other error (EACCES, EIO, ELOOP) is unexpected; fail safe.\n if (!isNotFoundErrno(lstatErr)) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot probe symlink ancestor',\n requestedPath,\n lstatErr instanceof Error ? lstatErr : undefined,\n );\n }\n }\n const parent = dirname(current);\n if (parent === current) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot resolve path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n missingSegments.push(basename(current));\n current = parent;\n }\n }\n }\n\n async validatePathForWrite(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccess(requestedPath);\n\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n // Re-check the resolved target: a symlink inside an allowed root may point\n // at a sensitive file. Writing through such a link must be blocked too.\n this.assertNotSensitiveFile(resolvedTarget, requestedPath);\n return resolvedTarget;\n }\n\n async validatePathForDelete(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccess(requestedPath);\n\n // A workspace root is refused here, before the parent-containment check\n // below reaches for a parent that is out-of-root by construction and\n // reports \"Outside allowed directories\" for the one directory the caller\n // can see IS allowed. delete-file.ts keeps its own root check for the\n // nested-root case, where the parent is itself an allowed root and this\n // path is never taken.\n if (this.isAllowedRoot(normalizedRequested)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Deleting a workspace root directory is not allowed',\n requestedPath,\n );\n }\n\n const parent = dirname(normalizedRequested);\n let realParent: string;\n try {\n realParent = await realpath(parent);\n } catch (error) {\n throw new FsError(\n ErrorCode.NOT_FOUND,\n 'Parent directory not found',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n const normalizedRealParent = normalizePath(realParent);\n\n if (!isPathWithinDirectories(normalizedRealParent, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Resolve the final component when it exists. For deletion, we ONLY\n // block if the real target is outside the sandbox IF the target is\n // NOT a symlink. Deleting a symlink is safe even if it points outside.\n try {\n const stats = await lstat(normalizedRequested);\n if (stats.isSymbolicLink()) {\n // Symlink: check link sensitivity but don't resolve target.\n // The parent check above ensures the link itself is in an allowed root.\n this.assertNotSensitiveFile(normalizedRequested, requestedPath);\n return normalizedRequested;\n }\n\n // Not a symlink: resolve to catch path escapes (e.g. /allowed/dir/../../etc)\n // and block sensitive files.\n const realTarget = normalizePath(await realpath(normalizedRequested));\n if (!isPathWithinDirectories(realTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n this.assertNotSensitiveFile(realTarget, requestedPath);\n return realTarget;\n } catch (error) {\n // A denial raised inside this block (out-of-root real target, or a\n // sensitive file reached through a symlinked parent) must propagate.\n // Only a probe failure — ENOENT and friends — falls through to the\n // parent check, which is sufficient for a path that does not exist.\n if (isFsError(error)) {\n throw error;\n }\n }\n return normalizedRequested;\n }\n\n async recomputeAllowedDirectories(): Promise<void> {\n const cliAllowedDirs = normalizeAllowedDirectories(this.options?.cliAllowedDirs ?? []);\n\n // Parse allowed directories from environment variable\n const allowMissing =\n cli.allowMissingRoots ??\n parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');\n const envAllowedDirs = await resolveConfiguredDirs('FS_ALLOWED_DIRS', { allowMissing });\n\n // Parse FS_ROOT_BOUNDARY (the --root-boundary flag beats the env var)\n const boundaries = await resolveConfiguredDirs('FS_ROOT_BOUNDARY', {\n resolveReal: true,\n ...(cli.rootBoundary !== undefined ? { rawValue: cli.rootBoundary } : {}),\n });\n\n const allowCwd = Boolean(this.options?.allowCwd);\n const allowCwdDirs: string[] = [];\n if (allowCwd) {\n let cwd = normalizePath(process.cwd());\n const walkCwd =\n cli.allowCwdWalk ?? parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n if (walkCwd) {\n cwd = await findProjectRoot(cwd, [...boundaries, homedir()]);\n }\n if (isUnsafeCwdPath(cwd)) {\n Logger.warn(`Skipped adding unsafe current working directory to allowed list: ${cwd}`);\n } else {\n allowCwdDirs.push(cwd);\n }\n }\n\n const signal = AbortSignal.timeout(ROOTS_TIMEOUT_MS);\n let baseline = [...cliAllowedDirs, ...envAllowedDirs, ...allowCwdDirs];\n // FS_ROOT_BOUNDARY is the only filter grants answer to (see\n // `grantedDirectories`); without one they pass through as accepted.\n let grantsToInclude = this.grantedDirectories;\n if (boundaries.length > 0) {\n const { kept, dropped } = await partitionRootsWithin(baseline, boundaries, signal);\n for (const root of dropped) {\n Logger.warn(\n `Skipped allowed root outside FS_ROOT_BOUNDARY (${boundaries.join(', ')}): ${root}`,\n );\n }\n baseline = kept;\n grantsToInclude = (await partitionRootsWithin(this.grantedDirectories, boundaries, signal))\n .kept;\n }\n\n const combined = [...baseline, ...grantsToInclude];\n const nextState = await resolveAllowedDirectoriesState(combined, signal);\n // Commit boundaries and the allowed set together, after every await has\n // resolved, so a rejecting recompute leaves the guard's previous,\n // consistent view intact.\n this.rootBoundaries = boundaries;\n this.initialize(nextState, combined);\n }\n}\n"]}
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/core/path.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Stats } from 'node:fs';\nimport { lstat, readdir, readlink, realpath, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';\n\nimport { runExclusive, withAbort } from './concurrency.ts';\nimport { cli } from './config.ts';\nimport {\n classifyCauseChain,\n ErrorCode,\n FsError,\n isFsError,\n isNotFoundErrno,\n isSkippableErrno,\n rethrowIfAborted,\n SKIPPABLE_FS_CODES,\n} from './errors.ts';\nimport { Logger } from './observability.ts';\nimport { findProjectRoot, isUnsafeCwdPath, resolveConfiguredDirs } from './path-discovery.ts';\nimport {\n escapeNonAscii,\n expandHome,\n getReservedDeviceNameForPath,\n IS_WINDOWS,\n isPathInsideDirectory,\n isPathWithinDirectories,\n isSamePath,\n isUnresolvableDrivePath,\n isWindowsDrivePath,\n isWindowsDriveRelativePath,\n mayHaveEquivalentSpelling,\n normalizeAllowedDirectory,\n normalizePath,\n toPosixRelative,\n unicodeMatchKey,\n} from './path-utils.ts';\nimport { SensitiveMatcher } from './sensitive.ts';\nimport { parseTrueEnvFlag, ROOTS_TIMEOUT_MS } from './util.ts';\n\n// Allowed-directory assembly and the PathGuard that enforces it. The\n// sensitive-file denylist lives in sensitive.ts, and the character-level\n// primitives (IS_WINDOWS, isAlpha, isSlash, toPosixPath) live in\n// path-utils.ts.\n\n/** Links followed, or respelling rounds run, before a path is refused (Linux ELOOP's 40). */\nconst MAX_SYMLINK_HOPS = 40;\n\nexport interface ValidatedPathDetails {\n requestedPath: string;\n resolvedPath: string;\n isSymlink: boolean;\n}\n\n/** How one walk prints the paths it found; see `PathGuard.resultPaths`. */\nexport interface ResultPaths {\n /** The root the relative paths are relative to, as configured; the walked directory when there is none. */\n readonly root: string;\n /** The walked directory is that root itself. */\n readonly atRoot: boolean;\n show(absolutePath: string): string;\n}\n\n/** A root and every spelling that names it: as configured, and its realpath when that differs. */\ninterface RootSpellings {\n readonly root: string;\n readonly spellings: readonly string[];\n}\n\nexport interface ServerOptions {\n allowCwd?: boolean;\n cliAllowedDirs?: string[];\n readOnly?: boolean;\n}\n\n/**\n * The real path of `normalizedPath`, or the one it would have if it does not\n * exist yet: realpath of its nearest existing ancestor plus the missing suffix.\n * Lets a missing root be checked against a realpath-canonicalized boundary even\n * when the boundary itself is reached through a symlink. `undefined` when an\n * ancestor exists but cannot be resolved (EACCES and friends).\n */\nasync function projectRealPath(\n normalizedPath: string,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n const missing: string[] = [];\n let current = normalizedPath;\n for (;;) {\n try {\n signal?.throwIfAborted();\n const real = await withAbort(realpath(current), signal);\n return normalizePath(missing.length === 0 ? real : join(real, ...missing.reverse()));\n } catch (error) {\n rethrowIfAborted(error);\n if (!isNotFoundErrno(error)) return undefined;\n const parent = dirname(current);\n if (parent === current) return undefined;\n missing.push(basename(current));\n current = parent;\n }\n }\n}\n\n/**\n * Every root — configured or granted — must fall under FS_ROOT_BOUNDARY. An\n * existing root must *resolve* inside the boundary; a missing root is projected\n * through its nearest existing ancestor before comparison so symlinked boundary\n * spellings still compare against realpath-canonicalized bounds.\n */\nasync function partitionRootsWithin(\n roots: readonly string[],\n bounds: readonly string[],\n signal?: AbortSignal,\n): Promise<{ kept: string[]; dropped: string[] }> {\n const normalizedBounds = normalizeAllowedDirectories(bounds);\n const kept: string[] = [];\n const dropped: string[] = [];\n for (const root of roots.map(normalizePath)) {\n const real = await projectRealPath(root, signal);\n (real !== undefined && isPathWithinDirectories(real, normalizedBounds) ? kept : dropped).push(\n root,\n );\n }\n return { kept, dropped };\n}\n\n// ---------------------------------------------------------------------------\n// Resolver pipeline\n// ---------------------------------------------------------------------------\n\nexport function normalizeAllowedDirectories(dirs: readonly string[]): string[] {\n return [...new Set(dirs.map(normalizeAllowedDirectory).filter((entry) => entry.length > 0))];\n}\n\nasync function resolveRealPath(normalized: string, signal?: AbortSignal): Promise<string | null> {\n try {\n signal?.throwIfAborted();\n const realPath = await withAbort(realpath(normalized), signal);\n return normalizeAllowedDirectory(realPath);\n } catch (error) {\n rethrowIfAborted(error);\n // Only suppress ENOENT — the path genuinely does not exist.\n // EACCES, EIO, and other unexpected errors are rethrown so callers\n // cannot silently operate with a narrowed allowed-directory set.\n if (isNotFoundErrno(error)) {\n return null;\n }\n throw error;\n }\n}\n\nasync function resolveRootSpellings(\n dirs: readonly string[],\n signal?: AbortSignal,\n): Promise<RootSpellings[]> {\n const roots = normalizeAllowedDirectories(dirs);\n const reals = await Promise.all(roots.map((dir) => resolveRealPath(dir, signal)));\n return roots.map((root, i) => {\n const real = reals[i];\n return { root, spellings: real && !isSamePath(real, root) ? [root, real] : [root] };\n });\n}\n\nexport async function resolveAllowedDirectoriesState(\n dirs: readonly string[],\n signal?: AbortSignal,\n): Promise<string[]> {\n const entries = await resolveRootSpellings(dirs, signal);\n return [...new Set(entries.flatMap((entry) => entry.spellings))];\n}\n\n/**\n * TOCTOU note: validation returns a plain string and the fs operation is a\n * later syscall, so a symlink swapped in between could redirect it. Reads and\n * appends in core/fs.ts open with O_NOFOLLOW (final component) and re-check the\n * fd's real path on Linux (ancestor swap). stat's directory count opens and\n * re-checks the directory, and reads entries through that descriptor, only on\n * Linux with procfs; elsewhere it reads the directory by path, so the swap\n * window stays there, like the read path's non-Linux residual. Residuals: the\n * directory walker behind list and the search tools (core/glob.ts) still\n * resolves each entry by path, as the count does for a symlinked entry;\n * write paths that resolve a parent by name\n * (`rename`, `mkdir`, `rm`, `cp`) would need openat()-relative-to-dirfd, which\n * Node lacks; non-Linux skips the fd re-check.\n */\nexport class PathGuard {\n private allowedDirectoriesState: string[] | undefined;\n /** One entry per configured or granted root, without the realpath aliases. */\n private rootsState: string[] | undefined;\n private readonly sensitive = new SensitiveMatcher();\n /**\n * Directories added by an accepted access grant (R8) — from the tool\n * executor's grant round-trip, or from a client's declared workspace roots on\n * the legacy stdio leg. Held separately from the configured baseline because\n * `recomputeAllowedDirectories` rebuilds that baseline from CLI args and env\n * on every call: anything merged into it would be recomputed away.\n *\n * These are NOT filtered against the baseline. A granted directory is\n * out-of-baseline by definition, so a baseline filter dropped every one while\n * `applyGrant` still reported success — the whole round-trip prompted the\n * user and then changed nothing. FS_ROOT_BOUNDARY and the unsafe-path denylist\n * remain the limits, both checked in `applyGrant` — the only writer of this\n * field — so the recompute can rebuild from it without re-filtering.\n */\n private grantedDirectories: string[] = [];\n /**\n * The grants a user approved for a path a tool asked about, as opposed to\n * the workspace roots a client declared. Relative paths never resolve\n * against these: one approval must not change what a relative path names,\n * neither in a later round of the same call nor in a later call.\n */\n private accessGrants: string[] = [];\n /**\n * Where relative paths resolve: the roots minus `accessGrants`, each with\n * its spellings, plus every spelling flattened for containment.\n */\n private relativeBases:\n { entries: readonly RootSpellings[]; roots: string[]; expanded: string[] } | undefined;\n private rootBoundaries: string[] = [];\n\n private readonly options: ServerOptions | undefined;\n\n constructor(options?: ServerOptions) {\n this.options = options;\n }\n\n /**\n * `expanded` is the containment set (each root plus its realpath alias);\n * `roots` is what a caller choosing \"the root\" sees, one entry per root.\n * `relativeBases` lists the roots relative paths resolve against, with\n * their spellings; without it, every root, each spelled only as given.\n */\n initialize(\n expanded: readonly string[],\n roots: readonly string[] = expanded,\n relativeBases?: readonly RootSpellings[],\n ): void {\n this.allowedDirectoriesState = normalizeAllowedDirectories(expanded);\n this.rootsState = normalizeAllowedDirectories(roots);\n const entries = (\n relativeBases ?? this.rootsState.map((root) => ({ root, spellings: [root] }))\n ).map((entry) => ({\n root: normalizeAllowedDirectory(entry.root),\n spellings: normalizeAllowedDirectories(entry.spellings),\n }));\n this.relativeBases = {\n entries,\n roots: entries.map((entry) => entry.root),\n expanded: relativeBases\n ? normalizeAllowedDirectories(entries.flatMap((entry) => entry.spellings))\n : this.allowedDirectoriesState,\n };\n }\n\n isInitialized(): boolean {\n return this.allowedDirectoriesState !== undefined;\n }\n\n getAllowedDirectories(): string[] {\n if (!this.allowedDirectoriesState) {\n return [];\n }\n return [...this.allowedDirectoriesState];\n }\n\n /**\n * The allowed directory that contains `resolvedPath` — the longest match,\n * so a nested root wins over its parent — or `undefined` when none does.\n * Walks use it as the `.gitignore` ceiling: rules above an allowed root are\n * never read.\n */\n allowedRootContaining(resolvedPath: string): string | undefined {\n const normalized = normalizePath(resolvedPath);\n let best: string | undefined;\n for (const dir of this.getAllowedDirectories()) {\n if (\n isPathInsideDirectory(dir, normalized) &&\n (best === undefined || dir.length > best.length)\n ) {\n best = dir;\n }\n }\n return best;\n }\n\n /**\n * The roots as configured or granted, one per root. getAllowedDirectories()\n * also holds each root's realpath alias for containment, so an aliased root\n * (symlink, junction, 8.3 short name) appears there twice.\n */\n getRoots(): string[] {\n return this.rootsState ? [...this.rootsState] : [];\n }\n\n isSensitive(filePath: string): boolean {\n return this.sensitive.isSensitive(filePath);\n }\n\n /**\n * True when a walked entry is inside the allowed directories and not sensitive,\n * on both its requested and resolved paths. Every entry is realpath-resolved: a\n * symlinked ancestor pointing outside the sandbox would otherwise pass the\n * lexical check (fs.glob follows symlinks and yields external entries as\n * non-symlink dirents). Escapes and sensitive targets throw ACCESS_DENIED and\n * skippable errors filter the entry; anything else is fatal. The accepted\n * TOCTOU window is documented on this class.\n */\n async isEntryAccessible(entryPath: string): Promise<boolean> {\n try {\n const { requestedPath, resolvedPath } = await this.validateExistingPathDetailed(entryPath);\n return !this.isSensitive(requestedPath) && !this.isSensitive(resolvedPath);\n } catch (error) {\n if (isFsError(error)) {\n if (SKIPPABLE_FS_CODES.has(error.code)) return false;\n throw error;\n }\n if (isSkippableErrno(error)) return false;\n throw error;\n }\n }\n\n async validateExistingPath(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n return details.resolvedPath;\n }\n\n /**\n * True when a grant must never admit `targetDir` — an unsafe path (home,\n * /etc, C:\\Windows, ...). Checked on the lexical path AND on the one it\n * resolves to: `resolveAllowedDirectoriesState` pushes each root's realpath\n * into the allowed set, so a lexical-only check let a symlink or junction\n * aliasing $HOME/C:\\Windows in under an innocuous name — and the confirmation\n * prompt showed the alias, not the target. A target that cannot be resolved\n * is judged lexically; it does not exist to escape into.\n */\n private async isUnsafeGrantTarget(targetDir: string): Promise<boolean> {\n const normalized = normalizePath(targetDir);\n if (isUnsafeCwdPath(normalized)) return true;\n let resolved: string;\n try {\n resolved = normalizePath(await realpath(normalized));\n } catch {\n return false;\n }\n return !isSamePath(resolved, normalized) && isUnsafeCwdPath(resolved);\n }\n\n /** Walk up from a blocked path to the closest existing ancestor directory. */\n private async resolveGrantTargetDir(blockedPath: string): Promise<string> {\n let targetDir = blockedPath;\n for (;;) {\n const parent = dirname(targetDir);\n try {\n // An existing directory is the grant target; an existing file grants\n // its parent directory.\n return (await stat(targetDir)).isDirectory() ? targetDir : parent;\n } catch {\n // Missing — keep walking up.\n }\n if (parent === targetDir) return targetDir;\n targetDir = parent;\n }\n }\n\n /**\n * Pre-check (no mutation, no callback): given the paths a tool is about to\n * operate on, return the sorted, de-duplicated grant-target directories for\n * those that are outside the allowed roots AND grantable (within FS_ROOT_BOUNDARY\n * when it is configured). A path whose nearest existing ancestor escapes the\n * boundary is NOT grantable and is omitted — the operation will fail with\n * ACCESS_DENIED for it rather than prompt. The caller returns an\n * `input_required` result carrying exactly this set (R7), and on retry applies\n * each accepted grant via {@link applyGrant} (R8). `requestState` binds this\n * set so a grant accepted for X cannot authorize Y (R9).\n */\n async precheckAccess(paths: readonly string[]): Promise<string[]> {\n if (!this.allowedDirectoriesState || paths.length === 0) {\n return [];\n }\n const allowedDirs = this.allowedDirectoriesState;\n const grantDirs: string[] = [];\n for (const requested of paths) {\n if (!requested) {\n continue;\n }\n // Validation refuses it for its shape whatever is granted, so asking the\n // user to grant the working directory it resolves under is pointless.\n if (\n isUnresolvableDrivePath(requested) ||\n (IS_WINDOWS && getReservedDeviceNameForPath(requested) !== undefined)\n ) {\n continue;\n }\n const normalized = normalizePath(requested);\n if (this.isSensitive(requested) || this.isSensitive(normalized)) {\n continue;\n }\n if (isPathWithinDirectories(normalized, allowedDirs)) {\n continue;\n }\n const targetDir = normalizePath(await this.resolveGrantTargetDir(normalized));\n // Never offer a grant into a bare filesystem root or an unsafe path (home,\n // /etc, C:\\Windows, ...). Without FS_ROOT_BOUNDARY, isWithinBoundary returns\n // true for everything, so this is the one guard that still rejects roots a\n // misleading grant could reach. Mirrors the check gating --allow-cwd.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n continue;\n }\n if (grantDirs.some((dir) => isSamePath(dir, targetDir))) {\n continue;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n continue;\n }\n grantDirs.push(targetDir);\n }\n return grantDirs.sort();\n }\n\n /**\n * Apply an accepted access grant: enforce FS_ROOT_BOUNDARY again (a TOCTOU\n * re-check against the boundary resolved at config time), then extend the\n * allowed roots for the remainder of the session (R8, A4). Returns false when\n * the boundary or the unsafe-path denylist blocks the grant, AND when the\n * recompute did not actually admit the directory — the return value now\n * reports what the guard's allowed set really holds, not merely that the\n * pre-checks passed. The caller leaves a refused path to fail with\n * ACCESS_DENIED during the operation. Idempotent: re-granting an already\n * allowed directory is a no-op via the dedup in `initialize`.\n *\n * `kind` says where the grant came from: `access` for a path the user\n * approved when a tool asked, `workspace` for a root the client declared.\n * Only a workspace root becomes a place relative paths resolve.\n */\n async applyGrant(targetDir: string, kind: 'access' | 'workspace' = 'access'): Promise<boolean> {\n // Defense-in-depth: even a tampered/accepted grant cannot extend roots into\n // an unsafe path. The precheckAccess guard already refuses to offer these;\n // this catches a grant that arrived by another route — the legacy stdio\n // `roots/list` seeding, which has no confirmation round-trip at all.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n return false;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n return false;\n }\n // Read + write under the mutation lock so a concurrent grant cannot\n // interleave and lose this grant (GRANT-1). runExclusive is not reentrant:\n // nothing in this body may take the lock again.\n return runExclusive(this, async () => {\n const previous = this.grantedDirectories;\n const previousAccess = this.accessGrants;\n const dir = normalizePath(targetDir);\n this.grantedDirectories = [...previous, dir];\n this.accessGrants =\n kind === 'access'\n ? [...previousAccess, dir]\n : previousAccess.filter((grant) => !isSamePath(grant, dir));\n const rollBack = (): void => {\n this.grantedDirectories = previous;\n this.accessGrants = previousAccess;\n };\n try {\n await this.recomputeAllowedDirectories();\n } catch (error) {\n // A failed recompute leaves the guard with its previous, consistent view.\n rollBack();\n throw error;\n }\n // Verify rather than assume: a boundary that no longer covers this\n // directory would drop it in the recompute above, and reporting success\n // for a grant that did not land is exactly the failure this replaces.\n if (isPathWithinDirectories(dir, this.getAllowedDirectories())) {\n return true;\n }\n rollBack();\n await this.recomputeAllowedDirectories();\n return false;\n });\n }\n\n // One boundary rule for startup roots and grants: a missing target is\n // projected through its nearest existing ancestor, an unresolvable one is\n // refused. No boundary configured means no constraint; partitionRootsWithin\n // with no bounds would drop everything, so that case returns first.\n private async isWithinBoundary(targetDir: string): Promise<boolean> {\n if (this.rootBoundaries.length === 0) {\n return true;\n }\n const { kept } = await partitionRootsWithin([targetDir], this.rootBoundaries);\n return kept.length > 0;\n }\n\n private validateAccess(requestedPath: string): {\n normalizedRequested: string;\n allowedDirs: string[];\n accessDeniedHint: string;\n } {\n if (!this.allowedDirectoriesState) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'PathGuard not initialized. Call initialize() first.',\n requestedPath,\n );\n }\n // Before the drive-relative check, whose advice (`use C:\\path`) is wrong here.\n if (!IS_WINDOWS && isWindowsDrivePath(requestedPath)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `Windows-style path received on a POSIX host (${process.platform}). Use a POSIX path, such as one under an allowed root.`,\n requestedPath,\n undefined,\n 'Run list_roots to see the allowed directories.',\n );\n }\n if (isWindowsDriveRelativePath(requestedPath)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Drive-relative paths are not allowed. Use C:\\\\path or C:/path instead of C:path.',\n requestedPath,\n );\n }\n // A device name is only a device on Windows; on POSIX `aux.ts` is a file.\n const reservedDevice = IS_WINDOWS ? getReservedDeviceNameForPath(requestedPath) : undefined;\n if (reservedDevice) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `Reserved Windows device name not allowed: ${reservedDevice}.`,\n requestedPath,\n );\n }\n\n const normalizedRequested = normalizePath(requestedPath);\n const allowedDirs = this.allowedDirectoriesState;\n\n const accessDeniedHint =\n allowedDirs.length > 0\n ? `Allowed: ${allowedDirs.join(', ')}`\n : 'No allowed directories configured.';\n\n if (!isPathWithinDirectories(normalizedRequested, allowedDirs)) {\n // Out of root. The access-grant `input_required` round-trip is driven by\n // the executor's pre-check (precheckAccess) BEFORE the operation runs, so\n // by the time validation reaches here any grantable out-of-root path has\n // already been accepted and added to the allowed set. A path still out of\n // root here was either declined, ungrantable (outside FS_ROOT_BOUNDARY), or\n // never pre-checked — fail closed.\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n this.assertNotSensitiveFile(requestedPath, requestedPath);\n this.assertNotSensitiveFile(normalizedRequested, requestedPath);\n\n return {\n normalizedRequested,\n allowedDirs: this.allowedDirectoriesState,\n accessDeniedHint,\n };\n }\n\n private async handleRealpathError(\n error: unknown,\n normalizedRequested: string,\n allowedDirs: string[],\n accessDeniedHint: string,\n requestedPath: string,\n ): Promise<never> {\n if (isNotFoundErrno(error)) {\n // Resolve the nearest existing ancestor to detect out-of-sandbox symlinks.\n // e.g. if `link -> C:\\external` and path is `link\\nonexistent.txt`, the\n // ancestor resolves outside allowed dirs → ACCESS_DENIED, not NOT_FOUND.\n try {\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n } catch (ancestorErr) {\n // Rethrow any FsError — collapsing e.g. UNKNOWN to NOT_FOUND would mask\n // incomplete sandbox checks and make bugs invisible to callers.\n if (isFsError(ancestorErr)) {\n throw ancestorErr;\n }\n }\n\n throw new FsError(ErrorCode.NOT_FOUND, 'Path not found', requestedPath, error);\n }\n\n const mapped = classifyCauseChain(error);\n throw new FsError(\n mapped.code,\n 'Cannot access path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n async validateExistingPathDetailed(requestedPath: string): Promise<ValidatedPathDetails> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccess(requestedPath);\n\n let spelled = normalizedRequested;\n let realPath: string;\n try {\n realPath = await realpath(spelled);\n } catch (error) {\n // Not on disk as typed; it may be under another Unicode spelling (#101).\n const respelled = isNotFoundErrno(error)\n ? await this.respellMissing(spelled, allowedDirs, accessDeniedHint, requestedPath)\n : undefined;\n if (respelled === undefined) {\n realPath = await this.handleRealpathError(\n error,\n spelled,\n allowedDirs,\n accessDeniedHint,\n requestedPath,\n );\n } else {\n this.assertNotSensitiveFile(respelled, requestedPath);\n spelled = respelled;\n try {\n realPath = await realpath(spelled);\n } catch (retryError) {\n realPath = await this.handleRealpathError(\n retryError,\n spelled,\n allowedDirs,\n accessDeniedHint,\n requestedPath,\n );\n }\n }\n }\n\n const normalizedReal = normalizePath(realPath);\n\n if (!isPathWithinDirectories(normalizedReal, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Re-check the resolved real path: a symlink inside an allowed root may\n // point at a sensitive file (e.g. link -> .env). The early check above only\n // sees the requested/normalized path, not the symlink target.\n this.assertNotSensitiveFile(normalizedReal, requestedPath);\n\n return {\n requestedPath: spelled,\n resolvedPath: normalizedReal,\n isSymlink: !isSamePath(spelled, normalizedReal),\n };\n }\n\n async validateExistingDirectory(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n\n let stats: Stats;\n try {\n stats = await stat(details.resolvedPath);\n } catch (error) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot access directory',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requestedPath);\n }\n\n return details.resolvedPath;\n }\n\n resolvePathOrRoot(pathValue: string | undefined): string {\n if (pathValue && pathValue.trim().length > 0) {\n return pathValue;\n }\n const roots = this.getRoots();\n if (roots.length === 0) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'No roots configured. Use the roots tool or --allow-cwd.',\n );\n }\n if (roots.length > 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Multiple roots configured. Provide an explicit path.',\n );\n }\n const root = roots[0];\n if (!root) {\n throw new FsError(ErrorCode.ACCESS_DENIED, 'Workspace root is unexpectedly undefined');\n }\n return root;\n }\n\n /**\n * Where the relative paths a caller sent should point, keyed by the spelling\n * sent; a path that keeps its meaning is absent. A relative path resolves\n * against the working directory, as it always has, while that lands inside\n * a root. When it does not — a desktop host starts the server outside the\n * project, and an HTTP client never shares its working directory — it\n * resolves against the roots: the one root, or with several, the one it\n * exists under. Searches print paths relative to the root (`resultPaths`),\n * so this is what lets one tool's output feed the next.\n *\n * \"Roots\" here are the configured ones and those a client declared, never an\n * access grant (see `accessGrants`). Absolute, `~` and drive paths keep their\n * meaning, as does every path when there is no such root; validation judges\n * them as before. A Windows drive path on POSIX is left for validation to\n * refuse: joined to a root, `C:\\x` would be an absolute path that passes. A\n * relative path that exists under several roots, or under none of several,\n * is refused rather than guessed.\n */\n async resolveRelativePaths(paths: readonly string[]): Promise<Map<string, string>> {\n const resolved = new Map<string, string>();\n const bases = this.relativeBases;\n if (!bases || bases.roots.length === 0) return resolved;\n for (const requested of new Set(paths)) {\n if (\n requested.trim().length === 0 ||\n isUnresolvableDrivePath(requested) ||\n isAbsolute(expandHome(requested)) ||\n isPathWithinDirectories(normalizePath(requested), bases.expanded)\n ) {\n continue;\n }\n resolved.set(requested, await this.resolveAgainstRoots(requested, bases.roots));\n }\n return resolved;\n }\n\n private async resolveAgainstRoots(requested: string, roots: readonly string[]): Promise<string> {\n const candidates = roots.map((root) => join(root, requested));\n const [first] = candidates;\n if (first === undefined || candidates.length === 1) return first ?? requested;\n // Each candidate is probed through validation itself, so the probe learns\n // nothing validation would not tell the caller: a denylisted name is\n // refused before the filesystem is touched, and a path through a symlink\n // out of the roots is refused whether or not its target exists.\n const existing: string[] = [];\n let refused: string | undefined;\n for (const candidate of candidates) {\n try {\n await this.validateExistingPath(candidate);\n existing.push(candidate);\n } catch (error) {\n if (isFsError(error) && error.code === ErrorCode.NOT_FOUND) continue;\n refused ??= candidate;\n }\n }\n const [only] = existing;\n if (only !== undefined && existing.length === 1) return only;\n // Validation then refuses it again, with its own reason.\n if (existing.length === 0 && refused !== undefined) return refused;\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n existing.length === 0\n ? `Relative path exists under none of the ${String(roots.length)} roots, so it names no one file. Pass an absolute path under one of: ${roots.join(', ')}.`\n : `Relative path exists under more than one root: ${existing.join(', ')}. Pass the absolute path you mean.`,\n requested,\n );\n }\n\n /**\n * How a tool prints the paths a walk of `walkedDir` found, so the caller can\n * pass each one straight back: relative to the root holding `walkedDir`\n * when `resolveRelativePaths` maps that spelling back to the same path,\n * absolute otherwise. A scoped search of `src` prints `src/a.ts`, not\n * `a.ts`, which would name `<root>/a.ts`. `root` is the root as configured,\n * as `list_roots` shows it, even when the walk went through its realpath.\n *\n * The absolute fallback covers a working directory in a subfolder of a root\n * (relative paths resolve there), a directory under an access grant only\n * (relative paths never resolve there), several roots with the working\n * directory outside them (a name two roots share would read back as\n * ambiguous, and which names are shared can change between the print and\n * the read), and a name that would not read back as given: `~`, blank, or a\n * POSIX name holding a backslash or starting with a drive (`C:`).\n */\n resultPaths(walkedDir: string): ResultPaths {\n const walked = normalizePath(walkedDir);\n const bases = this.relativeBases;\n let found: { root: string; spelling: string } | undefined;\n for (const entry of bases?.entries ?? []) {\n for (const spelling of entry.spellings) {\n if (\n isPathInsideDirectory(spelling, walked) &&\n (found === undefined || spelling.length > found.spelling.length)\n ) {\n found = { root: entry.root, spelling };\n }\n }\n }\n if (found === undefined || bases === undefined) {\n return { root: walked, atRoot: false, show: (absolutePath) => absolutePath };\n }\n const { root, spelling } = found;\n const allowed = bases.expanded;\n const severalRoots = bases.roots.length > 1;\n return {\n root,\n atRoot: isSamePath(spelling, walked),\n show: (absolutePath) => {\n // Windows separators become `/`, which every tool accepts there; on\n // POSIX a backslash is part of a name and must stay one.\n const rel = IS_WINDOWS\n ? toPosixRelative(spelling, absolutePath)\n : relative(spelling, absolutePath);\n if (\n rel.trim().length === 0 ||\n rel === '..' ||\n rel.startsWith('../') ||\n isAbsolute(rel) ||\n isUnresolvableDrivePath(rel) ||\n expandHome(rel) !== rel ||\n !isSamePath(resolve(spelling, rel), absolutePath)\n ) {\n return absolutePath;\n }\n // Mirrors resolveRelativePaths: a spelling that lands inside a root\n // from the working directory resolves there, so it must land here.\n const fromCwd = normalizePath(rel);\n if (isPathWithinDirectories(fromCwd, allowed)) {\n return isSamePath(fromCwd, absolutePath) ? rel : absolutePath;\n }\n return severalRoots ? absolutePath : rel;\n },\n };\n }\n\n isAllowedRoot(normalizedPath: string): boolean {\n // isSamePath case-folds on case-insensitive filesystems (win + darwin),\n // matching the containment checks used everywhere else in the guard. The\n // previous IS_WINDOWS-only fold left darwin doing exact-case compares here\n // while isPathInsideDirectory folded — a root matched case-insensitively\n // everywhere else was missed.\n return this.getAllowedDirectories().some((dir) => isSamePath(dir, normalizedPath));\n }\n\n // Checks ONLY the sensitive-file denylist. Root containment and symlink\n // resolution must be verified separately (e.g. via validateExistingPath).\n private assertNotSensitiveFile(checkPath: string, requestedPath: string): void {\n if (this.isSensitive(checkPath)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Sensitive file blocked by the server denylist. To unblock one built-in secret-file pattern, restart the server with --allow <pattern> (or FS_ALLOWLIST); --allow-sensitive (or FS_ALLOW_SENSITIVE=1) turns the whole built-in list off. Paths blocked with --deny or FS_DENYLIST stay blocked.',\n requestedPath,\n );\n }\n }\n\n private throwAccessDenied(requestedPath: string, hint?: string): never {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n hint ? `Outside allowed directories. ${hint}` : 'Outside allowed directories.',\n requestedPath,\n );\n }\n private async resolveNearestExistingAncestor(\n requestedPath: string,\n currentPath: string,\n ): Promise<{ realAncestor: string; resolvedTarget: string }> {\n const missingSegments: string[] = [];\n let current = currentPath;\n let hops = 0;\n for (;;) {\n try {\n const realAncestor = normalizePath(await realpath(current));\n const resolvedTarget =\n missingSegments.length === 0\n ? realAncestor\n : normalizePath(join(realAncestor, ...missingSegments.reverse()));\n return { realAncestor, resolvedTarget };\n } catch (error) {\n try {\n const stats = await lstat(current);\n if (stats.isSymbolicLink()) {\n if (++hops > MAX_SYMLINK_HOPS) {\n throw new FsError(\n ErrorCode.SYMLINK_NOT_ALLOWED,\n 'Too many symbolic links encountered',\n requestedPath,\n );\n }\n const allowedDirs = this.getAllowedDirectories();\n const linkParent = normalizePath(await realpath(dirname(current)));\n if (!isPathWithinDirectories(linkParent, allowedDirs)) {\n this.throwAccessDenied(requestedPath);\n }\n const target = await readlink(current);\n const resolvedTarget = isAbsolute(target) ? target : resolve(linkParent, target);\n const normalizedTarget = normalizePath(resolvedTarget);\n if (!isPathWithinDirectories(normalizedTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath);\n }\n current =\n missingSegments.length === 0\n ? normalizedTarget\n : normalizePath(join(normalizedTarget, ...missingSegments.reverse()));\n missingSegments.length = 0;\n continue;\n }\n } catch (lstatErr) {\n if (isFsError(lstatErr)) {\n throw lstatErr;\n }\n // ENOENT is expected during ancestor walk — the entry simply doesn't exist.\n // Any other error (EACCES, EIO, ELOOP) is unexpected; fail safe.\n if (!isNotFoundErrno(lstatErr)) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot probe symlink ancestor',\n requestedPath,\n lstatErr instanceof Error ? lstatErr : undefined,\n );\n }\n }\n const parent = dirname(current);\n if (parent === current) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot resolve path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n missingSegments.push(basename(current));\n current = parent;\n }\n }\n }\n\n /**\n * `normalized` re-spelled with the on-disk names of the components that are\n * missing as typed but present under another Unicode spelling (#101), or\n * `undefined` when no component changed. Only names below a root are\n * matched, never the root's own. Each directory is scanned only after its\n * real path is checked to be inside the roots, and each matched entry is\n * realpathed and checked again, so a link found this way cannot lead out\n * (`resolveLast: false` leaves the last one unresolved, for delete). The\n * first component with no match ends the walk; it and the rest stay as\n * typed, so a write still creates them. The result is a spelling only:\n * the caller runs its own containment and denylist checks on it.\n */\n private async respellMissing(\n normalized: string,\n allowedDirs: readonly string[],\n accessDeniedHint: string,\n requestedPath: string,\n resolveLast = true,\n ): Promise<string | undefined> {\n const root = this.allowedRootContaining(normalized);\n if (root === undefined || !mayHaveEquivalentSpelling(normalized.slice(root.length))) {\n return undefined;\n }\n const missing: string[] = [];\n let ancestor = normalized;\n let real: string;\n for (;;) {\n try {\n real = normalizePath(await realpath(ancestor));\n break;\n } catch (error) {\n if (!isNotFoundErrno(error) || isSamePath(ancestor, root)) return undefined;\n missing.unshift(basename(ancestor));\n ancestor = dirname(ancestor);\n }\n }\n // An ancestor that resolves out of the roots is the caller's to refuse.\n if (!isPathWithinDirectories(real, allowedDirs)) return undefined;\n\n const spelled: string[] = [];\n for (const [index, name] of missing.entries()) {\n let onDisk: string | undefined = name;\n if (mayHaveEquivalentSpelling(name)) {\n // The scan must not reveal what the denylist hides: a name it covers\n // under the resolved parent is refused as the exact spelling is, and\n // a directory `list` would refuse is never read.\n this.assertNotSensitiveFile(join(real, ...missing.slice(index)), requestedPath);\n if (this.isSensitive(real)) break;\n onDisk = await this.findEquivalentEntry(real, name, requestedPath);\n }\n if (onDisk === undefined) break;\n spelled.push(onDisk);\n if (!resolveLast && index === missing.length - 1) break;\n try {\n real = normalizePath(await realpath(join(ancestor, ...spelled)));\n } catch {\n // Missing as typed, or a dangling link: the caller's checks judge it.\n break;\n }\n if (!isPathWithinDirectories(real, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n }\n if (spelled.every((name, index) => name === missing[index])) return undefined;\n return join(ancestor, ...spelled, ...missing.slice(spelled.length));\n }\n\n /**\n * The entry of `realDir` that `name` names: `name` itself when it is\n * there, else the one entry with the same `unicodeMatchKey`. Two or more\n * such entries are refused, never chosen between. `undefined` when none\n * matches or the directory cannot be read.\n */\n private async findEquivalentEntry(\n realDir: string,\n name: string,\n requestedPath: string,\n ): Promise<string | undefined> {\n let entries: string[];\n try {\n entries = await readdir(realDir);\n } catch {\n return undefined;\n }\n if (entries.includes(name)) return name;\n const key = unicodeMatchKey(name);\n const matches = entries.filter((entry) => unicodeMatchKey(entry) === key);\n // A match the denylist covers is refused here, as a lone one would be by\n // the caller, so the ambiguity error below never lists a hidden name.\n for (const match of matches) this.assertNotSensitiveFile(join(realDir, match), requestedPath);\n if (matches.length > 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `Ambiguous Unicode path component \"${name}\": ${String(matches.length)} entries in ${realDir} match it (${matches.map(escapeNonAscii).join(', ')}). Pass the exact name, using \\\\u escapes for the characters shown that way.`,\n requestedPath,\n );\n }\n return matches[0];\n }\n\n async validatePathForWrite(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccess(requestedPath);\n\n let { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n const assertInside = (): void => {\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n };\n assertInside();\n // Part of it is not on disk as typed; it may be under another Unicode\n // spelling (#101). A followed link may also name a missing target spelled\n // differently on disk, and respelling a parent can expose such a link, so\n // repeat until the resolution settles, re-running the checks each round.\n let candidate = normalizedRequested;\n for (let round = 0; realAncestor !== resolvedTarget && round < MAX_SYMLINK_HOPS; round++) {\n const respelled =\n (await this.respellMissing(candidate, allowedDirs, accessDeniedHint, requestedPath)) ??\n (isSamePath(resolvedTarget, candidate)\n ? undefined\n : await this.respellMissing(\n resolvedTarget,\n allowedDirs,\n accessDeniedHint,\n requestedPath,\n ));\n if (respelled === undefined) break;\n this.assertNotSensitiveFile(respelled, requestedPath);\n ({ realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n respelled,\n ));\n assertInside();\n candidate = respelled;\n }\n // Re-check the resolved target: a symlink inside an allowed root may point\n // at a sensitive file. Writing through such a link must be blocked too.\n this.assertNotSensitiveFile(resolvedTarget, requestedPath);\n return resolvedTarget;\n }\n\n async validatePathForDelete(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccess(requestedPath);\n\n // A workspace root is refused here, before the parent-containment check\n // below reaches for a parent that is out-of-root by construction and\n // reports \"Outside allowed directories\" for the one directory the caller\n // can see IS allowed. delete-file.ts keeps its own root check for the\n // nested-root case, where the parent is itself an allowed root and this\n // path is never taken.\n if (this.isAllowedRoot(normalizedRequested)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Deleting a workspace root directory is not allowed',\n requestedPath,\n );\n }\n\n // Probe the final component before the parent, so a target that is not on\n // disk as typed can be re-spelled (#101) before either is checked.\n let target = normalizedRequested;\n let stats: Stats | undefined;\n try {\n stats = await lstat(target);\n } catch (error) {\n // The link itself is deleted, so the last component stays unresolved.\n const respelled = isNotFoundErrno(error)\n ? await this.respellMissing(target, allowedDirs, accessDeniedHint, requestedPath, false)\n : undefined;\n if (respelled !== undefined) {\n this.assertNotSensitiveFile(respelled, requestedPath);\n target = respelled;\n stats = await lstat(target).catch(() => undefined);\n }\n }\n\n const parent = dirname(target);\n let realParent: string;\n try {\n realParent = await realpath(parent);\n } catch (error) {\n throw new FsError(\n ErrorCode.NOT_FOUND,\n 'Parent directory not found',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n const normalizedRealParent = normalizePath(realParent);\n\n if (!isPathWithinDirectories(normalizedRealParent, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // A target that does not exist needs only the parent check above.\n if (stats === undefined) return target;\n // For deletion, we ONLY block if the real target is outside the sandbox\n // IF the target is NOT a symlink. Deleting a symlink is safe even if it\n // points outside.\n if (stats.isSymbolicLink()) {\n // Symlink: check link sensitivity but don't resolve target.\n // The parent check above ensures the link itself is in an allowed root.\n // Spelled through the validated real parent, so every path to one link\n // (an aliased parent, another Unicode spelling) names it the same way.\n const link = join(normalizedRealParent, basename(target));\n this.assertNotSensitiveFile(target, requestedPath);\n this.assertNotSensitiveFile(link, requestedPath);\n return link;\n }\n\n // Not a symlink: resolve to catch path escapes (e.g. /allowed/dir/../../etc)\n // and block sensitive files. A probe failure (ENOENT and friends) falls\n // back to the parent check, which is sufficient for a path that does not\n // exist.\n let realTarget: string;\n try {\n realTarget = normalizePath(await realpath(target));\n } catch {\n return target;\n }\n if (!isPathWithinDirectories(realTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n this.assertNotSensitiveFile(realTarget, requestedPath);\n return realTarget;\n }\n\n async recomputeAllowedDirectories(): Promise<void> {\n const cliAllowedDirs = normalizeAllowedDirectories(this.options?.cliAllowedDirs ?? []);\n\n // Parse allowed directories from environment variable\n const allowMissing =\n cli.allowMissingRoots ??\n parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');\n const envAllowedDirs = await resolveConfiguredDirs('FS_ALLOWED_DIRS', { allowMissing });\n\n // Parse FS_ROOT_BOUNDARY (the --root-boundary flag beats the env var)\n const boundaries = await resolveConfiguredDirs('FS_ROOT_BOUNDARY', {\n resolveReal: true,\n ...(cli.rootBoundary !== undefined ? { rawValue: cli.rootBoundary } : {}),\n });\n\n const allowCwd = Boolean(this.options?.allowCwd);\n const allowCwdDirs: string[] = [];\n if (allowCwd) {\n let cwd = normalizePath(process.cwd());\n const walkCwd =\n cli.allowCwdWalk ?? parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n if (walkCwd) {\n cwd = await findProjectRoot(cwd, [...boundaries, homedir()]);\n }\n if (isUnsafeCwdPath(cwd)) {\n Logger.warn(`Skipped adding unsafe current working directory to allowed list: ${cwd}`);\n } else {\n allowCwdDirs.push(cwd);\n }\n }\n\n const signal = AbortSignal.timeout(ROOTS_TIMEOUT_MS);\n let baseline = [...cliAllowedDirs, ...envAllowedDirs, ...allowCwdDirs];\n // FS_ROOT_BOUNDARY is the only filter grants answer to (see\n // `grantedDirectories`); without one they pass through as accepted.\n let grantsToInclude = this.grantedDirectories;\n if (boundaries.length > 0) {\n const { kept, dropped } = await partitionRootsWithin(baseline, boundaries, signal);\n for (const root of dropped) {\n Logger.warn(\n `Skipped allowed root outside FS_ROOT_BOUNDARY (${boundaries.join(', ')}): ${root}`,\n );\n }\n baseline = kept;\n grantsToInclude = (await partitionRootsWithin(this.grantedDirectories, boundaries, signal))\n .kept;\n }\n\n const combined = [...baseline, ...grantsToInclude];\n const spellings = await resolveRootSpellings(combined, signal);\n const nextState = [...new Set(spellings.flatMap((entry) => entry.spellings))];\n // A configured root stays a base even when it was also granted.\n const accessGrants = this.accessGrants;\n const bases = spellings.filter(\n (entry) =>\n baseline.some((dir) => isSamePath(dir, entry.root)) ||\n !accessGrants.some((grant) => isSamePath(grant, entry.root)),\n );\n // Commit boundaries and the allowed set together, after every await has\n // resolved, so a rejecting recompute leaves the guard's previous,\n // consistent view intact.\n this.rootBoundaries = boundaries;\n this.initialize(nextState, combined, bases);\n }\n}\n"]}
@@ -1,6 +1,8 @@
1
1
  import type { Stats } from 'node:fs';
2
2
  import type { FileHandle } from 'node:fs/promises';
3
3
  import { FsError } from './errors.ts';
4
+ declare const O_NOFOLLOW: number;
5
+ export { O_NOFOLLOW };
4
6
  export declare const READ_NOFOLLOW: number;
5
7
  export declare const APPEND_NOFOLLOW: number;
6
8
  export declare const APPEND_CREATE_NOFOLLOW: number;
@@ -25,6 +27,7 @@ export type ReadSpec = {
25
27
  export interface ReadFileResult {
26
28
  path: string;
27
29
  content: string;
30
+ fileSize?: number;
28
31
  totalLines?: number;
29
32
  readMode: ReadSpec['kind'];
30
33
  modified: string;
@@ -35,10 +38,17 @@ export interface ReadFileResult {
35
38
  linesRead?: number;
36
39
  hasMoreLines?: boolean;
37
40
  }
38
- export declare function createTooLargeError(bytesRead: number, maxSize: number, requestedPath: string): FsError;
41
+ /** `label` qualifies the size parenthetical; `suggestion` overrides the TOO_LARGE default remedy. */
42
+ export declare function createTooLargeError(bytesRead: number, maxSize: number, requestedPath: string, { label, suggestion }?: {
43
+ label?: string;
44
+ suggestion?: string;
45
+ }): FsError;
39
46
  export declare function readFileBufferWithLimit(handle: FileHandle, maxSize: number, requestedPath: string, signal?: AbortSignal): Promise<Buffer>;
40
47
  export declare const countLines: (content: string) => number;
48
+ /** The text path's binary refusal; `read` matches it to offer `binary: true`. */
49
+ export declare const BINARY_FILE_MESSAGE = "Binary file detected.";
50
+ export declare function isBinaryRefusal(error: unknown): error is FsError;
41
51
  export declare function assertFileStats(filePath: string, stats: Stats): void;
42
- /** Read an already-validated, already-stat'd file per `spec`. */
43
- export declare function readFileWithStats(filePath: string, validPath: string, stats: Stats, spec: ReadSpec, verifyHandle: (handle: FileHandle) => Promise<void>): Promise<ReadFileResult>;
52
+ /** Read an already-validated, already-stat'd file per `spec`, opened via `openVerified`. */
53
+ export declare function readFileWithStats(filePath: string, validPath: string, stats: Stats, spec: ReadSpec, openVerified: () => Promise<FileHandle>): Promise<ReadFileResult>;
44
54
  //# sourceMappingURL=read.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/core/read.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAwC,OAAO,EAAa,MAAM,aAAa,CAAC;AAevF,eAAO,MAAM,aAAa,QAAkC,CAAC;AAE7D,eAAO,MAAM,eAAe,QAAoE,CAAC;AACjG,eAAO,MAAM,sBAAsB,QAC0D,CAAC;AAkC9F,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAazE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GACpB,OAAO,CAMT;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAkCjB;AAED,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,KAAG,MAI5C,CAAC;AAqXF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAIpE;AAED,iEAAiE;AACjE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAClD,OAAO,CAAC,cAAc,CAAC,CAezB"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/core/read.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAa,OAAO,EAAa,MAAM,aAAa,CAAC;AAU5D,QAAA,MAAQ,UAAU,QAGjB,CAAC;AACF,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,eAAO,MAAM,aAAa,QAAkC,CAAC;AAE7D,eAAO,MAAM,eAAe,QAAoE,CAAC;AACjG,eAAO,MAAM,sBAAsB,QAC0D,CAAC;AAa9F,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAazE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,qGAAqG;AACrG,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,EAAE,KAAK,EAAE,UAAU,EAAE,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAQT;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAsCjB;AAED,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,KAAG,MAI5C,CAAC;AAgSF,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAMhE;AA4FD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAIpE;AAED,4FAA4F;AAC5F,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,GACtC,OAAO,CAAC,cAAc,CAAC,CAczB"}
package/dist/core/read.js CHANGED
@@ -53,12 +53,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
53
53
  // File reading pipeline split out of fs.ts. fs.ts imports from here; never the reverse.
54
54
  import { isUtf8 } from 'node:buffer';
55
55
  import { constants } from 'node:fs';
56
- import { open as fsOpen } from 'node:fs/promises';
57
56
  import { StringDecoder } from 'node:string_decoder';
58
57
  import { withAbort } from "./concurrency.js";
59
- import { ErrorCode, formatUnknownErrorMessage, FsError, isFsError } from "./errors.js";
58
+ import { ErrorCode, FsError, isFsError } from "./errors.js";
60
59
  import { hasUtf16Bom, isBinarySample, isKnownBinaryExtension, MIME_SAMPLE_SIZE } from "./mime.js";
61
- import { Logger } from "./observability.js";
62
60
  import { getMaxTextFileSize } from "./util.js";
63
61
  const STREAM_CHUNK_SIZE = 64 * 1024;
64
62
  // O_NOFOLLOW refuses (ELOOP) a final-component symlink, so a regular file
@@ -66,30 +64,11 @@ const STREAM_CHUNK_SIZE = 64 * 1024;
66
64
  // Opens receive the realpath-resolved target, so in-root symlinks still work.
67
65
  // Absent (undefined at runtime, despite @types/node) on Windows; 0 is a no-op.
68
66
  const { O_NOFOLLOW = 0, O_NONBLOCK = 0 } = constants;
67
+ export { O_NOFOLLOW };
69
68
  export const READ_NOFOLLOW = constants.O_RDONLY | O_NOFOLLOW;
70
69
  // O_NONBLOCK: an append to a symlink-swapped FIFO fails fast instead of hanging.
71
70
  export const APPEND_NOFOLLOW = constants.O_WRONLY | constants.O_APPEND | O_NOFOLLOW | O_NONBLOCK;
72
71
  export const APPEND_CREATE_NOFOLLOW = constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_EXCL | O_NOFOLLOW;
73
- async function openReadableFileHandle(filePath, signal) {
74
- const handlePromise = fsOpen(filePath, READ_NOFOLLOW);
75
- if (!signal)
76
- return handlePromise;
77
- try {
78
- return await withAbort(handlePromise, signal);
79
- }
80
- catch (error) {
81
- void handlePromise
82
- .then((handle) => {
83
- void handle.close().catch((closeErr) => {
84
- Logger.warn(`Failed to close file handle for ${filePath} after abort: ${formatUnknownErrorMessage(closeErr)}`);
85
- });
86
- })
87
- .catch(() => {
88
- /* ignore open error */
89
- });
90
- throw error;
91
- }
92
- }
93
72
  async function readProbe(handle, signal) {
94
73
  const buffer = Buffer.allocUnsafe(MIME_SAMPLE_SIZE);
95
74
  const { bytesRead } = await withAbort(handle.read(buffer, 0, MIME_SAMPLE_SIZE, 0), signal);
@@ -98,10 +77,15 @@ async function readProbe(handle, signal) {
98
77
  }
99
78
  return buffer.subarray(0, bytesRead);
100
79
  }
101
- export function createTooLargeError(bytesRead, maxSize, requestedPath) {
102
- return new FsError(ErrorCode.TOO_LARGE, `File exceeds size limit (${bytesRead} > ${maxSize} bytes)`, requestedPath);
80
+ /** `label` qualifies the size parenthetical; `suggestion` overrides the TOO_LARGE default remedy. */
81
+ export function createTooLargeError(bytesRead, maxSize, requestedPath, { label, suggestion } = {}) {
82
+ return new FsError(ErrorCode.TOO_LARGE, `File exceeds size limit (${bytesRead} > ${maxSize} bytes${label ? `, ${label}` : ''})`, requestedPath, undefined, suggestion);
103
83
  }
104
84
  export async function readFileBufferWithLimit(handle, maxSize, requestedPath, signal) {
85
+ // Node emits an already-aborted signal's AbortError twice: once to the
86
+ // iterator and once as an 'error' event no one listens to yet, which is
87
+ // an uncaught exception and shuts the server down. Throw it here instead.
88
+ signal?.throwIfAborted();
105
89
  const stream = handle.createReadStream({
106
90
  start: 0,
107
91
  highWaterMark: STREAM_CHUNK_SIZE,
@@ -166,6 +150,7 @@ const LF = 0x0a;
166
150
  * decodes identically.
167
151
  */
168
152
  async function* readLinesBounded(handle, options, filePath, startLine) {
153
+ options.signal?.throwIfAborted(); // see readFileBufferWithLimit
169
154
  const stream = handle.createReadStream({
170
155
  start: 0,
171
156
  highWaterMark: STREAM_CHUNK_SIZE,
@@ -177,7 +162,10 @@ async function* readLinesBounded(handle, options, filePath, startLine) {
177
162
  let pending = '';
178
163
  let pendingBytes = 0;
179
164
  let lineNumber = 0;
180
- const tooLong = (bytes) => new FsError(ErrorCode.TOO_LARGE, `File too large (single line ${bytes} > ${options.maxSize} bytes). Use a narrower range or head.`, filePath);
165
+ const tooLong = (bytes) => createTooLargeError(bytes, options.maxSize, filePath, {
166
+ label: 'single line',
167
+ suggestion: 'Use a narrower range or head.',
168
+ });
181
169
  try {
182
170
  for await (const chunk of stream) {
183
171
  const buffer = chunk;
@@ -254,8 +242,14 @@ async function readRangeContent(handle, startLine, endLine, options, filePath) {
254
242
  hasMoreLines = true;
255
243
  break;
256
244
  }
245
+ // join('\n') puts a separator only between lines, so the first costs none.
246
+ const lineBytes = Buffer.byteLength(line, 'utf-8') + (lines.length > 0 ? newlineBytes : 0);
247
+ if (lines.length > 0 && estimatedBytes + lineBytes > options.maxSize) {
248
+ hasMoreLines = true;
249
+ break;
250
+ }
257
251
  lines.push(line);
258
- estimatedBytes += Buffer.byteLength(line, 'utf-8') + newlineBytes;
252
+ estimatedBytes += lineBytes;
259
253
  if (estimatedBytes > options.maxSize) {
260
254
  hasMoreLines = await peekHasMore(iterator);
261
255
  break;
@@ -339,7 +333,10 @@ async function readTailContent(handle, tail, options, filePath) {
339
333
  lines = lines.slice(1);
340
334
  }
341
335
  if (stoppedByLimit && lines.length < tail) {
342
- throw new FsError(ErrorCode.TOO_LARGE, `File too large (${totalLen} > ${options.maxSize} bytes, could not collect ${tail} lines). Use a narrower tail or head.`, filePath);
336
+ throw createTooLargeError(totalLen, options.maxSize, filePath, {
337
+ label: `could not collect ${tail} lines`,
338
+ suggestion: 'Use a narrower tail or head.',
339
+ });
343
340
  }
344
341
  let hasMoreLines = stoppedByLimit || position > 0;
345
342
  if (lines.length > tail) {
@@ -363,10 +360,17 @@ async function readFullContent(handle, maxSize, requestedPath, signal, strictUtf
363
360
  const content = buffer.toString('utf-8');
364
361
  return { content, totalLines: countLines(content) };
365
362
  }
363
+ /** The text path's binary refusal; `read` matches it to offer `binary: true`. */
364
+ export const BINARY_FILE_MESSAGE = 'Binary file detected.';
365
+ export function isBinaryRefusal(error) {
366
+ return (error instanceof FsError &&
367
+ error.code === ErrorCode.INVALID_INPUT &&
368
+ error.message === BINARY_FILE_MESSAGE);
369
+ }
366
370
  async function assertNotBinary(validPath, filePath, handle, normalized) {
367
371
  normalized.signal?.throwIfAborted();
368
372
  if (isKnownBinaryExtension(validPath)) {
369
- throw new FsError(ErrorCode.INVALID_INPUT, 'Binary file detected.', filePath);
373
+ throw new FsError(ErrorCode.INVALID_INPUT, BINARY_FILE_MESSAGE, filePath);
370
374
  }
371
375
  const probe = await readProbe(handle, normalized.signal);
372
376
  // A UTF-16 BOM passes the binary probe (it is text), but every reader
@@ -376,23 +380,23 @@ async function assertNotBinary(validPath, filePath, handle, normalized) {
376
380
  throw new FsError(ErrorCode.INVALID_INPUT, 'UTF-16 text file detected; convert it to UTF-8 first.', filePath);
377
381
  }
378
382
  if (isBinarySample(probe)) {
379
- throw new FsError(ErrorCode.INVALID_INPUT, 'Binary file detected.', filePath);
383
+ throw new FsError(ErrorCode.INVALID_INPUT, BINARY_FILE_MESSAGE, filePath);
380
384
  }
381
385
  }
382
- function assertSizeWithinLimit(size, maxSize, filePath) {
383
- if (size <= maxSize)
384
- return;
385
- throw new FsError(ErrorCode.TOO_LARGE, `File too large (${size} > ${maxSize} bytes). Use head to preview.`, filePath);
386
- }
387
386
  async function readByMode(handle, validPath, filePath, stats, spec, options) {
388
387
  switch (spec.kind) {
389
388
  case 'full': {
390
- assertSizeWithinLimit(stats.size, options.maxSize, filePath);
389
+ if (stats.size > options.maxSize) {
390
+ throw createTooLargeError(stats.size, options.maxSize, filePath, {
391
+ suggestion: 'Use head to preview.',
392
+ });
393
+ }
391
394
  const { content, totalLines } = await readFullContent(handle, options.maxSize, filePath, options.signal, spec.strictUtf8 === true);
392
395
  return {
393
396
  path: validPath,
394
397
  readMode: 'full',
395
398
  modified: stats.mtime.toISOString(),
399
+ fileSize: stats.size,
396
400
  content,
397
401
  totalLines,
398
402
  linesRead: totalLines,
@@ -404,6 +408,7 @@ async function readByMode(handle, validPath, filePath, stats, spec, options) {
404
408
  path: validPath,
405
409
  readMode: 'head',
406
410
  modified: stats.mtime.toISOString(),
411
+ fileSize: stats.size,
407
412
  head: spec.lines,
408
413
  ...(await readRangeContent(handle, 1, spec.lines, options, filePath)),
409
414
  };
@@ -412,6 +417,7 @@ async function readByMode(handle, validPath, filePath, stats, spec, options) {
412
417
  path: validPath,
413
418
  readMode: 'range',
414
419
  modified: stats.mtime.toISOString(),
420
+ fileSize: stats.size,
415
421
  startLine: spec.start,
416
422
  ...(spec.end !== undefined ? { endLine: spec.end } : {}),
417
423
  ...(await readRangeContent(handle, spec.start, spec.end, options, filePath)),
@@ -421,6 +427,7 @@ async function readByMode(handle, validPath, filePath, stats, spec, options) {
421
427
  path: validPath,
422
428
  readMode: 'tail',
423
429
  modified: stats.mtime.toISOString(),
430
+ fileSize: stats.size,
424
431
  tail: spec.lines,
425
432
  ...(await readTailContent(handle, spec.lines, options, validPath)),
426
433
  };
@@ -431,8 +438,8 @@ export function assertFileStats(filePath, stats) {
431
438
  throw new FsError(ErrorCode.NOT_FILE, 'Not a regular file', filePath);
432
439
  }
433
440
  }
434
- /** Read an already-validated, already-stat'd file per `spec`. */
435
- export async function readFileWithStats(filePath, validPath, stats, spec, verifyHandle) {
441
+ /** Read an already-validated, already-stat'd file per `spec`, opened via `openVerified`. */
442
+ export async function readFileWithStats(filePath, validPath, stats, spec, openVerified) {
436
443
  const env_1 = { stack: [], error: void 0, hasError: false };
437
444
  try {
438
445
  const options = {
@@ -441,8 +448,7 @@ export async function readFileWithStats(filePath, validPath, stats, spec, verify
441
448
  };
442
449
  spec.signal?.throwIfAborted();
443
450
  assertFileStats(filePath, stats);
444
- const handle = __addDisposableResource(env_1, await openReadableFileHandle(validPath, spec.signal), true);
445
- await verifyHandle(handle);
451
+ const handle = __addDisposableResource(env_1, await openVerified(), true);
446
452
  await assertNotBinary(validPath, filePath, handle, options);
447
453
  spec.signal?.throwIfAborted();
448
454
  return await readByMode(handle, validPath, filePath, stats, spec, options);