projen
Version:
CDK for software projects
109 lines • 12.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureRelativePathStartsWithDot = ensureRelativePathStartsWithDot;
exports.isHiddenPath = isHiddenPath;
exports.ensureNotHiddenPath = ensureNotHiddenPath;
exports.isPathInside = isPathInside;
exports.realpathOfClosestExisting = realpathOfClosestExisting;
exports.ensurePathInsideProject = ensurePathInsideProject;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
/**
* Helper function to format a path as dot notation regardless of how it
* was handed in.
*
* @param path - can be formatted as "path", "./path" (but not "/path", as it is absolute)
* @returns "./path"
*/
function ensureRelativePathStartsWithDot(path) {
if (path.startsWith(".")) {
return path;
}
if (path.startsWith("/")) {
throw new Error(`Path ${path} must be relative`);
}
return `./${path}`;
}
/**
* Checks if part of the file path is hidden
*/
function isHiddenPath(path) {
return /(^|\/)\.[^\/\.]/g.test(path);
}
/**
* Helper to assert a path is not hidden
*/
function ensureNotHiddenPath(value, name) {
if (isHiddenPath(value)) {
throw Error(`${name} cannot be a hidden path, got: ${value}`);
}
}
/**
* Checks if `file` is strictly inside `dir` (the directory itself does not count).
*
* Purely lexical: does not consult the filesystem, so symlinks are not
* resolved. Relative paths are resolved against the current working directory.
*/
function isPathInside(dir, file) {
const rel = (0, node_path_1.relative)(dir, file);
return (rel !== "" &&
!rel.startsWith(`..${node_path_1.sep}`) &&
rel !== ".." &&
!(0, node_path_1.isAbsolute)(rel));
}
/**
* Resolves symlinks in `p`, falling back to the closest existing ancestor for
* paths that do not exist (yet).
*/
function realpathOfClosestExisting(p) {
const missing = new Array();
let current = (0, node_path_1.resolve)(p);
while (true) {
try {
return (0, node_path_1.join)((0, node_fs_1.realpathSync)(current), ...missing);
}
catch {
const parent = (0, node_path_1.dirname)(current);
if (parent === current) {
return (0, node_path_1.resolve)(p);
}
missing.unshift((0, node_path_1.basename)(current));
current = parent;
}
}
}
/**
* Asserts that a path stays strictly inside a project directory.
*
* Use this before passing a user-configurable path to a destructive command
* (e.g. `rm -fr`): an absolute path or a path traversing outside of the
* project directory (via `..` or a symlink) could otherwise delete files
* anywhere on the machine. The project directory itself (`.`) is also
* rejected.
*
* The check resolves both paths on the filesystem (symlinks in existing path
* segments are followed). The last segment of the path is intentionally kept
* as-is: a symlink inside the project only affects the link itself.
*
* @param value the path to check, relative to the project directory
* @param name name of the option, used in the error message
* @param projectDir the project directory the path must stay inside of
* @throws if the path is absolute or does not stay inside the project directory
*/
function ensurePathInsideProject(value, name, projectDir) {
if ((0, node_path_1.isAbsolute)(value)) {
throw new Error(`${name} must be a relative path within the project directory, got absolute path: ${value}`);
}
const baseDir = realpathOfClosestExisting(projectDir);
const resolved = (0, node_path_1.resolve)(baseDir, value);
if (!isPathInside(baseDir, resolved)) {
throw new Error(`${name} must be inside the project directory, got: ${value}`);
}
// the path might traverse outside of the project through a symlinked
// directory, which a lexical `resolve` cannot detect
const realParent = realpathOfClosestExisting((0, node_path_1.dirname)(resolved));
if (!isPathInside(baseDir, (0, node_path_1.join)(realParent, (0, node_path_1.basename)(resolved)))) {
throw new Error(`${name} must be inside the project directory, but points outside of it through a symlink, got: ${value}`);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0aC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL3BhdGgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFrQkEsMEVBVUM7QUFLRCxvQ0FFQztBQUtELGtEQUlDO0FBUUQsb0NBUUM7QUFNRCw4REFnQkM7QUFvQkQsMERBNEJDO0FBbElELHFDQUF1QztBQUN2Qyx5Q0FRbUI7QUFFbkI7Ozs7OztHQU1HO0FBQ0gsU0FBZ0IsK0JBQStCLENBQUMsSUFBWTtJQUMxRCxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN6QixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN6QixNQUFNLElBQUksS0FBSyxDQUFDLFFBQVEsSUFBSSxtQkFBbUIsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFRCxPQUFPLEtBQUssSUFBSSxFQUFFLENBQUM7QUFDckIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsWUFBWSxDQUFDLElBQVk7SUFDdkMsT0FBTyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDdkMsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsbUJBQW1CLENBQUMsS0FBYSxFQUFFLElBQVk7SUFDN0QsSUFBSSxZQUFZLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLEtBQUssQ0FBQyxHQUFHLElBQUksa0NBQWtDLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDaEUsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQWdCLFlBQVksQ0FBQyxHQUFXLEVBQUUsSUFBWTtJQUNwRCxNQUFNLEdBQUcsR0FBRyxJQUFBLG9CQUFRLEVBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ2hDLE9BQU8sQ0FDTCxHQUFHLEtBQUssRUFBRTtRQUNWLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLGVBQUcsRUFBRSxDQUFDO1FBQzNCLEdBQUcsS0FBSyxJQUFJO1FBQ1osQ0FBQyxJQUFBLHNCQUFVLEVBQUMsR0FBRyxDQUFDLENBQ2pCLENBQUM7QUFDSixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBZ0IseUJBQXlCLENBQUMsQ0FBUztJQUNqRCxNQUFNLE9BQU8sR0FBRyxJQUFJLEtBQUssRUFBVSxDQUFDO0lBQ3BDLElBQUksT0FBTyxHQUFHLElBQUEsbUJBQU8sRUFBQyxDQUFDLENBQUMsQ0FBQztJQUV6QixPQUFPLElBQUksRUFBRSxDQUFDO1FBQ1osSUFBSSxDQUFDO1lBQ0gsT0FBTyxJQUFBLGdCQUFJLEVBQUMsSUFBQSxzQkFBWSxFQUFDLE9BQU8sQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFDLENBQUM7UUFDakQsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE1BQU0sTUFBTSxHQUFHLElBQUEsbUJBQU8sRUFBQyxPQUFPLENBQUMsQ0FBQztZQUNoQyxJQUFJLE1BQU0sS0FBSyxPQUFPLEVBQUUsQ0FBQztnQkFDdkIsT0FBTyxJQUFBLG1CQUFPLEVBQUMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsQ0FBQztZQUNELE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBQSxvQkFBUSxFQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDbkMsT0FBTyxHQUFHLE1BQU0sQ0FBQztRQUNuQixDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFDSCxTQUFnQix1QkFBdUIsQ0FDckMsS0FBYSxFQUNiLElBQVksRUFDWixVQUFrQjtJQUVsQixJQUFJLElBQUEsc0JBQVUsRUFBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQ2IsR0FBRyxJQUFJLDZFQUE2RSxLQUFLLEVBQUUsQ0FDNUYsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRyx5QkFBeUIsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN0RCxNQUFNLFFBQVEsR0FBRyxJQUFBLG1CQUFPLEVBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBRXpDLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDckMsTUFBTSxJQUFJLEtBQUssQ0FDYixHQUFHLElBQUksK0NBQStDLEtBQUssRUFBRSxDQUM5RCxDQUFDO0lBQ0osQ0FBQztJQUVELHFFQUFxRTtJQUNyRSxxREFBcUQ7SUFDckQsTUFBTSxVQUFVLEdBQUcseUJBQXlCLENBQUMsSUFBQSxtQkFBTyxFQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7SUFDaEUsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsSUFBQSxnQkFBSSxFQUFDLFVBQVUsRUFBRSxJQUFBLG9CQUFRLEVBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDakUsTUFBTSxJQUFJLEtBQUssQ0FDYixHQUFHLElBQUksMkZBQTJGLEtBQUssRUFBRSxDQUMxRyxDQUFDO0lBQ0osQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByZWFscGF0aFN5bmMgfSBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0IHtcbiAgYmFzZW5hbWUsXG4gIGRpcm5hbWUsXG4gIGlzQWJzb2x1dGUsXG4gIGpvaW4sXG4gIHJlbGF0aXZlLFxuICByZXNvbHZlLFxuICBzZXAsXG59IGZyb20gXCJub2RlOnBhdGhcIjtcblxuLyoqXG4gKiBIZWxwZXIgZnVuY3Rpb24gdG8gZm9ybWF0IGEgcGF0aCBhcyBkb3Qgbm90YXRpb24gcmVnYXJkbGVzcyBvZiBob3cgaXRcbiAqIHdhcyBoYW5kZWQgaW4uXG4gKlxuICogQHBhcmFtIHBhdGggLSBjYW4gYmUgZm9ybWF0dGVkIGFzIFwicGF0aFwiLCBcIi4vcGF0aFwiIChidXQgbm90IFwiL3BhdGhcIiwgYXMgaXQgaXMgYWJzb2x1dGUpXG4gKiBAcmV0dXJucyBcIi4vcGF0aFwiXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbnN1cmVSZWxhdGl2ZVBhdGhTdGFydHNXaXRoRG90KHBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmIChwYXRoLnN0YXJ0c1dpdGgoXCIuXCIpKSB7XG4gICAgcmV0dXJuIHBhdGg7XG4gIH1cblxuICBpZiAocGF0aC5zdGFydHNXaXRoKFwiL1wiKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgUGF0aCAke3BhdGh9IG11c3QgYmUgcmVsYXRpdmVgKTtcbiAgfVxuXG4gIHJldHVybiBgLi8ke3BhdGh9YDtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgcGFydCBvZiB0aGUgZmlsZSBwYXRoIGlzIGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNIaWRkZW5QYXRoKHBhdGg6IHN0cmluZykge1xuICByZXR1cm4gLyhefFxcLylcXC5bXlxcL1xcLl0vZy50ZXN0KHBhdGgpO1xufVxuXG4vKipcbiAqIEhlbHBlciB0byBhc3NlcnQgYSBwYXRoIGlzIG5vdCBoaWRkZW5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVuc3VyZU5vdEhpZGRlblBhdGgodmFsdWU6IHN0cmluZywgbmFtZTogc3RyaW5nKSB7XG4gIGlmIChpc0hpZGRlblBhdGgodmFsdWUpKSB7XG4gICAgdGhyb3cgRXJyb3IoYCR7bmFtZX0gY2Fubm90IGJlIGEgaGlkZGVuIHBhdGgsIGdvdDogJHt2YWx1ZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgZmlsZWAgaXMgc3RyaWN0bHkgaW5zaWRlIGBkaXJgICh0aGUgZGlyZWN0b3J5IGl0c2VsZiBkb2VzIG5vdCBjb3VudCkuXG4gKlxuICogUHVyZWx5IGxleGljYWw6IGRvZXMgbm90IGNvbnN1bHQgdGhlIGZpbGVzeXN0ZW0sIHNvIHN5bWxpbmtzIGFyZSBub3RcbiAqIHJlc29sdmVkLiBSZWxhdGl2ZSBwYXRocyBhcmUgcmVzb2x2ZWQgYWdhaW5zdCB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzUGF0aEluc2lkZShkaXI6IHN0cmluZywgZmlsZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IHJlbCA9IHJlbGF0aXZlKGRpciwgZmlsZSk7XG4gIHJldHVybiAoXG4gICAgcmVsICE9PSBcIlwiICYmXG4gICAgIXJlbC5zdGFydHNXaXRoKGAuLiR7c2VwfWApICYmXG4gICAgcmVsICE9PSBcIi4uXCIgJiZcbiAgICAhaXNBYnNvbHV0ZShyZWwpXG4gICk7XG59XG5cbi8qKlxuICogUmVzb2x2ZXMgc3ltbGlua3MgaW4gYHBgLCBmYWxsaW5nIGJhY2sgdG8gdGhlIGNsb3Nlc3QgZXhpc3RpbmcgYW5jZXN0b3IgZm9yXG4gKiBwYXRocyB0aGF0IGRvIG5vdCBleGlzdCAoeWV0KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlYWxwYXRoT2ZDbG9zZXN0RXhpc3RpbmcocDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgbWlzc2luZyA9IG5ldyBBcnJheTxzdHJpbmc+KCk7XG4gIGxldCBjdXJyZW50ID0gcmVzb2x2ZShwKTtcblxuICB3aGlsZSAodHJ1ZSkge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gam9pbihyZWFscGF0aFN5bmMoY3VycmVudCksIC4uLm1pc3NpbmcpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgY29uc3QgcGFyZW50ID0gZGlybmFtZShjdXJyZW50KTtcbiAgICAgIGlmIChwYXJlbnQgPT09IGN1cnJlbnQpIHtcbiAgICAgICAgcmV0dXJuIHJlc29sdmUocCk7XG4gICAgICB9XG4gICAgICBtaXNzaW5nLnVuc2hpZnQoYmFzZW5hbWUoY3VycmVudCkpO1xuICAgICAgY3VycmVudCA9IHBhcmVudDtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBBc3NlcnRzIHRoYXQgYSBwYXRoIHN0YXlzIHN0cmljdGx5IGluc2lkZSBhIHByb2plY3QgZGlyZWN0b3J5LlxuICpcbiAqIFVzZSB0aGlzIGJlZm9yZSBwYXNzaW5nIGEgdXNlci1jb25maWd1cmFibGUgcGF0aCB0byBhIGRlc3RydWN0aXZlIGNvbW1hbmRcbiAqIChlLmcuIGBybSAtZnJgKTogYW4gYWJzb2x1dGUgcGF0aCBvciBhIHBhdGggdHJhdmVyc2luZyBvdXRzaWRlIG9mIHRoZVxuICogcHJvamVjdCBkaXJlY3RvcnkgKHZpYSBgLi5gIG9yIGEgc3ltbGluaykgY291bGQgb3RoZXJ3aXNlIGRlbGV0ZSBmaWxlc1xuICogYW55d2hlcmUgb24gdGhlIG1hY2hpbmUuIFRoZSBwcm9qZWN0IGRpcmVjdG9yeSBpdHNlbGYgKGAuYCkgaXMgYWxzb1xuICogcmVqZWN0ZWQuXG4gKlxuICogVGhlIGNoZWNrIHJlc29sdmVzIGJvdGggcGF0aHMgb24gdGhlIGZpbGVzeXN0ZW0gKHN5bWxpbmtzIGluIGV4aXN0aW5nIHBhdGhcbiAqIHNlZ21lbnRzIGFyZSBmb2xsb3dlZCkuIFRoZSBsYXN0IHNlZ21lbnQgb2YgdGhlIHBhdGggaXMgaW50ZW50aW9uYWxseSBrZXB0XG4gKiBhcy1pczogYSBzeW1saW5rIGluc2lkZSB0aGUgcHJvamVjdCBvbmx5IGFmZmVjdHMgdGhlIGxpbmsgaXRzZWxmLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSB0aGUgcGF0aCB0byBjaGVjaywgcmVsYXRpdmUgdG8gdGhlIHByb2plY3QgZGlyZWN0b3J5XG4gKiBAcGFyYW0gbmFtZSBuYW1lIG9mIHRoZSBvcHRpb24sIHVzZWQgaW4gdGhlIGVycm9yIG1lc3NhZ2VcbiAqIEBwYXJhbSBwcm9qZWN0RGlyIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSB0aGUgcGF0aCBtdXN0IHN0YXkgaW5zaWRlIG9mXG4gKiBAdGhyb3dzIGlmIHRoZSBwYXRoIGlzIGFic29sdXRlIG9yIGRvZXMgbm90IHN0YXkgaW5zaWRlIHRoZSBwcm9qZWN0IGRpcmVjdG9yeVxuICovXG5leHBvcnQgZnVuY3Rpb24gZW5zdXJlUGF0aEluc2lkZVByb2plY3QoXG4gIHZhbHVlOiBzdHJpbmcsXG4gIG5hbWU6IHN0cmluZyxcbiAgcHJvamVjdERpcjogc3RyaW5nLFxuKSB7XG4gIGlmIChpc0Fic29sdXRlKHZhbHVlKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGAke25hbWV9IG11c3QgYmUgYSByZWxhdGl2ZSBwYXRoIHdpdGhpbiB0aGUgcHJvamVjdCBkaXJlY3RvcnksIGdvdCBhYnNvbHV0ZSBwYXRoOiAke3ZhbHVlfWAsXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IGJhc2VEaXIgPSByZWFscGF0aE9mQ2xvc2VzdEV4aXN0aW5nKHByb2plY3REaXIpO1xuICBjb25zdCByZXNvbHZlZCA9IHJlc29sdmUoYmFzZURpciwgdmFsdWUpO1xuXG4gIGlmICghaXNQYXRoSW5zaWRlKGJhc2VEaXIsIHJlc29sdmVkKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGAke25hbWV9IG11c3QgYmUgaW5zaWRlIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSwgZ290OiAke3ZhbHVlfWAsXG4gICAgKTtcbiAgfVxuXG4gIC8vIHRoZSBwYXRoIG1pZ2h0IHRyYXZlcnNlIG91dHNpZGUgb2YgdGhlIHByb2plY3QgdGhyb3VnaCBhIHN5bWxpbmtlZFxuICAvLyBkaXJlY3RvcnksIHdoaWNoIGEgbGV4aWNhbCBgcmVzb2x2ZWAgY2Fubm90IGRldGVjdFxuICBjb25zdCByZWFsUGFyZW50ID0gcmVhbHBhdGhPZkNsb3Nlc3RFeGlzdGluZyhkaXJuYW1lKHJlc29sdmVkKSk7XG4gIGlmICghaXNQYXRoSW5zaWRlKGJhc2VEaXIsIGpvaW4ocmVhbFBhcmVudCwgYmFzZW5hbWUocmVzb2x2ZWQpKSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgJHtuYW1lfSBtdXN0IGJlIGluc2lkZSB0aGUgcHJvamVjdCBkaXJlY3RvcnksIGJ1dCBwb2ludHMgb3V0c2lkZSBvZiBpdCB0aHJvdWdoIGEgc3ltbGluaywgZ290OiAke3ZhbHVlfWAsXG4gICAgKTtcbiAgfVxufVxuIl19