mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-31 08:46:58 +02:00
fix(HTTP Request Node): Trim url whitespace (#27381)
Co-authored-by: yehorkardash <yehor.kardash@n8n.io>
This commit is contained in:
parent
21a3090152
commit
47ffd1cc07
|
|
@ -693,7 +693,7 @@ export class HttpRequestV1 implements INodeType {
|
|||
const parametersAreJson = this.getNodeParameter('jsonParameters', itemIndex);
|
||||
|
||||
const options = this.getNodeParameter('options', itemIndex, {});
|
||||
const url = this.getNodeParameter('url', itemIndex);
|
||||
let url = this.getNodeParameter('url', itemIndex);
|
||||
|
||||
if (typeof url !== 'string') {
|
||||
const actualType = url === null ? 'null' : typeof url;
|
||||
|
|
@ -703,6 +703,12 @@ export class HttpRequestV1 implements INodeType {
|
|||
);
|
||||
}
|
||||
|
||||
url = url.trim();
|
||||
|
||||
if (!url) {
|
||||
throw new NodeOperationError(this.getNode(), 'URL parameter cannot be empty');
|
||||
}
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
|
|
|
|||
|
|
@ -740,7 +740,7 @@ export class HttpRequestV2 implements INodeType {
|
|||
const parametersAreJson = this.getNodeParameter('jsonParameters', itemIndex);
|
||||
|
||||
const options = this.getNodeParameter('options', itemIndex, {});
|
||||
const url = this.getNodeParameter('url', itemIndex);
|
||||
let url = this.getNodeParameter('url', itemIndex);
|
||||
|
||||
if (typeof url !== 'string') {
|
||||
const actualType = url === null ? 'null' : typeof url;
|
||||
|
|
@ -750,6 +750,12 @@ export class HttpRequestV2 implements INodeType {
|
|||
);
|
||||
}
|
||||
|
||||
url = url.trim();
|
||||
|
||||
if (!url) {
|
||||
throw new NodeOperationError(this.getNode(), 'URL parameter cannot be empty');
|
||||
}
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ export class HttpRequestV3 implements INodeType {
|
|||
}
|
||||
}
|
||||
|
||||
const url = this.getNodeParameter('url', itemIndex);
|
||||
let url = this.getNodeParameter('url', itemIndex);
|
||||
|
||||
if (typeof url !== 'string') {
|
||||
const actualType = url === null ? 'null' : typeof url;
|
||||
|
|
@ -228,6 +228,12 @@ export class HttpRequestV3 implements INodeType {
|
|||
);
|
||||
}
|
||||
|
||||
url = url.trim();
|
||||
|
||||
if (!url) {
|
||||
throw new NodeOperationError(this.getNode(), 'URL parameter cannot be empty');
|
||||
}
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
|
|
|
|||
|
|
@ -15,21 +15,37 @@ describe('HttpRequestV1', () => {
|
|||
};
|
||||
node = new HttpRequestV1(baseDescription);
|
||||
executeFunctions = {
|
||||
getInputData: jest.fn(() => [{ json: {} }]),
|
||||
getInputData: jest.fn(),
|
||||
getNodeParameter: jest.fn(),
|
||||
getNode: jest.fn(() => ({
|
||||
type: 'n8n-nodes-base.httpRequest',
|
||||
typeVersion: 1,
|
||||
})),
|
||||
getNode: jest.fn(() => {
|
||||
return {
|
||||
type: 'n8n-nodes-base.httpRequest',
|
||||
typeVersion: 1,
|
||||
};
|
||||
}),
|
||||
getCredentials: jest.fn(),
|
||||
helpers: {
|
||||
request: jest.fn(),
|
||||
requestOAuth1: jest.fn(),
|
||||
requestOAuth2: jest.fn(),
|
||||
requestOAuth1: jest.fn(
|
||||
async () =>
|
||||
await Promise.resolve({
|
||||
success: true,
|
||||
}),
|
||||
),
|
||||
requestOAuth2: jest.fn(
|
||||
async () =>
|
||||
await Promise.resolve({
|
||||
success: true,
|
||||
}),
|
||||
),
|
||||
requestWithAuthentication: jest.fn(),
|
||||
requestWithAuthenticationPaginated: jest.fn(),
|
||||
assertBinaryData: jest.fn(),
|
||||
getBinaryStream: jest.fn(),
|
||||
getBinaryMetadata: jest.fn(),
|
||||
binaryToString: jest.fn(),
|
||||
binaryToString: jest.fn((buffer: Buffer) => {
|
||||
return buffer.toString();
|
||||
}),
|
||||
prepareBinaryData: jest.fn(),
|
||||
},
|
||||
getContext: jest.fn(),
|
||||
|
|
@ -40,27 +56,93 @@ describe('HttpRequestV1', () => {
|
|||
});
|
||||
|
||||
describe('URL Parameter Validation', () => {
|
||||
it.each([
|
||||
{ url: undefined, expectedType: 'undefined' },
|
||||
{ url: null, expectedType: 'null' },
|
||||
{ url: 42, expectedType: 'number' },
|
||||
])('should throw error when URL is $expectedType', async ({ url, expectedType }) => {
|
||||
it('should throw error when URL is only whitespace', async () => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'responseFormat':
|
||||
return 'json';
|
||||
case 'requestMethod':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return ' ';
|
||||
case 'jsonParameters':
|
||||
return false;
|
||||
case 'options':
|
||||
return {};
|
||||
case 'url':
|
||||
return url;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
(executeFunctions.getCredentials as jest.Mock).mockRejectedValue(new Error('No credentials'));
|
||||
|
||||
await expect(node.execute.call(executeFunctions)).rejects.toThrow(
|
||||
'URL parameter cannot be empty',
|
||||
);
|
||||
});
|
||||
|
||||
it('should trim whitespace from valid URL', async () => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'responseFormat':
|
||||
return 'json';
|
||||
case 'requestMethod':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return ' http://example.com ';
|
||||
case 'jsonParameters':
|
||||
return false;
|
||||
case 'options':
|
||||
return {};
|
||||
case 'bodyParametersUi':
|
||||
case 'headerParametersUi':
|
||||
case 'queryParametersUi':
|
||||
return { parameter: [] };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
(executeFunctions.getCredentials as jest.Mock).mockRejectedValue(new Error('No credentials'));
|
||||
const response = {
|
||||
success: true,
|
||||
};
|
||||
(executeFunctions.helpers.request as jest.Mock).mockResolvedValue(response);
|
||||
|
||||
const result = await node.execute.call(executeFunctions);
|
||||
expect(result).toEqual([[{ json: { success: true }, pairedItem: { item: 0 } }]]);
|
||||
expect(executeFunctions.helpers.request).toHaveBeenCalledTimes(1);
|
||||
const requestArgs = (executeFunctions.helpers.request as jest.Mock).mock.calls[0][0];
|
||||
expect(requestArgs.uri ?? requestArgs.url).toBe('http://example.com');
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ url: undefined, expectedType: 'undefined' },
|
||||
{ url: null, expectedType: 'null' },
|
||||
{ url: 42, expectedType: 'number' },
|
||||
])('should throw error when URL is $expectedType', async ({ url, expectedType }) => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'responseFormat':
|
||||
return 'json';
|
||||
case 'requestMethod':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return url;
|
||||
case 'jsonParameters':
|
||||
return false;
|
||||
case 'options':
|
||||
return {};
|
||||
case 'bodyParametersUi':
|
||||
case 'headerParametersUi':
|
||||
case 'queryParametersUi':
|
||||
return { parameter: [] };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
(executeFunctions.getCredentials as jest.Mock).mockRejectedValue(new Error('No credentials'));
|
||||
|
||||
await expect(node.execute.call(executeFunctions)).rejects.toThrow(
|
||||
`URL parameter must be a string, got ${expectedType}`,
|
||||
|
|
|
|||
|
|
@ -162,6 +162,68 @@ describe('HttpRequestV2', () => {
|
|||
});
|
||||
|
||||
describe('URL Parameter Validation', () => {
|
||||
it('should throw error when URL is only whitespace', async () => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'responseFormat':
|
||||
return 'json';
|
||||
case 'requestMethod':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return ' ';
|
||||
case 'authentication':
|
||||
return 'none';
|
||||
case 'jsonParameters':
|
||||
return false;
|
||||
case 'options':
|
||||
return options;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
await expect(node.execute.call(executeFunctions)).rejects.toThrow(
|
||||
'URL parameter cannot be empty',
|
||||
);
|
||||
});
|
||||
|
||||
it('should trim whitespace from valid URL', async () => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'responseFormat':
|
||||
return 'json';
|
||||
case 'requestMethod':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return ' http://example.com ';
|
||||
case 'authentication':
|
||||
return 'none';
|
||||
case 'jsonParameters':
|
||||
return false;
|
||||
case 'options':
|
||||
return options;
|
||||
case 'bodyParametersUi':
|
||||
case 'headerParametersUi':
|
||||
case 'queryParametersUi':
|
||||
return { parameter: [] };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
const response = {
|
||||
success: true,
|
||||
};
|
||||
(executeFunctions.helpers.request as jest.Mock).mockResolvedValue(response);
|
||||
|
||||
const result = await node.execute.call(executeFunctions);
|
||||
expect(result).toEqual([[{ json: { success: true }, pairedItem: { item: 0 } }]]);
|
||||
expect(executeFunctions.helpers.request).toHaveBeenCalledTimes(1);
|
||||
const requestArgs = (executeFunctions.helpers.request as jest.Mock).mock.calls[0][0];
|
||||
expect(requestArgs.uri ?? requestArgs.url).toBe('http://example.com');
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ url: undefined, expectedType: 'undefined' },
|
||||
{ url: null, expectedType: 'null' },
|
||||
|
|
@ -182,6 +244,10 @@ describe('HttpRequestV2', () => {
|
|||
return false;
|
||||
case 'options':
|
||||
return options;
|
||||
case 'bodyParametersUi':
|
||||
case 'headerParametersUi':
|
||||
case 'queryParametersUi':
|
||||
return { parameter: [] };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,6 +298,56 @@ describe('HttpRequestV3', () => {
|
|||
'URL parameter must be a string, got number',
|
||||
);
|
||||
});
|
||||
it('should throw error when URL is only whitespace', async () => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'method':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return ' ';
|
||||
case 'authentication':
|
||||
return 'none';
|
||||
case 'options':
|
||||
return options;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
await expect(node.execute.call(executeFunctions)).rejects.toThrow(
|
||||
'URL parameter cannot be empty',
|
||||
);
|
||||
});
|
||||
|
||||
it('should trim whitespace from valid URL', async () => {
|
||||
(executeFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
||||
(executeFunctions.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'method':
|
||||
return 'GET';
|
||||
case 'url':
|
||||
return ' http://example.com ';
|
||||
case 'authentication':
|
||||
return 'none';
|
||||
case 'options':
|
||||
return options;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
const response = {
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: Buffer.from(JSON.stringify({ success: true })),
|
||||
};
|
||||
(executeFunctions.helpers.request as jest.Mock).mockResolvedValue(response);
|
||||
|
||||
const result = await node.execute.call(executeFunctions);
|
||||
expect(result).toEqual([[{ json: { success: true }, pairedItem: { item: 0 } }]]);
|
||||
expect(executeFunctions.helpers.request).toHaveBeenCalledTimes(1);
|
||||
const requestArgs = (executeFunctions.helpers.request as jest.Mock).mock.calls[0][0];
|
||||
expect(requestArgs.uri ?? requestArgs.url).toBe('http://example.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSON Parameter Validation', () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user