mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-09-29 13:41:45 +02:00
fix: restore public file streaming and contain ZIP failures
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
const { expect } = require("chai");
|
||||
const { Readable, PassThrough } = require("stream");
|
||||
const { setImmediate } = require("timers");
|
||||
const { once } = require("events");
|
||||
const vm = require("vm");
|
||||
const got = require("got");
|
||||
const archiver = require("archiver");
|
||||
require("ts-node/register/transpile-only");
|
||||
const GitHubDownload = require("../src/core/source/GitHubDownload").default;
|
||||
const { AnonymizeTransformer } = require("../src/core/anonymize-utils");
|
||||
const { streamAnonymizedZip } = require("../src/core/zipStream");
|
||||
|
||||
async function fixture() {
|
||||
const zip = archiver("zip");
|
||||
const chunks = [];
|
||||
zip.on("data", chunk => chunks.push(chunk));
|
||||
const finished = once(zip, "end");
|
||||
zip.append("private source content", { name: "repo/README.md" });
|
||||
await zip.finalize();
|
||||
await finished;
|
||||
return Buffer.concat(chunks);
|
||||
}
|
||||
|
||||
describe("ZIP stream errors", function () {
|
||||
it("finishes a valid ZIP with anonymized file content", async function () {
|
||||
const input = await fixture();
|
||||
const previous = { stream: got.stream, zip: GitHubDownload.prototype.getZipUrl };
|
||||
const response = new PassThrough();
|
||||
const parser = require("unzip-stream").Parse();
|
||||
const files = [];
|
||||
const entries = [];
|
||||
parser.on("entry", entry => {
|
||||
const chunks = [];
|
||||
entry.on("data", chunk => chunks.push(chunk));
|
||||
entries.push(once(entry, "end").then(() => files.push({ name: entry.path, content: Buffer.concat(chunks).toString() })));
|
||||
});
|
||||
const finished = once(parser, "finish");
|
||||
response.pipe(parser);
|
||||
try {
|
||||
GitHubDownload.prototype.getZipUrl = async () => ({ url: "https://example.test/archive.zip" });
|
||||
got.stream = () => Readable.from([input]);
|
||||
await streamAnonymizedZip({
|
||||
repoId: "test", organization: "owner", repoName: "public", commit: "abc",
|
||||
getToken: () => "", anonymizerOptions: { terms: ["private"], image: true, link: true },
|
||||
}, response);
|
||||
await finished;
|
||||
await Promise.all(entries);
|
||||
expect(files).to.deep.equal([{ name: "README.md", content: "XXXX-1 source content" }]);
|
||||
} finally {
|
||||
got.stream = previous.stream;
|
||||
GitHubDownload.prototype.getZipUrl = previous.zip;
|
||||
response.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
it("aborts a download on an asynchronous anonymization timeout without crashing", async function () {
|
||||
const input = await fixture();
|
||||
const previous = { stream: got.stream, zip: GitHubDownload.prototype.getZipUrl, flush: AnonymizeTransformer.prototype._flush };
|
||||
const response = new PassThrough();
|
||||
const errors = [];
|
||||
const chunks = [];
|
||||
response.on("error", error => errors.push(error));
|
||||
response.on("data", chunk => chunks.push(chunk));
|
||||
const closed = new Promise(resolve => response.once("close", resolve));
|
||||
const timeout = vm.runInNewContext('Object.assign(new Error("Script execution timed out after 1000ms"), { code: "ERR_SCRIPT_EXECUTION_TIMEOUT" })');
|
||||
try {
|
||||
GitHubDownload.prototype.getZipUrl = async () => ({ url: "https://example.test/archive.zip" });
|
||||
got.stream = () => Readable.from([input]);
|
||||
// VM timeout errors come from another realm and can arrive after the ZIP
|
||||
// parser finishes, while archiver is still consuming the entry stream.
|
||||
AnonymizeTransformer.prototype._flush = function (callback) {
|
||||
setImmediate(() => callback(timeout));
|
||||
};
|
||||
await streamAnonymizedZip({
|
||||
repoId: "test", organization: "owner", repoName: "public", commit: "abc",
|
||||
getToken: () => "", anonymizerOptions: { terms: ["private"], image: true, link: true },
|
||||
}, response);
|
||||
await closed;
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
expect(response.destroyed).to.equal(true);
|
||||
expect(errors).to.have.length(1);
|
||||
expect(errors[0].code).to.equal("ERR_SCRIPT_EXECUTION_TIMEOUT");
|
||||
expect(Buffer.concat(chunks).includes(Buffer.from("private source content"))).to.equal(false);
|
||||
} finally {
|
||||
got.stream = previous.stream;
|
||||
GitHubDownload.prototype.getZipUrl = previous.zip;
|
||||
AnonymizeTransformer.prototype._flush = previous.flush;
|
||||
response.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user