diff --git a/plugins/promptfoo/src/parsers/burp-items.test.ts b/plugins/promptfoo/src/parsers/burp-items.test.ts new file mode 100644 index 0000000..cead8e3 --- /dev/null +++ b/plugins/promptfoo/src/parsers/burp-items.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest'; + +import { parseBurp } from './burp.js'; + +describe('parseBurp item extraction', () => { + it('parses multiple Burp items without regex backtracking', () => { + const repeatedNoise = 'a'.repeat(2000); + const xml = ` + + ${repeatedNoise} + + https://example.com/one + example.com + 443 + https + GET + /one + + + + https://example.com/two + example.com + 443 + https + POST + /two + + + + `; + + const parsed = parseBurp(xml); + + expect(parsed).toHaveLength(2); + expect(parsed.map((item) => item.url)).toEqual([ + 'https://example.com/one', + 'https://example.com/two', + ]); + }); +}); diff --git a/plugins/promptfoo/src/parsers/burp.ts b/plugins/promptfoo/src/parsers/burp.ts index b3c9a27..0139a4e 100644 --- a/plugins/promptfoo/src/parsers/burp.ts +++ b/plugins/promptfoo/src/parsers/burp.ts @@ -42,11 +42,7 @@ export function parseBurpSingle(xml: string): ParsedArtifact { function extractItems(xml: string): BurpItem[] { const items: BurpItem[] = []; - // Match elements - const itemMatches = xml.matchAll(/([\s\S]*?)<\/item>/gi); - - for (const match of itemMatches) { - const itemXml = match[1]; + for (const itemXml of extractElementContents(xml, 'item')) { const url = extractTag(itemXml, 'url'); const host = extractTag(itemXml, 'host'); @@ -72,6 +68,31 @@ function extractItems(xml: string): BurpItem[] { return items; } +function extractElementContents(xml: string, tag: string): string[] { + const items: string[] = []; + const openTag = `<${tag}>`; + const closeTag = ``; + let startIndex = 0; + + while (startIndex < xml.length) { + const openIndex = xml.indexOf(openTag, startIndex); + if (openIndex === -1) { + break; + } + + const contentStart = openIndex + openTag.length; + const closeIndex = xml.indexOf(closeTag, contentStart); + if (closeIndex === -1) { + break; + } + + items.push(xml.slice(contentStart, closeIndex)); + startIndex = closeIndex + closeTag.length; + } + + return items; +} + /** * Extract text content from an XML tag */