fix(javascript): advertise the exact empty seed rule and match empty API keys

Express the empty-seed rule as a JSON-Schema-representable union, so
the advertised trace_application_feature schema rejects what the
server rejects, and match context-bridge seeds on the exact api_key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iwakurakaoru0822 2026-10-06 22:00:14 -07:00
parent 6cc45e8dc9
commit 35e5508fd0
3 changed files with 64 additions and 42 deletions

View File

@ -1,7 +1,9 @@
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { Ajv2020 } from "ajv/dist/2020.js";
import { expect, it } from "vitest";
import { z } from "zod";
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
import { javascriptApplicationAnalysisResultSchema } from "../domain/javascriptApplicationAnalysis.js";
@ -181,23 +183,27 @@ it("selects an empty IPC channel with an exact empty seed", async () => {
]);
});
it.each([
{ kind: "channel", value: "" },
{ kind: "string", value: "", match: "exact" },
])("accepts an exact empty seed: %j", (seed) => {
expect(
traceApplicationFeatureInputSchema.shape.seed.safeParse(seed).success,
).toBe(true);
});
const seedSchema = traceApplicationFeatureInputSchema.shape.seed;
const validateAdvertisedSeed = new Ajv2020({
strict: false,
validateFormats: false,
}).compile(z.toJSONSchema(seedSchema, { io: "input" }));
it.each([
{ kind: "string", value: "" },
{ kind: "channel", value: "", match: "contains" },
{ kind: "node-id", value: "", match: "exact" },
])("rejects an empty seed that is not an exact literal: %j", (seed) => {
const parsed = traceApplicationFeatureInputSchema.shape.seed.safeParse(seed);
expect(parsed.success).toBe(false);
});
[{ kind: "channel", value: "", match: "exact" }, true],
[{ kind: "string", value: "", match: "exact" }, true],
[{ kind: "channel", value: "/x" }, true],
[{ kind: "string", value: "" }, false],
[{ kind: "channel", value: "" }, false],
[{ kind: "channel", value: "", match: "contains" }, false],
[{ kind: "node-id", value: "", match: "exact" }, false],
] as const)(
"validates empty seed %j at runtime and in the advertised schema",
(seed, accepted) => {
expect(seedSchema.safeParse(seed).success).toBe(accepted);
expect(validateAdvertisedSeed(seed)).toBe(accepted);
},
);
it("pairs the same empty IPC channel across application versions", async () => {
const left = await analyzeSource(IPC_SOURCE);
@ -225,3 +231,21 @@ it("pairs the same empty IPC channel across application versions", async () => {
"ipc-channel",
);
});
it("selects an empty context-bridge API with an exact empty seed", async () => {
const output = await analyzeSource(
"const { contextBridge } = require('electron'); contextBridge.exposeInMainWorld('', {}); contextBridge.exposeInMainWorld('\"\"', {});",
);
const apis = new Map(
output.graph.nodes
.filter(({ kind }) => kind === "context-bridge-api")
.map((node) => [node.node_id, node.observations[0]?.properties.api_key]),
);
const matches = findApplicationFeatureSeeds(output.graph.nodes, {
kind: "api",
value: "",
match: "exact",
case_sensitive: true,
});
expect(matches.map(({ node_id: nodeId }) => apis.get(nodeId))).toEqual([""]);
});

View File

@ -132,7 +132,7 @@ const memberFieldSuffixes = new Set([
const relevantFields = {
route: [".label", ".value", ".path"],
api: [".label", ".api_name", ".key", ".methods", ".members"],
api: [".label", ".api_name", ".api_key", ".key", ".methods", ".members"],
channel: [".label", ".channel", ".key"],
module: [
".label",

View File

@ -17,36 +17,34 @@ export const featureSeedMatchMode = (seed: {
}): "exact" | "contains" =>
seed.match ?? (seed.kind === "string" ? "contains" : "exact");
const literalSeedKinds = [
"route",
"string",
"api",
"channel",
"module",
"native-export",
] as const;
/**
* Literal starting point for one application feature trace. An exact literal
* seed may be empty, such as the channel in `ipcMain.handle("")`; an empty
* containment seed would select every node, so it is rejected.
* Literal starting point for one application feature trace. An empty value,
* such as the channel in `ipcMain.handle("")`, must be an explicit exact
* literal seed: empty containment would select every node.
*/
const applicationFeatureSeedSchema = z
.strictObject({
kind: z.enum([
"node-id",
"route",
"string",
"api",
"channel",
"module",
"native-export",
]),
value: z.string(),
const applicationFeatureSeedSchema = z.union([
z.strictObject({
kind: z.enum(["node-id", ...literalSeedKinds]),
value: boundedTextSchema,
match: z.enum(["exact", "contains"]).nullable().default(null),
case_sensitive: z.boolean().default(false),
})
.superRefine((seed, context) => {
if (seed.value !== "") return;
if (seed.kind === "node-id" || featureSeedMatchMode(seed) === "contains")
context.addIssue({
code: "custom",
path: ["value"],
message:
'An empty seed value requires an exact literal match: use match "exact" with a non-node-id kind',
});
});
}),
z.strictObject({
kind: z.enum(literalSeedKinds),
value: z.literal(""),
match: z.literal("exact"),
case_sensitive: z.boolean().default(false),
}),
]);
/** Evidence-backed application graph and feature seed. */
export const traceApplicationFeatureInputSchema = z