fix: reject include patterns that are not in the volume root
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
This commit is contained in:
parent
469f0d6c4e
commit
66f95dcd8e
2 changed files with 29 additions and 0 deletions
|
|
@ -100,6 +100,21 @@ describe("executeBackup - include / exclude patterns", () => {
|
||||||
expect(processPattern("!*.log", "/volume")).toBe("!*.log");
|
expect(processPattern("!*.log", "/volume")).toBe("!*.log");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("rejects include patterns that escape the volume root", () => {
|
||||||
|
const volumePath = "/var/lib/zerobyte/volumes/vol123/_data";
|
||||||
|
const signal = new AbortController().signal;
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
createBackupOptions(
|
||||||
|
createSchedule({
|
||||||
|
includePatterns: ["../../../../etc/shadow", "/../etc/passwd", "!/../../secrets.txt"],
|
||||||
|
}),
|
||||||
|
volumePath,
|
||||||
|
signal,
|
||||||
|
),
|
||||||
|
).toThrow("Include pattern escapes volume root");
|
||||||
|
});
|
||||||
|
|
||||||
test("anchors relative glob include patterns to the volume path", () => {
|
test("anchors relative glob include patterns to the volume path", () => {
|
||||||
const volumePath = "/var/lib/zerobyte/volumes/vol123/_data";
|
const volumePath = "/var/lib/zerobyte/volumes/vol123/_data";
|
||||||
const schedule = createSchedule({
|
const schedule = createSchedule({
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,26 @@ export const processPattern = (pattern: string, volumePath: string, relative = f
|
||||||
const isNegated = pattern.startsWith("!");
|
const isNegated = pattern.startsWith("!");
|
||||||
const p = isNegated ? pattern.slice(1) : pattern;
|
const p = isNegated ? pattern.slice(1) : pattern;
|
||||||
|
|
||||||
|
const ensurePatternIsWithinVolume = (candidate: string) => {
|
||||||
|
const resolvedVolumePath = path.resolve(volumePath);
|
||||||
|
const resolvedCandidatePath = path.resolve(volumePath, candidate);
|
||||||
|
const relativePath = path.relative(resolvedVolumePath, resolvedCandidatePath);
|
||||||
|
|
||||||
|
if (relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) {
|
||||||
|
throw new Error(`Include pattern escapes volume root: ${pattern}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!p.startsWith("/")) {
|
if (!p.startsWith("/")) {
|
||||||
if (!relative) return pattern;
|
if (!relative) return pattern;
|
||||||
|
ensurePatternIsWithinVolume(p);
|
||||||
const processed = path.join(volumePath, p);
|
const processed = path.join(volumePath, p);
|
||||||
return isNegated ? `!${processed}` : processed;
|
return isNegated ? `!${processed}` : processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (relative) {
|
||||||
|
ensurePatternIsWithinVolume(p.slice(1));
|
||||||
|
}
|
||||||
const processed = path.join(volumePath, p.slice(1));
|
const processed = path.join(volumePath, p.slice(1));
|
||||||
return isNegated ? `!${processed}` : processed;
|
return isNegated ? `!${processed}` : processed;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue