fix: harden anonymization privacy paths (#754)

This commit is contained in:
Thomas Durieux
2026-07-22 04:28:01 +02:00
committed by GitHub
parent 5ef10dee9e
commit aab6ccf7fc
5 changed files with 262 additions and 50 deletions
+37
View File
@@ -2,6 +2,10 @@ const { expect } = require("chai");
const { Transform } = require("stream");
const { StringDecoder } = require("string_decoder");
require("ts-node/register/transpile-only");
const {
ContentAnonimizer: RealContentAnonimizer,
AnonymizeTransformer: RealAnonymizeTransformer,
} = require("../src/core/anonymize-utils");
const {
withWordBoundaries,
termVariants,
@@ -449,6 +453,20 @@ describe("ContentAnonimizer", function () {
expect(result).to.include("anonymous.4open.science/r/abc123");
});
it("escapes regex characters in repository and branch names", function () {
const anon = new RealContentAnonimizer({
repoName: "secret-owner/secret.repo",
branchName: "release+candidate",
repoId: "abc123",
});
const result = anon.anonymize(
"https://raw.githubusercontent.com/secret-owner/secret.repo/release+candidate/src/index.ts"
);
expect(result).to.equal(
"https://anonymous.4open.science/r/abc123/src/index.ts"
);
});
it("is case-insensitive for repo name", function () {
const anon = new ContentAnonimizer({
repoName: "Owner/Repo",
@@ -647,6 +665,25 @@ describe("AnonymizeTransformer (streaming)", function () {
expect(result).to.equal("Created by XXXX-1 at 2025/01/01");
});
it("does not leak matches longer than the normal streaming overlap", async function () {
const secret = "A".repeat(5000);
const transformer = new RealAnonymizeTransformer({
filePath: "fixture.txt",
terms: ["A{5000}"],
});
const chunks = [];
const done = new Promise((resolve, reject) => {
transformer.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
transformer.on("end", () =>
resolve(Buffer.concat(chunks).toString("utf8"))
);
transformer.on("error", reject);
});
transformer.end(Buffer.from(` ${secret} `));
const result = await done;
expect(result).to.equal(" XXXX-1 ");
});
// Regression for #342/#349: zip download was constructing the transformer
// and then assigning opt.filePath after the fact, but isText is decided in
// the constructor — so every entry was treated as binary and passed through