fix(secrets): scan typed JSON collision values
This commit is contained in:
@@ -1028,6 +1028,40 @@ describe("advisor", () => {
|
||||
expect(prompt).toContain("TOKABC123_");
|
||||
});
|
||||
|
||||
it("collects regex values from tool arguments that resemble image blocks", async () => {
|
||||
const obfuscator = new SecretObfuscator([
|
||||
{ type: "plain", content: "OTHERSECRET", friendlyName: "TOKABC123" },
|
||||
{ type: "regex", content: "tok_[a-z0-9]+" },
|
||||
]);
|
||||
const promptInputs: string[] = [];
|
||||
const agent = makeAgent(promptInputs);
|
||||
const messages: AgentMessage[] = [
|
||||
{ role: "user", content: "remember OTHERSECRET for later", timestamp: 1 } as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "toolCall", id: "call-1", name: "read", arguments: { type: "image", value: "tok_abc123" } },
|
||||
],
|
||||
timestamp: 2,
|
||||
} as unknown as AgentMessage,
|
||||
];
|
||||
const host: AdvisorRuntimeHost = {
|
||||
snapshotMessages: () => messages,
|
||||
enqueueAdvice: () => {},
|
||||
obfuscator,
|
||||
};
|
||||
const runtime = new AdvisorRuntime(agent, host);
|
||||
|
||||
runtime.onTurnEnd();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(promptInputs).toHaveLength(1);
|
||||
const prompt = promptInputs[0]!;
|
||||
expect(prompt).not.toContain("OTHERSECRET");
|
||||
expect(prompt).not.toContain("tok_abc123");
|
||||
expect(prompt).not.toContain("TOKABC123_");
|
||||
});
|
||||
|
||||
it("expands plan-mode context once, then collapses an unchanged re-injection", async () => {
|
||||
const promptInputs: string[] = [];
|
||||
const agent = makeAgent(promptInputs);
|
||||
|
||||
@@ -553,10 +553,6 @@ function obfuscateAdvisorMessage(
|
||||
}
|
||||
}
|
||||
|
||||
function isImageBlock(value: object): value is ImageContent {
|
||||
return "type" in value && value.type === "image";
|
||||
}
|
||||
|
||||
function collectAdvisorRegexSecretValues(obfuscator: SecretObfuscator, messages: AgentMessage[]): Set<string> {
|
||||
const values = new Set<string>();
|
||||
const add = (value: string | undefined): void => {
|
||||
@@ -574,7 +570,7 @@ function collectAdvisorRegexSecretValues(obfuscator: SecretObfuscator, messages:
|
||||
for (const item of value) addJsonStrings(item);
|
||||
return;
|
||||
}
|
||||
if (value === null || typeof value !== "object" || isImageBlock(value)) return;
|
||||
if (value === null || typeof value !== "object") return;
|
||||
for (const item of Object.values(value)) addJsonStrings(item);
|
||||
};
|
||||
const addContent = (content: TextualContent): void => {
|
||||
|
||||
@@ -120,7 +120,7 @@ function collectShareRegexSecretValues(o: SecretObfuscator, data: SessionData):
|
||||
for (const item of value) addJsonStrings(item);
|
||||
return;
|
||||
}
|
||||
if (!isRecord(value) || value.type === "image") return;
|
||||
if (!isRecord(value)) return;
|
||||
for (const item of Object.values(value)) addJsonStrings(item);
|
||||
};
|
||||
const addContent = (content: string | (TextContent | ImageContent)[]): void => {
|
||||
|
||||
@@ -402,6 +402,43 @@ describe("buildShareSnapshot", () => {
|
||||
expect(flat).not.toContain(regexSecret);
|
||||
expect(flat).toContain(`${friendlyName}_`);
|
||||
});
|
||||
|
||||
test("collects regex values from tool arguments that resemble image blocks", () => {
|
||||
const plainSecret = "OTHERSECRET";
|
||||
const friendlyName = "TOKABC123";
|
||||
const regexSecret = "tok_abc123";
|
||||
const ts = "2026-06-12T00:00:00.000Z";
|
||||
const entries: SessionEntry[] = [
|
||||
{
|
||||
type: "message",
|
||||
id: "a1",
|
||||
parentId: null,
|
||||
timestamp: ts,
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "toolCall", id: "call-1", name: "read", arguments: { type: "image", value: regexSecret } },
|
||||
],
|
||||
timestamp: 1,
|
||||
},
|
||||
} as unknown as SessionEntry,
|
||||
];
|
||||
const sm = {
|
||||
getHeader: () => ({ ...sessionData([], "x").header, title: `remember ${plainSecret}` }),
|
||||
getEntries: () => entries,
|
||||
getLeafId: () => "a1",
|
||||
} as unknown as SessionManager;
|
||||
const obfuscator = new SecretObfuscator([
|
||||
{ type: "plain", content: plainSecret, friendlyName },
|
||||
{ type: "regex", content: "tok_[a-z0-9]+" },
|
||||
]);
|
||||
|
||||
const flat = JSON.stringify(buildShareSnapshot(sm, { obfuscator }));
|
||||
|
||||
expect(flat).not.toContain(plainSecret);
|
||||
expect(flat).not.toContain(regexSecret);
|
||||
expect(flat).not.toContain(`${friendlyName}_`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeShareServerUrl", () => {
|
||||
|
||||
Reference in New Issue
Block a user