This repository was archived by the owner on May 15, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathcommand-denied.ts
More file actions
78 lines (65 loc) · 2.52 KB
/
Copy pathcommand-denied.ts
File metadata and controls
78 lines (65 loc) · 2.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import { parseCommand } from "@roo/parse-command"
/**
* Find the longest matching prefix from a list of prefixes for a given command.
* Case-insensitive prefix matching with wildcard support.
*
* This mirrors the logic in `src/core/auto-approval/commands.ts` so the webview
* can independently determine which sub-commands are denied.
*/
function findLongestPrefixMatch(command: string, prefixes: string[]): string | null {
if (!command || !prefixes?.length) {
return null
}
const trimmedCommand = command.trim().toLowerCase()
let longestMatch: string | null = null
for (const prefix of prefixes) {
const lowerPrefix = prefix.toLowerCase()
if (lowerPrefix === "*" || trimmedCommand.startsWith(lowerPrefix)) {
if (!longestMatch || lowerPrefix.length > longestMatch.length) {
longestMatch = lowerPrefix
}
}
}
return longestMatch
}
/**
* Check if a single sub-command is denied based on the longest prefix match rule.
* A command is considered denied when the deny list has a matching prefix that is
* at least as long as any matching allow list prefix.
*/
function isSubcommandDenied(command: string, allowedCommands: string[], deniedCommands: string[]): boolean {
if (!command?.trim() || !deniedCommands?.length) {
return false
}
const cmdWithoutRedirection = command.replace(/\d*>&\d*/, "").trim()
const longestDeniedMatch = findLongestPrefixMatch(cmdWithoutRedirection, deniedCommands)
if (!longestDeniedMatch) {
return false
}
const longestAllowedMatch = findLongestPrefixMatch(cmdWithoutRedirection, allowedCommands || [])
if (!longestAllowedMatch) {
return true
}
// Deny list wins when its match is longer or equal
return longestDeniedMatch.length >= longestAllowedMatch.length
}
/**
* Get the list of denied sub-commands from a full command string.
* Parses the command into sub-commands (splitting by &&, ||, ;, |, etc.)
* and returns the ones that match the deny list.
*
* @param command - Full command string (may contain chained commands)
* @param allowedCommands - List of allowed command prefixes
* @param deniedCommands - List of denied command prefixes
* @returns Array of sub-command strings that are denied
*/
export function getDeniedSubcommands(command: string, allowedCommands: string[], deniedCommands: string[]): string[] {
if (!command?.trim() || !deniedCommands?.length) {
return []
}
const subCommands = parseCommand(command)
return subCommands.filter((cmd) => {
const trimmed = cmd.trim()
return trimmed && isSubcommandDenied(trimmed, allowedCommands, deniedCommands)
})
}