Fix case-insensitive Claude tool allowlist matching

This commit is contained in:
2026-02-24 12:48:48 -05:00
parent 23ad28ad12
commit 422e8fe5a5
4 changed files with 120 additions and 1 deletions

View File

@@ -458,6 +458,38 @@ function toToolNameCandidates(toolName: string): string[] {
return dedupeStrings(candidates);
}
function buildCaseInsensitiveToolLookup(tools: readonly string[]): Map<string, string> {
const lookup = new Map<string, string>();
for (const tool of tools) {
const normalized = tool.trim().toLowerCase();
if (!normalized || lookup.has(normalized)) {
continue;
}
lookup.set(normalized, tool);
}
return lookup;
}
function resolveAllowedToolMatch(input: {
candidates: readonly string[];
allowset: ReadonlySet<string>;
caseInsensitiveLookup: ReadonlyMap<string, string>;
}): string | undefined {
const direct = input.candidates.find((candidate) => input.allowset.has(candidate));
if (direct) {
return direct;
}
for (const candidate of input.candidates) {
const match = input.caseInsensitiveLookup.get(candidate.toLowerCase());
if (match) {
return match;
}
}
return undefined;
}
function defaultEventPayloadForStatus(status: ActorResultStatus): DomainEventPayload {
if (status === "success") {
return {
@@ -1177,6 +1209,7 @@ export class PipelineExecutor {
private createToolPermissionHandler(allowedTools: readonly string[]): ActorToolPermissionHandler {
const allowset = new Set(allowedTools);
const caseInsensitiveAllowLookup = buildCaseInsensitiveToolLookup(allowedTools);
const rulesEngine = this.securityContext?.rulesEngine;
const toolPolicy = toAllowedToolPolicy(allowedTools);
@@ -1192,7 +1225,11 @@ export class PipelineExecutor {
}
const candidates = toToolNameCandidates(toolName);
const allowMatch = candidates.find((candidate) => allowset.has(candidate));
const allowMatch = resolveAllowedToolMatch({
candidates,
allowset,
caseInsensitiveLookup: caseInsensitiveAllowLookup,
});
if (!allowMatch) {
rulesEngine?.assertToolInvocationAllowed({
tool: candidates[0] ?? toolName,