Skip to content

Commit 564618b

Browse files
author
githubnull
committed
fix: 修复PowerShell格式HTTP请求解析问题
1 parent 978761a commit 564618b

1 file changed

Lines changed: 86 additions & 25 deletions

File tree

src/frontEnd/src/utils/httpRequestParser.ts

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -261,53 +261,114 @@ function parseCurlCmd(input: string): ParsedHttpRequest | null {
261261

262262
/**
263263
* 解析 PowerShell 格式
264+
*
265+
* PowerShell 特殊字符处理:
266+
* - ` (反引号) 是转义字符
267+
* - `" 表示转义的双引号
268+
* - ` + 换行 是续行符
269+
* - @{ } 是哈希表语法
264270
*/
265271
function 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(/(?:Invoke-WebRequest|Invoke-RestMethod|iwr)\s+(?:-Uri\s+)?['"]?(https?:\/\/[^\s'"]+)['"]?/i)
272-
if (urlMatch && urlMatch[1]) {
273-
url = urlMatch[1]
278+
// 匹配 -Uri "url" 或 -Uri 'url' 或直接跟在命令后面的URL
279+
const urlPatterns = [
280+
/(?:Invoke-WebRequest|Invoke-RestMethod|iwr)\s+(?:-[^U]\w+\s+[^-]+\s+)*-Uri\s+"([^"]+)"/i,
281+
/(?:Invoke-WebRequest|Invoke-RestMethod|iwr)\s+(?:-[^U]\w+\s+[^-]+\s+)*-Uri\s+'([^']+)'/i,
282+
/(?:Invoke-WebRequest|Invoke-RestMethod|iwr)\s+(?:-UseBasicParsing\s+)?-Uri\s+"([^"]+)"/i,
283+
/(?:Invoke-WebRequest|Invoke-RestMethod|iwr)\s+"(https?:\/\/[^"]+)"/i,
284+
/(?:Invoke-WebRequest|Invoke-RestMethod|iwr)\s+'(https?:\/\/[^']+)'/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(/-Method\s+['"]?(\w+)['"]?/i)
296+
const methodMatch = normalized.match(/-Method\s+['"]?(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)['"]?/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(/-ContentType\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(/-Headers\s+@\{([^}]+)\}/i)
310+
// 匹配 @{ 到 } 之间的内容,包括换行
311+
const headersMatch = normalized.match(/-Headers\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(/-Body\s+['"]([^'"]+)['"]/i) ||
303-
normalized.match(/-Body\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+
/-Body\s+"((?:[^"`]|`["\\tnr])*)"/i, // 双引号,处理 `" 转义
349+
/-Body\s+'([^']*)'/i, // 单引号
350+
/-Body\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

Comments
 (0)