Download packages/core/src/policy/utils.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 5.61 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/policy/utils.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/core/src/policy/utils.test.ts
-
curl -L -o utils.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/policy/utils.test.ts
5.61 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { expect, describe, it } from 'vitest'; | |
| import { escapeRegex, buildArgsPatterns, isSafeRegExp } from './utils.js'; | |
| describe('policy/utils', () => { | |
| describe('escapeRegex', () => { | |
| it('should escape special regex characters', () => { | |
| const input = '.-*+?^${}()|[]\\ "'; | |
| const escaped = escapeRegex(input); | |
| expect(escaped).toBe( | |
| '\\.\\-\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\\\ \\"', | |
| ); | |
| }); | |
| it('should return the same string if no special characters are present', () => { | |
| const input = 'abcABC123'; | |
| expect(escapeRegex(input)).toBe(input); | |
| }); | |
| }); | |
| describe('isSafeRegExp', () => { | |
| it('should return true for simple regexes', () => { | |
| expect(isSafeRegExp('abc')).toBe(true); | |
| expect(isSafeRegExp('^abc$')).toBe(true); | |
| expect(isSafeRegExp('a|b')).toBe(true); | |
| }); | |
| it('should return true for safe quantifiers', () => { | |
| expect(isSafeRegExp('a+')).toBe(true); | |
| expect(isSafeRegExp('a*')).toBe(true); | |
| expect(isSafeRegExp('a?')).toBe(true); | |
| expect(isSafeRegExp('a{1,3}')).toBe(true); | |
| }); | |
| it('should return true for safe groups', () => { | |
| expect(isSafeRegExp('(abc)*')).toBe(true); | |
| expect(isSafeRegExp('(a|b)+')).toBe(true); | |
| }); | |
| it('should return false for invalid regexes', () => { | |
| expect(isSafeRegExp('[')).toBe(false); | |
| expect(isSafeRegExp('([a-z)')).toBe(false); | |
| expect(isSafeRegExp('*')).toBe(false); | |
| }); | |
| it('should return false for long regexes', () => { | |
| expect(isSafeRegExp('a'.repeat(3000))).toBe(false); | |
| }); | |
| it('should return false for nested quantifiers (ReDoS heuristic)', () => { | |
| expect(isSafeRegExp('(a+)+')).toBe(false); | |
| expect(isSafeRegExp('(a|b)*')).toBe(true); | |
| expect(isSafeRegExp('(.*)*')).toBe(false); | |
| expect(isSafeRegExp('([a-z]+)+')).toBe(false); | |
| expect(isSafeRegExp('(.*)+')).toBe(false); | |
| }); | |
| }); | |
| describe('buildArgsPatterns', () => { | |
| it('should return argsPattern if provided and no commandPrefix/regex', () => { | |
| const result = buildArgsPatterns('my-pattern', undefined, undefined); | |
| expect(result).toEqual(['my-pattern']); | |
| }); | |
| it('should build pattern from a single commandPrefix', () => { | |
| const result = buildArgsPatterns(undefined, 'ls', undefined); | |
| expect(result).toEqual(['\\"command\\":\\"ls(?:[\\s"]|\\\\")']); | |
| }); | |
| it('should build patterns from an array of commandPrefixes', () => { | |
| const result = buildArgsPatterns(undefined, ['echo', 'ls'], undefined); | |
| expect(result).toEqual([ | |
| '\\"command\\":\\"echo(?:[\\s"]|\\\\")', | |
| '\\"command\\":\\"ls(?:[\\s"]|\\\\")', | |
| ]); | |
| }); | |
| it('should build pattern from commandRegex', () => { | |
| const result = buildArgsPatterns(undefined, undefined, 'rm -rf .*'); | |
| expect(result).toEqual(['"command":"rm -rf .*']); | |
| }); | |
| it('should prioritize commandPrefix over commandRegex and argsPattern', () => { | |
| const result = buildArgsPatterns('raw', 'prefix', 'regex'); | |
| expect(result).toEqual(['\\"command\\":\\"prefix(?:[\\s"]|\\\\")']); | |
| }); | |
| it('should prioritize commandRegex over argsPattern if no commandPrefix', () => { | |
| const result = buildArgsPatterns('raw', undefined, 'regex'); | |
| expect(result).toEqual(['"command":"regex']); | |
| }); | |
| it('should escape characters in commandPrefix', () => { | |
| const result = buildArgsPatterns(undefined, 'git checkout -b', undefined); | |
| expect(result).toEqual([ | |
| '\\"command\\":\\"git\\ checkout\\ \\-b(?:[\\s"]|\\\\")', | |
| ]); | |
| }); | |
| it('should correctly escape quotes in commandPrefix', () => { | |
| const result = buildArgsPatterns(undefined, 'git "fix"', undefined); | |
| expect(result).toEqual([ | |
| // eslint-disable-next-line no-useless-escape | |
| '\\\"command\\\":\\\"git\\ \\\\\\\"fix\\\\\\\"(?:[\\s\"]|\\\\\")', | |
| ]); | |
| }); | |
| it('should handle undefined correctly when no inputs are provided', () => { | |
| const result = buildArgsPatterns(undefined, undefined, undefined); | |
| expect(result).toEqual([undefined]); | |
| }); | |
| it('should match prefixes followed by JSON escaped quotes', () => { | |
| // Testing the security fix logic: allowing "echo \"foo\"" | |
| const prefix = 'echo '; | |
| const patterns = buildArgsPatterns(undefined, prefix, undefined); | |
| const regex = new RegExp(patterns[0]!); | |
| // Mimic JSON stringified args | |
| // echo "foo" -> {"command":"echo \"foo\""} | |
| const validJsonArgs = '{"command":"echo \\"foo\\""}'; | |
| expect(regex.test(validJsonArgs)).toBe(true); | |
| }); | |
| it('should NOT match prefixes followed by raw backslashes (security check)', () => { | |
| // Testing that we blocked the hole: "echo\foo" | |
| const prefix = 'echo '; | |
| const patterns = buildArgsPatterns(undefined, prefix, undefined); | |
| const regex = new RegExp(patterns[0]!); | |
| // echo\foo -> {"command":"echo\\foo"} | |
| // In regex matching: "echo " is followed by "\" which is NOT in [\s"] and is not \" | |
| const attackJsonArgs = '{"command":"echo\\\\foo"}'; | |
| expect(regex.test(attackJsonArgs)).toBe(false); | |
| // Also validation for "git " matching "git\status" | |
| const gitPatterns = buildArgsPatterns(undefined, 'git ', undefined); | |
| const gitRegex = new RegExp(gitPatterns[0]!); | |
| // git\status -> {"command":"git\\status"} | |
| const gitAttack = '{"command":"git\\\\status"}'; | |
| expect(gitAttack).not.toMatch(gitRegex); | |
| }); | |
| }); | |
| }); | |