diff --git a/plugins/promptfoo/src/parsers/curl.test.ts b/plugins/promptfoo/src/parsers/curl.test.ts new file mode 100644 index 0000000..9831449 --- /dev/null +++ b/plugins/promptfoo/src/parsers/curl.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest'; + +import { parseCurl } from './curl.js'; + +describe('parseCurl', () => { + it('ignores unquoted inline comments', () => { + const parsed = parseCurl("curl https://example.com/api # explain the request"); + + expect(parsed.url).toBe('https://example.com/api'); + }); + + it('preserves hash characters inside quoted headers', () => { + const parsed = parseCurl( + "curl https://example.com/api -H 'Authorization: Bearer abc#123' -H 'X-Trace: keep#me'" + ); + + expect(parsed.headers.Authorization).toBe('Bearer abc#123'); + expect(parsed.headers['X-Trace']).toBe('keep#me'); + }); + + it('preserves escaped hash characters outside quotes', () => { + const parsed = parseCurl('curl https://example.com/api --data token=abc\\#123'); + + expect(parsed.body).toEqual({ token: 'abc#123' }); + }); +}); diff --git a/plugins/promptfoo/src/parsers/curl.ts b/plugins/promptfoo/src/parsers/curl.ts index 968d7a1..e3e8342 100644 --- a/plugins/promptfoo/src/parsers/curl.ts +++ b/plugins/promptfoo/src/parsers/curl.ts @@ -24,7 +24,7 @@ export function parseCurl(command: string): ParsedArtifact { // Remove comments and normalize whitespace const cleaned = raw .split('\n') - .map((line) => line.replace(/#.*$/, '').trim()) + .map((line) => stripInlineComment(line).trim()) .join(' ') .replace(/\\\s+/g, ' ') // Handle line continuations .replace(/\s+/g, ' ') @@ -76,6 +76,43 @@ export function parseCurl(command: string): ParsedArtifact { }; } +function stripInlineComment(line: string): string { + let inQuote: string | null = null; + let escaped = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + + if (escaped) { + escaped = false; + continue; + } + + if (char === '\\') { + escaped = true; + continue; + } + + if (inQuote) { + if (char === inQuote) { + inQuote = null; + } + continue; + } + + if (char === '"' || char === "'") { + inQuote = char; + continue; + } + + if (char === '#') { + return line.slice(0, i); + } + } + + return line; +} + /** * Tokenize a curl command, handling quoted strings */