mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 11:34:44 -07:00
f090736ebc
Co-authored-by: Taylor Mullen <ntaylormullen@google.com>
143 lines
4.7 KiB
TypeScript
143 lines
4.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* Escapes a string for use in a regular expression.
|
|
*/
|
|
export function escapeRegex(text: string): string {
|
|
return text.replace(/[-[\]{}()*+?.,\\^$|#\s"]/g, '\\$&');
|
|
}
|
|
|
|
/**
|
|
* Basic validation for regular expressions to prevent common ReDoS patterns.
|
|
* This is a heuristic check and not a substitute for a full ReDoS scanner.
|
|
*/
|
|
export function isSafeRegExp(pattern: string): boolean {
|
|
try {
|
|
// 1. Ensure it's a valid regex
|
|
new RegExp(pattern);
|
|
} catch {
|
|
return false;
|
|
}
|
|
|
|
// 2. Limit length to prevent extremely long regexes
|
|
if (pattern.length > 2048) {
|
|
return false;
|
|
}
|
|
|
|
// 3. Heuristic: Check for nested quantifiers which are a primary source of ReDoS.
|
|
// Examples: (a+)+, (a|b)*, (.*)*, ([a-z]+)+
|
|
// We look for a group (...) followed by a quantifier (+, *, or {n,m})
|
|
// where the group itself contains a quantifier.
|
|
// This matches a '(' followed by some content including a quantifier, then ')',
|
|
// followed by another quantifier.
|
|
const nestedQuantifierPattern = /\([^)]*[*+?{].*\)[*+?{]/;
|
|
if (nestedQuantifierPattern.test(pattern)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Builds a list of args patterns for policy matching.
|
|
*
|
|
* This function handles the transformation of command prefixes and regexes into
|
|
* the internal argsPattern representation used by the PolicyEngine.
|
|
*
|
|
* @param argsPattern An optional raw regex string for arguments.
|
|
* @param commandPrefix An optional command prefix (or list of prefixes) to allow.
|
|
* @param commandRegex An optional command regex string to allow.
|
|
* @returns An array of string patterns (or undefined) for the PolicyEngine.
|
|
*/
|
|
export function buildArgsPatterns(
|
|
argsPattern?: string,
|
|
commandPrefix?: string | string[],
|
|
commandRegex?: string,
|
|
): Array<string | undefined> {
|
|
if (commandPrefix) {
|
|
const prefixes = Array.isArray(commandPrefix)
|
|
? commandPrefix
|
|
: [commandPrefix];
|
|
|
|
return prefixes.map((prefix) => {
|
|
// JSON.stringify safely encodes the prefix in quotes.
|
|
// We remove ONLY the trailing quote to match it as an open prefix string.
|
|
const encodedPrefix = JSON.stringify(prefix);
|
|
const openQuotePrefix = encodedPrefix.substring(
|
|
0,
|
|
encodedPrefix.length - 1,
|
|
);
|
|
|
|
// Escape the exact JSON literal segment we expect to see
|
|
const matchSegment = escapeRegex(`"command":${openQuotePrefix}`);
|
|
|
|
// We allow [\s], ["], or the specific sequence [\"] (for escaped quotes
|
|
// in JSON). We do NOT allow generic [\\], which would match "git\status"
|
|
// -> "gitstatus".
|
|
return `${matchSegment}(?:[\\s"]|\\\\")`;
|
|
});
|
|
}
|
|
|
|
if (commandRegex) {
|
|
return [`"command":"${commandRegex}`];
|
|
}
|
|
|
|
return [argsPattern];
|
|
}
|
|
|
|
/**
|
|
* Builds a regex pattern to match a specific parameter and value in tool arguments.
|
|
* This is used to narrow tool approvals to specific parameters.
|
|
*
|
|
* @param paramName The name of the parameter.
|
|
* @param value The value to match.
|
|
* @returns A regex string that matches "<paramName>":<value> in a JSON string.
|
|
*/
|
|
export function buildParamArgsPattern(
|
|
paramName: string,
|
|
value: unknown,
|
|
): string {
|
|
const encodedValue = JSON.stringify(value);
|
|
// We wrap the JSON string in escapeRegex and prepend/append \\0 to explicitly
|
|
// match top-level JSON properties generated by stableStringify, preventing
|
|
// argument injection bypass attacks.
|
|
return `\\\\0${escapeRegex(`"${paramName}":${encodedValue}`)}\\\\0`;
|
|
}
|
|
|
|
/**
|
|
* Builds a regex pattern to match a specific file path in tool arguments.
|
|
* This is used to narrow tool approvals for edit tools to specific files.
|
|
*
|
|
* @param filePath The relative path to the file.
|
|
* @returns A regex string that matches "file_path":"<path>" in a JSON string.
|
|
*/
|
|
export function buildFilePathArgsPattern(filePath: string): string {
|
|
return buildParamArgsPattern('file_path', filePath);
|
|
}
|
|
|
|
/**
|
|
* Builds a regex pattern to match a specific directory path in tool arguments.
|
|
* This is used to narrow tool approvals for list_directory tool.
|
|
*
|
|
* @param dirPath The path to the directory.
|
|
* @returns A regex string that matches "dir_path":"<path>" in a JSON string.
|
|
*/
|
|
export function buildDirPathArgsPattern(dirPath: string): string {
|
|
return buildParamArgsPattern('dir_path', dirPath);
|
|
}
|
|
|
|
/**
|
|
* Builds a regex pattern to match a specific "pattern" in tool arguments.
|
|
* This is used to narrow tool approvals for search tools like glob/grep to specific patterns.
|
|
*
|
|
* @param pattern The pattern to match.
|
|
* @returns A regex string that matches "pattern":"<pattern>" in a JSON string.
|
|
*/
|
|
export function buildPatternArgsPattern(pattern: string): string {
|
|
return buildParamArgsPattern('pattern', pattern);
|
|
}
|