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");
|
||||
});
|
||||
|
||||
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", () => {
|
||||
const volumePath = "/var/lib/zerobyte/volumes/vol123/_data";
|
||||
const schedule = createSchedule({
|
||||
|
|
|
|||
|
|
@ -23,12 +23,26 @@ export const processPattern = (pattern: string, volumePath: string, relative = f
|
|||
const isNegated = pattern.startsWith("!");
|
||||
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 (!relative) return pattern;
|
||||
ensurePatternIsWithinVolume(p);
|
||||
const processed = path.join(volumePath, p);
|
||||
return isNegated ? `!${processed}` : processed;
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
ensurePatternIsWithinVolume(p.slice(1));
|
||||
}
|
||||
const processed = path.join(volumePath, p.slice(1));
|
||||
return isNegated ? `!${processed}` : processed;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue