fix(curl-parser): curl commands with url without protocol (#5453)

This commit is contained in:
Bijin A B
2025-09-03 16:05:19 +05:30
committed by GitHub
parent 985b5ed20c
commit 3b74e0da86
2 changed files with 92 additions and 3 deletions

View File

@@ -312,7 +312,22 @@ const isURL = (arg) => {
if (typeof arg !== 'string') {
return false;
}
return !!URL.parse(arg || '').host;
// First try to parse as a regular URL (with protocol)
if (URL.parse(arg || '').host) {
return true;
}
// Check if it looks like a domain without protocol
// This regex matches domain patterns like:
// - example.com
// - sub.example.com
// - example.com/path
// - example.com/path?query=value
// Must contain at least one dot to be considered a domain
const DOMAIN_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\/[^\s]*)?(\?[^\s]*)?$/;
return DOMAIN_PATTERN.test(arg);
};
/**
@@ -320,8 +335,9 @@ const isURL = (arg) => {
* Handles shell-quote operator objects and query parameter patterns
*/
const isURLFragment = (arg) => {
// If it's a glob pattern that looks like a URL, treat it as a complete URL
if (arg && typeof arg === 'object' && arg.op === 'glob') {
return !!URL.parse(arg.pattern || '').host;
return isURL(arg.pattern);
}
if (arg && typeof arg === 'object' && arg.op === '&') {
return true;
@@ -341,7 +357,13 @@ const setURL = (request, url) => {
const urlString = getUrlString(url);
if (!urlString) return;
const newUrl = request.url ? request.url + urlString : urlString;
// Add default protocol if none is present
let processedUrl = urlString;
if (!request.url && !urlString.match(/^[a-zA-Z]+:\/\//)) {
processedUrl = 'https://' + urlString;
}
const newUrl = request.url ? request.url + processedUrl : processedUrl;
const { url: formattedUrl, queries, urlWithoutQuery } = parseUrl(newUrl);

View File

@@ -438,6 +438,73 @@ describe('parseCurlCommand', () => {
});
});
describe('handling URLs without protocols', () => {
it('should parse URL without protocol and default to https', () => {
const result = parseCurlCommand(`
curl echo.usebruno.com
`);
expect(result).toEqual({
method: 'get',
url: 'https://echo.usebruno.com',
urlWithoutQuery: 'https://echo.usebruno.com'
});
});
it('should parse URL without protocol with path and query parameters', () => {
const result = parseCurlCommand(`
curl api.example.com/users?page=1&limit=10
`);
expect(result).toEqual({
method: 'get',
url: 'https://api.example.com/users?page=1&limit=10',
urlWithoutQuery: 'https://api.example.com/users',
queries: [
{ name: 'page', value: '1' },
{ name: 'limit', value: '10' }
]
});
});
it('should parse a complex curl command with multiple features and no protocol', () => {
const result = parseCurlCommand(`
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token123" \
-H "X-Custom-Header: custom header" \
-d '{"name": "John\\'s data", "email": "john@example.com", "message": "Don\\'t stop believing!", "path": "/home/user/file.txt", "json": {"nested": "value", "array": [1, 2, 3]}}' \
-u "api_user:api_pass" \
--compressed \
api.example.com/v1/users?param1=value1&param2=custom+param
`);
expect(result).toEqual({
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
'X-Custom-Header': 'custom header',
'Accept-Encoding': 'deflate, gzip'
},
data: '{"name": "John\'s data", "email": "john@example.com", "message": "Don\'t stop believing!", "path": "/home/user/file.txt", "json": {"nested": "value", "array": [1, 2, 3]}}',
auth: {
mode: 'basic',
basic: {
username: 'api_user',
password: 'api_pass'
}
},
queries: [
{ name: 'param1', value: 'value1' },
{ name: 'param2', value: 'custom+param' }
],
url: 'https://api.example.com/v1/users?param1=value1&param2=custom+param',
urlWithoutQuery: 'https://api.example.com/v1/users'
});
});
});
describe('Edge Cases', () => {
it('should handle compressed flag', () => {
const result = parseCurlCommand(`