Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions plugins/promptfoo/src/parsers/curl.test.ts
Original file line number Diff line number Diff line change
@@ -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' });
});
});
39 changes: 38 additions & 1 deletion plugins/promptfoo/src/parsers/curl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, ' ')
Expand Down Expand Up @@ -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
*/
Expand Down
Loading