|
1 | 1 | import { parse } from "shell-quote" |
| 2 | +import { |
| 3 | + protectNewlinesInQuotes, |
| 4 | + NEWLINE_PLACEHOLDER, |
| 5 | + CARRIAGE_RETURN_PLACEHOLDER, |
| 6 | +} from "./command-validation-quote-protection" |
2 | 7 |
|
3 | 8 | type ShellToken = string | { op: string } | { command: string } |
4 | 9 |
|
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 | | - |
16 | 10 | /** |
17 | 11 | * # Command Denylist Feature - Longest Prefix Match Strategy |
18 | 12 | * |
@@ -133,101 +127,6 @@ export function containsDangerousSubstitution(source: string): boolean { |
133 | 127 | ) |
134 | 128 | } |
135 | 129 |
|
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 | | - |
231 | 130 | // kilocode_change start added exception for quoted newlines |
232 | 131 | /** |
233 | 132 | * Split a command string into individual sub-commands by |
|
0 commit comments