fix: Sanitize cURL URLs with <PLACEHOLDER> to {PLACEHOLDER} (#21032)

This commit is contained in:
Michael Kret
2025-10-23 16:16:28 +03:00
committed by GitHub
parent 1d41e47af1
commit 1e2bd08edc
2 changed files with 98 additions and 2 deletions
@@ -1,4 +1,8 @@
import { toHttpNodeParameters, useImportCurlCommand } from '@/composables/useImportCurlCommand';
import {
sanitizeCurlUrlPlaceholders,
toHttpNodeParameters,
useImportCurlCommand,
} from '@/composables/useImportCurlCommand';
const showToast = vi.fn();
@@ -570,3 +574,77 @@ describe('useImportCurlCommand', () => {
});
});
});
describe('sanitizeCurlUrlPlaceholders', () => {
test('should replace angle-bracket placeholders in URL', () => {
const curl = 'curl https://api.openai.com/v1/agents/<AGENT_ID>/runs';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe('curl https://api.openai.com/v1/agents/{AGENT_ID}/runs');
});
test('should handle multiple placeholders in URL', () => {
const curl = 'curl https://example.com/<USER_ID>/files/<FILE_ID>';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe('curl https://example.com/{USER_ID}/files/{FILE_ID}');
});
test('should not modify body containing <html> tags', () => {
const curl = "curl https://example.com -d '<div>Hello <b>World</b></div>'";
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(curl);
});
test('should handle quoted URLs correctly', () => {
const curl = 'curl "https://api.test.com/<PLACEHOLDER>"';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe('curl "https://api.test.com/{PLACEHOLDER}"');
});
test('should leave URLs without placeholders untouched', () => {
const curl = 'curl https://example.com/v1/data';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(curl);
});
test('should not crash on malformed curl commands without URLs', () => {
const curl = 'curl -X POST -d "key=value"';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(curl);
});
test('should handle URLs with query parameters and placeholders', () => {
const curl = 'curl https://example.com/<ID>?a=1&b=<B>';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe('curl https://example.com/{ID}?a=1&b={B}');
});
test('should not alter JSON body containing HTML tags', () => {
const curl =
'curl https://example.com -H "Content-Type: application/json" -d \'{"message": "<b>Hello</b>"}\'';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(curl);
});
test('should not alter JSON body with HTML in nested properties', () => {
const curl =
'curl https://example.com/api -H "Content-Type: application/json" -d \'{"user": {"bio": "<div>Hi <i>there</i></div>"}}\'';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(curl);
});
test('should not alter JSON body where key names contain HTML', () => {
const curl =
'curl https://example.com -H "Content-Type: application/json" -d \'{"<html>key</html>": "value"}\'';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(curl);
});
test('should sanitize URL but keep HTML in JSON body intact', () => {
const curl =
'curl https://api.example.com/<USER_ID> -H "Content-Type: application/json" -d \'{"content": "<div><b>Text</b></div>"}\'';
const result = sanitizeCurlUrlPlaceholders(curl);
expect(result).toBe(
'curl https://api.example.com/{USER_ID} -H "Content-Type: application/json" -d \'{"content": "<div><b>Text</b></div>"}\'',
);
});
});
@@ -231,8 +231,26 @@ export const flattenObject = <T extends Record<string, unknown>>(obj: T, prefix
{} as Record<string, unknown>,
);
/**
* Extracts and sanitizes the URL in a cURL command.
* Converts invalid placeholder syntax like \<PLACEHOLDER\> → {PLACEHOLDER}
*/
export function sanitizeCurlUrlPlaceholders(curlCommand: string): string {
const CURL_URL_REGEX = /curl\s+(['"]?)(https?:\/\/[^\s'"]+)\1/;
const PLACEHOLDER_REGEX = /<([A-Za-z0-9_-]+)>/g;
const urlMatch = curlCommand.match(CURL_URL_REGEX);
if (!urlMatch || !urlMatch[2]) return curlCommand;
const originalUrl = urlMatch[2];
const sanitizedUrl = originalUrl.replaceAll(PLACEHOLDER_REGEX, '{$1}');
return curlCommand.replace(originalUrl, sanitizedUrl);
}
export const toHttpNodeParameters = (curlCommand: string): HttpNodeParameters => {
const curlJson = curlToJson(curlCommand);
const curlJson = curlToJson(sanitizeCurlUrlPlaceholders(curlCommand));
const headers = curlJson.headers ?? {};
lowerCaseContentTypeKey(headers);