Files

71 lines
1.9 KiB
JavaScript

import assert from "node:assert/strict";
import path from "node:path";
import { rollup } from "rollup";
import typescript from "@rollup/plugin-typescript";
const root = path.resolve(import.meta.dirname, "..");
const bundle = await rollup({
input: path.join(root, "src/vfs/path.ts"),
plugins: [
typescript({
tsconfig: path.join(root, "tsconfig.worker.json"),
declaration: false,
}),
],
});
const { output } = await bundle.generate({ format: "es" });
await bundle.close();
const paths = await import(
`data:text/javascript;base64,${Buffer.from(output[0].code).toString("base64")}`
);
const targets = [
[String.prototype, "startsWith"],
[String.prototype, "endsWith"],
[String.prototype, "split"],
[String.prototype, "slice"],
[String.prototype, "charCodeAt"],
[String.prototype, "lastIndexOf"],
[Array.prototype, "push"],
[Array.prototype, "pop"],
[Array.prototype, "filter"],
[Array.prototype, "join"],
[JSON, "stringify"],
];
const originals = targets.map(([object, name]) => [object, name, object[name]]);
const intercepted = () => {
throw new Error("path operation intercepted");
};
let result;
try {
for (const [object, name] of targets) object[name] = intercepted;
result = {
normalized: paths.normalize("/tmp/../../etc/../safe/"),
inside: paths.under("/safe", "/safe/file"),
outside: paths.under("/safe", "/safe-evil/file"),
local: paths.toLocal("/safe", "/safe/file"),
relative: paths.relative("/safe/a", "/safe/b/file"),
invalid: (() => {
try {
paths.checkRoot("/safe/../escape");
return false;
} catch {
return true;
}
})(),
};
} finally {
for (const [object, name, original] of originals) object[name] = original;
}
assert.deepEqual(result, {
normalized: "/safe/",
inside: true,
outside: false,
local: "/file",
relative: "../b/file",
invalid: true,
});
console.log(
"mount containment survives string and array prototype replacement"
);