reporting-governance: harden artifact root boundary checks
This commit is contained in:
@@ -24,7 +24,36 @@ function assertObjectRecord(value, label) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function assertRelativePathWithinRoot(relativePath, label, { root }) {
|
||||
function assertPathWithinRealRoot(candidatePath, label, { root, allowMissingLeaf = false }) {
|
||||
const realRoot = fs.realpathSync(root);
|
||||
let realCandidate;
|
||||
|
||||
if (allowMissingLeaf && !fs.existsSync(candidatePath)) {
|
||||
let cursor = path.dirname(candidatePath);
|
||||
while (cursor !== root && !fs.existsSync(cursor)) {
|
||||
cursor = path.dirname(cursor);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(cursor)) {
|
||||
realCandidate = realRoot;
|
||||
} else {
|
||||
realCandidate = path.resolve(fs.realpathSync(cursor), path.relative(cursor, candidatePath));
|
||||
}
|
||||
} else {
|
||||
realCandidate = fs.realpathSync(candidatePath);
|
||||
}
|
||||
|
||||
const relativeToRealRoot = path.relative(realRoot, realCandidate);
|
||||
if (
|
||||
relativeToRealRoot === '..'
|
||||
|| relativeToRealRoot.startsWith(`..${path.sep}`)
|
||||
|| path.isAbsolute(relativeToRealRoot)
|
||||
) {
|
||||
throw new Error(`${label} must stay within repo root: symlink resolution escapes realpath boundary`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertRelativePathWithinRoot(relativePath, label, { root, allowMissingLeaf = false }) {
|
||||
const normalizedPath = assertNonEmptyString(relativePath, label);
|
||||
if (path.isAbsolute(normalizedPath)) {
|
||||
throw new Error(`${label} must stay within repo root: absolute paths are not allowed`);
|
||||
@@ -40,6 +69,7 @@ function assertRelativePathWithinRoot(relativePath, label, { root }) {
|
||||
throw new Error(`${label} must stay within repo root: path escapes root boundary`);
|
||||
}
|
||||
|
||||
assertPathWithinRealRoot(resolvedPath, label, { root, allowMissingLeaf });
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
@@ -70,7 +100,7 @@ export function validateDeploymentProfileArtifact(artifact, { repoRootOverride }
|
||||
assertRelativePathWithinRoot(relativePath, `deployment profile artifact spec.bindings.scripts.${key}`, { root });
|
||||
}
|
||||
for (const [key, relativePath] of Object.entries(artifactRoots)) {
|
||||
assertRelativePathWithinRoot(relativePath, `deployment profile artifact spec.bindings.artifact_roots.${key}`, { root });
|
||||
assertRelativePathWithinRoot(relativePath, `deployment profile artifact spec.bindings.artifact_roots.${key}`, { root, allowMissingLeaf: true });
|
||||
}
|
||||
|
||||
return artifact;
|
||||
|
||||
Reference in New Issue
Block a user