Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions packages/trace/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,18 @@ function recordFileAccess(access: "read" | "write", selector: fs.PathOrFileDescr
}

function fetchSelector(input: Parameters<typeof fetch>[0]): string | undefined {
if (typeof input === "string") return input;
if (input instanceof URL) return input.toString();
if (typeof Request !== "undefined" && input instanceof Request) return input.url;
return undefined;
let raw: string | undefined;
if (typeof input === "string") raw = input;
else if (input instanceof URL) raw = input.toString();
else if (typeof Request !== "undefined" && input instanceof Request) raw = input.url;
if (!raw) return undefined;
try {
const parsed = new URL(raw, "http://cellfence.local");
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined;
} catch {
return undefined;
}
return raw;
}

// 0.4.x (N-13): snapshot the disable flag at module-load time.
Expand Down
37 changes: 37 additions & 0 deletions tests/trace.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,43 @@ test("trace hook ignores fetch inputs that do not expose a URL selector", () =>
assert.deepEqual(evidence.accesses, []);
});

test("trace hook ignores fetch calls with non-HTTP schemes like data: URLs", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "cellfence-trace-fetch-non-http-"));
fs.writeFileSync(path.join(tempDir, "app.mjs"), `
try {
await fetch("data:text/plain,hello");
} catch {}
try {
await fetch(new URL("data:application/json,%7B%22ok%22%3Atrue%7D"));
} catch {}
try {
await fetch("https://example.invalid/valid-http");
} catch {}
`);

const evidencePath = path.join(tempDir, "resource-evidence.json");
const result = spawnSync(process.execPath, [
"--import",
pathToFileURL(tracePath).href,
"app.mjs",
], {
cwd: tempDir,
encoding: "utf8",
env: {
...process.env,
CELLFENCE_TRACE_CELL: "runtime",
CELLFENCE_TRACE_OUT: evidencePath,
},
});

assert.equal(result.status, 0, result.stderr);
const evidence = JSON.parse(fs.readFileSync(evidencePath, "utf8"));
assert.equal(evidence.transcriptStatus, "active");
assert.deepEqual(evidence.accesses.map((access) => access.selector), [
"https://example.invalid/valid-http",
]);
});

test("trace hook covers default cell/output and fd based skips", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "cellfence-trace-defaults-"));
fs.mkdirSync(path.join(tempDir, "data"), { recursive: true });
Expand Down
Loading