reporting-governance: harden artifact root boundary checks

This commit is contained in:
Eve
2026-05-08 10:29:16 +08:00
parent 4f816a93a5
commit 8c7aca145e
4 changed files with 125 additions and 36 deletions

View File

@@ -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;