Skip to content

Commit ab10bc2

Browse files
committed
move code to new file
1 parent 6b892a0 commit ab10bc2

File tree

3 files changed

+113
-109
lines changed

3 files changed

+113
-109
lines changed

webview-ui/src/utils/__tests__/command-validation.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ import {
1212
CommandValidator,
1313
createCommandValidator,
1414
containsDangerousSubstitution,
15-
// kilocode_change start
15+
} from "../command-validation"
16+
// kilocode_change start
17+
import {
1618
protectNewlinesInQuotes,
1719
NEWLINE_PLACEHOLDER,
1820
CARRIAGE_RETURN_PLACEHOLDER,
19-
// kilocode_change end
20-
} from "../command-validation"
21+
} from "../command-validation-quote-protection"
22+
// kilocode_change end
2123

2224
// kilocode_change start
2325
describe("protectNewlinesInQuotes", () => {
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// kilocode_change new file
2+
3+
/**
4+
* Placeholders used to protect newlines within quoted strings during command parsing.
5+
* These constants are used by the protectNewlinesInQuotes function to temporarily replace
6+
* newlines that appear inside quotes, preventing them from being treated as command separators.
7+
* We use separate placeholders for \n and \r to preserve the original line ending type.
8+
*/
9+
export const NEWLINE_PLACEHOLDER = "___NEWLINE___"
10+
export const CARRIAGE_RETURN_PLACEHOLDER = "___CARRIAGE_RETURN___"
11+
12+
/**
13+
* Protect newlines inside quoted strings by replacing them with placeholders.
14+
* This handles proper shell quoting rules where quotes can be concatenated.
15+
* Uses separate placeholders for \n and \r to preserve the original line ending type.
16+
*
17+
* Examples:
18+
* - "hello\nworld" -> newline is protected (inside double quotes)
19+
* - 'hello\nworld' -> newline is protected (inside single quotes)
20+
* - echo '"'A'"' -> A is NOT quoted (quote concatenation)
21+
* - "hello"world -> world is NOT quoted
22+
*
23+
* @param command - The command string to process
24+
* @param newlinePlaceholder - The placeholder string to use for \n characters
25+
* @param carriageReturnPlaceholder - The placeholder string to use for \r characters
26+
* @returns The command with newlines in quotes replaced by placeholders
27+
*/
28+
export function protectNewlinesInQuotes(
29+
command: string,
30+
newlinePlaceholder: string,
31+
carriageReturnPlaceholder: string,
32+
): string {
33+
let result = ""
34+
let i = 0
35+
36+
while (i < command.length) {
37+
const char = command[i]
38+
39+
if (char === '"') {
40+
// Start of double-quoted string
41+
result += char
42+
i++
43+
44+
// Process until we find the closing unescaped quote
45+
while (i < command.length) {
46+
const quoteChar = command[i]
47+
const prevChar = i > 0 ? command[i - 1] : ""
48+
49+
if (quoteChar === '"' && prevChar !== "\\") {
50+
// Found closing quote
51+
result += quoteChar
52+
i++
53+
break
54+
} else if (quoteChar === "\n") {
55+
// Replace \n inside double quotes
56+
result += newlinePlaceholder
57+
i++
58+
} else if (quoteChar === "\r") {
59+
// Replace \r inside double quotes
60+
result += carriageReturnPlaceholder
61+
i++
62+
} else {
63+
result += quoteChar
64+
i++
65+
}
66+
}
67+
} else if (char === "'") {
68+
// Start of single-quoted string
69+
result += char
70+
i++
71+
72+
// Process until we find the closing quote
73+
// Note: In single quotes, backslash does NOT escape (except for \' in some shells)
74+
while (i < command.length) {
75+
const quoteChar = command[i]
76+
77+
if (quoteChar === "'") {
78+
// Found closing quote
79+
result += quoteChar
80+
i++
81+
break
82+
} else if (quoteChar === "\n") {
83+
// Replace \n inside single quotes
84+
result += newlinePlaceholder
85+
i++
86+
} else if (quoteChar === "\r") {
87+
// Replace \r inside single quotes
88+
result += carriageReturnPlaceholder
89+
i++
90+
} else {
91+
result += quoteChar
92+
i++
93+
}
94+
}
95+
} else {
96+
// Not in quotes, keep character as-is
97+
result += char
98+
i++
99+
}
100+
}
101+
102+
return result
103+
}

webview-ui/src/utils/command-validation.ts

Lines changed: 5 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
import { parse } from "shell-quote"
2+
import {
3+
protectNewlinesInQuotes,
4+
NEWLINE_PLACEHOLDER,
5+
CARRIAGE_RETURN_PLACEHOLDER,
6+
} from "./command-validation-quote-protection"
27

38
type ShellToken = string | { op: string } | { command: string }
49

5-
// kilocode_change start
6-
/**
7-
* Placeholders used to protect newlines within quoted strings during command parsing.
8-
* These constants are used by the protectNewlinesInQuotes function to temporarily replace
9-
* newlines that appear inside quotes, preventing them from being treated as command separators.
10-
* We use separate placeholders for \n and \r to preserve the original line ending type.
11-
*/
12-
export const NEWLINE_PLACEHOLDER = "___NEWLINE___"
13-
export const CARRIAGE_RETURN_PLACEHOLDER = "___CARRIAGE_RETURN___"
14-
// kilocode_change end
15-
1610
/**
1711
* # Command Denylist Feature - Longest Prefix Match Strategy
1812
*
@@ -133,101 +127,6 @@ export function containsDangerousSubstitution(source: string): boolean {
133127
)
134128
}
135129

136-
// kilocode_change start
137-
/**
138-
* Protect newlines inside quoted strings by replacing them with placeholders.
139-
* This handles proper shell quoting rules where quotes can be concatenated.
140-
* Uses separate placeholders for \n and \r to preserve the original line ending type.
141-
*
142-
* Examples:
143-
* - "hello\nworld" -> newline is protected (inside double quotes)
144-
* - 'hello\nworld' -> newline is protected (inside single quotes)
145-
* - echo '"'A'"' -> A is NOT quoted (quote concatenation)
146-
* - "hello"world -> world is NOT quoted
147-
*
148-
* @param command - The command string to process
149-
* @param newlinePlaceholder - The placeholder string to use for \n characters
150-
* @param carriageReturnPlaceholder - The placeholder string to use for \r characters
151-
* @returns The command with newlines in quotes replaced by placeholders
152-
*/
153-
export function protectNewlinesInQuotes(
154-
command: string,
155-
newlinePlaceholder: string,
156-
carriageReturnPlaceholder: string,
157-
): string {
158-
let result = ""
159-
let i = 0
160-
161-
while (i < command.length) {
162-
const char = command[i]
163-
164-
if (char === '"') {
165-
// Start of double-quoted string
166-
result += char
167-
i++
168-
169-
// Process until we find the closing unescaped quote
170-
while (i < command.length) {
171-
const quoteChar = command[i]
172-
const prevChar = i > 0 ? command[i - 1] : ""
173-
174-
if (quoteChar === '"' && prevChar !== "\\") {
175-
// Found closing quote
176-
result += quoteChar
177-
i++
178-
break
179-
} else if (quoteChar === "\n") {
180-
// Replace \n inside double quotes
181-
result += newlinePlaceholder
182-
i++
183-
} else if (quoteChar === "\r") {
184-
// Replace \r inside double quotes
185-
result += carriageReturnPlaceholder
186-
i++
187-
} else {
188-
result += quoteChar
189-
i++
190-
}
191-
}
192-
} else if (char === "'") {
193-
// Start of single-quoted string
194-
result += char
195-
i++
196-
197-
// Process until we find the closing quote
198-
// Note: In single quotes, backslash does NOT escape (except for \' in some shells)
199-
while (i < command.length) {
200-
const quoteChar = command[i]
201-
202-
if (quoteChar === "'") {
203-
// Found closing quote
204-
result += quoteChar
205-
i++
206-
break
207-
} else if (quoteChar === "\n") {
208-
// Replace \n inside single quotes
209-
result += newlinePlaceholder
210-
i++
211-
} else if (quoteChar === "\r") {
212-
// Replace \r inside single quotes
213-
result += carriageReturnPlaceholder
214-
i++
215-
} else {
216-
result += quoteChar
217-
i++
218-
}
219-
}
220-
} else {
221-
// Not in quotes, keep character as-is
222-
result += char
223-
i++
224-
}
225-
}
226-
227-
return result
228-
}
229-
// kilocode_change end
230-
231130
// kilocode_change start added exception for quoted newlines
232131
/**
233132
* Split a command string into individual sub-commands by

0 commit comments

Comments
 (0)