UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

98 lines (97 loc) 5.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NATIVE_CACHE_ROOT = void 0; exports.getNativeFileCacheLocationToDelete = getNativeFileCacheLocationToDelete; exports.ensureSecureNativeFileCacheLocation = ensureSecureNativeFileCacheLocation; const path_1 = require("path"); const fs_1 = require("fs"); const nx_tmp_dir_1 = require("../utils/nx-tmp-dir"); const versions_1 = require("../utils/versions"); const owned_private_dir_1 = require("../utils/owned-private-dir"); /** * Path of the current user's native binary cache. Nothing about the constant is * owner-only — `ensureSecureNativeFileCacheLocation` is what establishes that, * and on Windows nothing does, since the OS temp dir is already per-account. */ exports.NATIVE_CACHE_ROOT = (0, path_1.join)(nx_tmp_dir_1.NX_USER_TMP_DIR, 'native-cache'); /** * The configured cache dir, normalized. `resolve` strips a trailing slash, which * would otherwise defeat the guards downstream: `lstat` on a path ending in `/` * resolves a symlink rather than reporting it, and `O_NOFOLLOW` then opens the * target. `NX_SOCKET_DIR` is normalized for the same reason. */ function configuredCacheDir() { const dir = process.env.NX_NATIVE_FILE_CACHE_DIRECTORY; return dir ? (0, path_1.resolve)(dir) : undefined; } function getNativeFileCacheLocationToDelete() { const configured = configuredCacheDir(); if (configured !== undefined) { // Checked for ownership before it is handed to a recursive delete: being // configured does not make it ours. Weaker than the load path below, which // also enforces mode and validates the version directory. return (0, owned_private_dir_1.isOwnedRealDirectory)(configured) ? configured : null; } return (0, owned_private_dir_1.isSafeSharedRoot)(nx_tmp_dir_1.NX_TMP_DIR).status === 'ok' && (0, owned_private_dir_1.isOwnedRealDirectory)(nx_tmp_dir_1.NX_USER_TMP_DIR) && (0, owned_private_dir_1.isOwnedRealDirectory)(exports.NATIVE_CACHE_ROOT) ? (0, path_1.join)(exports.NATIVE_CACHE_ROOT, versions_1.nxVersion) : null; } /** * Create the native file cache dir, or return `null` if it cannot be created * *securely* — in which case the caller loads the binding in place. * * The stable top-level container is verified as safe for private children. The * uid directory and every directory loaded through are owner-only. * `ensureOwnedPrivateDir` refuses a directory or symlink another local user * planted before us. */ function ensureSecureNativeFileCacheLocation( // Test seam: lets a spec plant a hostile directory under a root it controls. cacheRoot = exports.NATIVE_CACHE_ROOT) { const configured = configuredCacheDir(); if (configured !== undefined) { const dir = configured; try { // Held to the same 0700 bar as the default location: a `.node` is loaded // out of here. Unlike NX_SOCKET_DIR it is not additionally refused for // naming one of Nx's own roots, which is the only case that throws. (0, fs_1.mkdirSync)((0, path_1.dirname)(dir), { recursive: true }); const established = (0, owned_private_dir_1.ensureOwnedPrivateDir)(dir); if (established.status !== 'ok') { // The guard's own reason rather than a fixed sentence: `not-created // (EACCES)` and `foreign-owner` are exactly what the catch below // promises to tell apart, and one string for all of them cannot. throw new owned_private_dir_1.DirectoryRefusedError(established.refusal); } return dir; } catch (e) { // Never discard a configured directory silently — the socket directory // follows the same rule. A typo, EACCES or EROFS is otherwise // indistinguishable from a working cache. console.warn(`Nx could not use the configured native file cache directory ${dir} (${e?.code ?? e?.message ?? e}). Loading the native binding in place instead.`); return null; } } const userRoot = (0, path_1.dirname)(cacheRoot); const sharedRoot = (0, path_1.dirname)(userRoot); // Outermost first: the stable shared container must either belong to root or // to us, and must be sticky if peers can write there. The uid directory then // becomes the owner-only boundary for sockets and native-cache alike. if ((0, owned_private_dir_1.ensureSafeSharedRoot)(sharedRoot).status !== 'ok') { return null; } for (const root of [userRoot, cacheRoot]) { if ((0, owned_private_dir_1.ensureOwnedPrivateDir)(root).status !== 'ok') { return null; } } // Verified, not assumed: this is the directory we load a `.node` out of. const versionDir = (0, path_1.join)(cacheRoot, versions_1.nxVersion); if ((0, owned_private_dir_1.ensureOwnedPrivateDir)(versionDir).status !== 'ok') { return null; } return versionDir; }