mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-09-29 21:51:44 +02:00
97 lines
4.3 KiB
JavaScript
97 lines
4.3 KiB
JavaScript
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 () {
|
|
for (const commit of ["abc", undefined, ""]) {
|
|
it(`downloads a public ZIP without a REST lookup with commit ${JSON.stringify(commit)}`, 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 () => { throw new Error("must not use the anonymous REST quota"); };
|
|
got.stream = url => {
|
|
expect(url).to.equal(`https://codeload.github.com/owner/public/zip/${commit || "HEAD"}`);
|
|
return Readable.from([input]);
|
|
};
|
|
await streamAnonymizedZip({
|
|
repoId: "test", organization: "owner", repoName: "public", commit,
|
|
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: () => "private-token", 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();
|
|
}
|
|
});
|
|
});
|