/** * System Directories Configuration * Issue #135: DB path resolution logic fix * Issue #1285: Path boundary matching and symlink resolution * * Centralized list of system directories that are not allowed for DB storage. * This supports security measures SEC-001, SEC-002, SEC-005. * * @module system-directories */ /** * System directories that are not allowed for DB storage * * SEC-001: System directory protection * These directories are protected to prevent writing database files * to critical system paths which could cause security issues. */ export declare const SYSTEM_DIRECTORIES: readonly ["/etc", "/usr", "/bin", "/sbin", "/var", "/tmp", "/dev", "/sys", "/proc"]; /** * The subset of {@link SYSTEM_DIRECTORIES} that is not a filesystem at all. * * Issue #1774: these three are kernel-backed namespaces, and their `mkdir` * error codes are not the ones `fs` callers assume. procfs answers a `mkdir` * for a child that cannot exist with **ENOENT rather than EPERM**, and Node's * recursive mkdir reads ENOENT as "the parent is missing", so it creates the * parent and retries — forever. Measured in a container: `mkdirSync('/proc/x/y', * {recursive:true})` had not returned after 25s at 100.4% CPU; the promise from * `fs.promises.mkdir` never settled and held a libuv threadpool thread (4 by * default) for the life of the process. Neither logs anything, and the * synchronous form stops the event loop, so a surrounding `try/catch` is never * reached. On macOS the paths do not exist and the call throws at once, which * is why this is invisible outside Linux and containers. * * Kept as a named subset rather than reusing the whole list because the two * lists answer different questions. {@link isSystemDirectory} answers "may a * database live here", and rejects `/tmp` and `/var` for that; but `/tmp` and * `/var/log` are ordinary writable directories that a log or hook directory may * legitimately be pointed at — `os.tmpdir()` is *inside* one on both platforms * (`/tmp` on Linux, `/var/folders/…` on macOS), which is where every isolated * test directory in this repository lives. Rejecting those would not prevent a * hang; it would redirect isolated test runs and container deployments back * onto the real `~/.codex` and `~/.commandmate`. * * `SYSTEM_DIRECTORIES` remains the superset — `tests/unit/config/system-directories.test.ts` * pins that, so the two cannot drift apart. */ export declare const VIRTUAL_FILESYSTEM_ROOTS: readonly ["/proc", "/sys", "/dev"]; /** * Check whether `target` is `dir` itself or lives underneath it. * * Issue #1285: A bare startsWith() has no path boundary, so '/tmp' matched * '/tmpfoo' and '/var' matched '/variance'. Requiring an exact match or a * separator after the prefix keeps unrelated siblings out of the match. * * @param target - Absolute path to test * @param dir - Absolute directory path to test against * @returns true if target is dir or is contained in dir */ export declare function isPathWithin(target: string, dir: string): boolean; /** * Check if a path is within a system directory * * Issue #1285: Resolution happens here rather than at each call site. The check * is only correct when the candidate path *and* the system directory list are * resolved consistently; resolving at a call site and comparing against the * unresolved literals silently defeats the guard (that is what made the SEC-002 * call site in db-migration-path.ts miss '/tmp'). Centralizing keeps every * caller correct by construction. * * The literal and physical forms are both matched, so a path is rejected if * either form lands in a system directory. This fails closed: a symlink placed * inside a system directory that points elsewhere is still rejected. * * This performs filesystem I/O and is therefore not a pure function. * * @param inputPath - The absolute path to check (relative paths are resolved against cwd) * @returns true if the path is within a system directory */ export declare function isSystemDirectory(inputPath: string): boolean; /** * Check if a path is inside a virtual filesystem ({@link VIRTUAL_FILESYSTEM_ROOTS}). * * Issue #1774: the question `isSystemDirectory` cannot answer for a directory * that is about to be created. A recursive `mkdir` under one of these roots does * not fail — it hangs the process, unrecoverably and without a log line — so * every path that reaches a `mkdir(…, {recursive:true})` has to be filtered * through this first. See {@link VIRTUAL_FILESYSTEM_ROOTS} for the measurements * and for why this is a subset of `SYSTEM_DIRECTORIES` rather than all of it. * * Matching is the same as `isSystemDirectory`: lexical *and* physical form, so a * symlink that points into `/proc` is rejected along with a literal `/proc/…`. * That is the direction that matters — a missed one hangs the server. * * Reads the filesystem (symlink resolution); it never creates anything, and * `realpath` on a procfs path returns ENOENT immediately rather than spinning. * * @param inputPath - The path to check (relative paths are resolved against cwd) * @returns true if a recursive mkdir for this path could spin */ export declare function isVirtualFilesystemPath(inputPath: string): boolean; //# sourceMappingURL=system-directories.d.ts.map