@@ -261,53 +261,114 @@ function parseCurlCmd(input: string): ParsedHttpRequest | null {
261261
262262/**
263263 * 解析 PowerShell 格式
264+ *
265+ * PowerShell 特殊字符处理:
266+ * - ` (反引号) 是转义字符
267+ * - `" 表示转义的双引号
268+ * - ` + 换行 是续行符
269+ * - @{ } 是哈希表语法
264270 */
265271function parsePowerShell ( input : string ) : ParsedHttpRequest | null {
266272 try {
267- let normalized = input . replace ( / ` \s * \n \s * / g, ' ' ) . trim ( )
273+ // Step 1: 处理 PowerShell 续行符 (反引号 + 换行)
274+ let normalized = input . replace ( / ` \s * \r ? \n \s * / g, ' ' ) . trim ( )
268275
269- // 提取URL
276+ // Step 2: 提取URL (在处理转义之前,因为URL通常不包含转义字符)
270277 let url = ''
271- const urlMatch = normalized . match ( / (?: I n v o k e - W e b R e q u e s t | I n v o k e - R e s t M e t h o d | i w r ) \s + (?: - U r i \s + ) ? [ ' " ] ? ( h t t p s ? : \/ \/ [ ^ \s ' " ] + ) [ ' " ] ? / i)
272- if ( urlMatch && urlMatch [ 1 ] ) {
273- url = urlMatch [ 1 ]
278+ // 匹配 -Uri "url" 或 -Uri 'url' 或直接跟在命令后面的URL
279+ const urlPatterns = [
280+ / (?: I n v o k e - W e b R e q u e s t | I n v o k e - R e s t M e t h o d | i w r ) \s + (?: - [ ^ U ] \w + \s + [ ^ - ] + \s + ) * - U r i \s + " ( [ ^ " ] + ) " / i,
281+ / (?: I n v o k e - W e b R e q u e s t | I n v o k e - R e s t M e t h o d | i w r ) \s + (?: - [ ^ U ] \w + \s + [ ^ - ] + \s + ) * - U r i \s + ' ( [ ^ ' ] + ) ' / i,
282+ / (?: I n v o k e - W e b R e q u e s t | I n v o k e - R e s t M e t h o d | i w r ) \s + (?: - U s e B a s i c P a r s i n g \s + ) ? - U r i \s + " ( [ ^ " ] + ) " / i,
283+ / (?: I n v o k e - W e b R e q u e s t | I n v o k e - R e s t M e t h o d | i w r ) \s + " ( h t t p s ? : \/ \/ [ ^ " ] + ) " / i,
284+ / (?: I n v o k e - W e b R e q u e s t | I n v o k e - R e s t M e t h o d | i w r ) \s + ' ( h t t p s ? : \/ \/ [ ^ ' ] + ) ' / i
285+ ]
286+ for ( const pattern of urlPatterns ) {
287+ const match = normalized . match ( pattern )
288+ if ( match && match [ 1 ] ) {
289+ url = match [ 1 ]
290+ break
291+ }
274292 }
275293
276- // 提取方法
294+ // Step 3: 提取方法
277295 let method = 'GET'
278- const methodMatch = normalized . match ( / - M e t h o d \s + [ ' " ] ? ( \w + ) [ ' " ] ? / i)
296+ const methodMatch = normalized . match ( / - M e t h o d \s + [ ' " ] ? ( G E T | P O S T | P U T | D E L E T E | P A T C H | H E A D | O P T I O N S ) [ ' " ] ? / i)
279297 if ( methodMatch && methodMatch [ 1 ] ) {
280298 method = methodMatch [ 1 ] . toUpperCase ( )
281299 }
300+
301+ // Step 4: 提取 ContentType
302+ let contentType = ''
303+ const contentTypeMatch = normalized . match ( / - C o n t e n t T y p e \s + [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / i)
304+ if ( contentTypeMatch && contentTypeMatch [ 1 ] ) {
305+ contentType = contentTypeMatch [ 1 ]
306+ }
282307
283- // 提取Headers - PowerShell使用 @{ } 语法
308+ // Step 5: 提取Headers - PowerShell使用 @{ } 语法
284309 const headers : Record < string , string > = { }
285- const headersMatch = normalized . match ( / - H e a d e r s \s + @ \{ ( [ ^ } ] + ) \} / i)
310+ // 匹配 @{ 到 } 之间的内容,包括换行
311+ const headersMatch = normalized . match ( / - H e a d e r s \s + @ \{ ( [ \s \S ] * ?) \} (? = \s + - | \s * $ ) / i)
286312 if ( headersMatch && headersMatch [ 1 ] ) {
287313 const headerBlock = headersMatch [ 1 ]
288- // 解析 "Name"="Value" 或 'Name'='Value' 格式
289- const headerPairs = headerBlock . match ( / [ ' " ] ( [ ^ ' " ] + ) [ ' " ] \s * = \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / g)
290- if ( headerPairs ) {
291- headerPairs . forEach ( pair => {
292- const pairMatch = pair . match ( / [ ' " ] ( [ ^ ' " ] + ) [ ' " ] \s * = \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / )
293- if ( pairMatch && pairMatch [ 1 ] && pairMatch [ 2 ] ) {
294- headers [ pairMatch [ 1 ] ] = pairMatch [ 2 ]
295- }
296- } )
314+
315+ // 解析每一行 header
316+ // 格式: "Name"="Value" 或 'Name'='Value'
317+ // PowerShell 中 `" 表示转义的双引号
318+ const headerRegex = / [ ' " ] ( [ ^ ' " ] + ) [ ' " ] \s * = \s * " ( (?: [ ^ " ` ] | ` " ) * ) " / g
319+ let headerMatch
320+ while ( ( headerMatch = headerRegex . exec ( headerBlock ) ) !== null ) {
321+ if ( headerMatch [ 1 ] && headerMatch [ 2 ] !== undefined ) {
322+ // 处理 PowerShell 转义: `" => "
323+ let value = headerMatch [ 2 ] . replace ( / ` " / g, '"' )
324+ // 处理其他转义: `` => `, `n => newline 等
325+ value = value . replace ( / ` ` / g, '`' )
326+ headers [ headerMatch [ 1 ] ] = value
327+ }
297328 }
329+
330+ // 也尝试匹配单引号格式
331+ const singleQuoteRegex = / [ ' " ] ( [ ^ ' " ] + ) [ ' " ] \s * = \s * ' ( [ ^ ' ] * ) ' / g
332+ while ( ( headerMatch = singleQuoteRegex . exec ( headerBlock ) ) !== null ) {
333+ if ( headerMatch [ 1 ] && headerMatch [ 2 ] !== undefined && ! headers [ headerMatch [ 1 ] ] ) {
334+ headers [ headerMatch [ 1 ] ] = headerMatch [ 2 ]
335+ }
336+ }
337+ }
338+
339+ // 如果提取到了 ContentType,添加到 headers
340+ if ( contentType && ! headers [ 'Content-Type' ] && ! headers [ 'content-type' ] ) {
341+ headers [ 'Content-Type' ] = contentType
298342 }
299343
300- // 提取Body
344+ // Step 6: 提取Body
301345 let body = ''
302- const bodyMatch = normalized . match ( / - B o d y \s + [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / i) ||
303- normalized . match ( / - B o d y \s + @ [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / i)
304- if ( bodyMatch && bodyMatch [ 1 ] ) {
305- body = bodyMatch [ 1 ]
306- if ( ! methodMatch ) {
307- method = 'POST'
346+ // 匹配 -Body "content" 其中 content 可能包含 `" 转义
347+ const bodyPatterns = [
348+ / - B o d y \s + " ( (?: [ ^ " ` ] | ` [ " \\ t n r ] ) * ) " / i, // 双引号,处理 `" 转义
349+ / - B o d y \s + ' ( [ ^ ' ] * ) ' / i, // 单引号
350+ / - B o d y \s + @ " ( [ \s \S ] * ?) " @ / i // Here-String
351+ ]
352+
353+ for ( const pattern of bodyPatterns ) {
354+ const match = normalized . match ( pattern )
355+ if ( match && match [ 1 ] !== undefined ) {
356+ body = match [ 1 ]
357+ // 处理 PowerShell 转义
358+ body = body . replace ( / ` " / g, '"' ) // `" => "
359+ body = body . replace ( / ` n / g, '\n' ) // `n => newline
360+ body = body . replace ( / ` r / g, '\r' ) // `r => carriage return
361+ body = body . replace ( / ` t / g, '\t' ) // `t => tab
362+ body = body . replace ( / ` ` / g, '`' ) // `` => `
363+ break
308364 }
309365 }
310366
367+ // 如果有 body 但没有明确指定方法,默认为 POST
368+ if ( body && ! methodMatch ) {
369+ method = 'POST'
370+ }
371+
311372 if ( ! url ) {
312373 return null
313374 }
0 commit comments