mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat: Add option to restrict credential usage in http request node (#17583)
This commit is contained in:
@@ -44,6 +44,7 @@ export {
|
||||
randomString,
|
||||
isSafeObjectProperty,
|
||||
setSafeObjectProperty,
|
||||
isDomainAllowed,
|
||||
} from './utils';
|
||||
export {
|
||||
isINodeProperties,
|
||||
|
||||
@@ -358,3 +358,43 @@ export function setSafeObjectProperty(
|
||||
target[property] = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function isDomainAllowed(
|
||||
urlString: string,
|
||||
options: {
|
||||
allowedDomains: string;
|
||||
},
|
||||
): boolean {
|
||||
if (!options.allowedDomains || options.allowedDomains.trim() === '') {
|
||||
return true; // If no restrictions are set, allow all domains
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(urlString);
|
||||
const hostname = url.hostname;
|
||||
|
||||
const allowedDomainsList = options.allowedDomains
|
||||
.split(',')
|
||||
.map((domain) => domain.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
for (const allowedDomain of allowedDomainsList) {
|
||||
// Handle wildcard domains (*.example.com)
|
||||
if (allowedDomain.startsWith('*.')) {
|
||||
const domainSuffix = allowedDomain.substring(2); // Remove the *. part
|
||||
if (hostname.endsWith(domainSuffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Exact match
|
||||
else if (hostname === allowedDomain) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
// If URL parsing fails, deny access to be safe
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
jsonParse,
|
||||
jsonStringify,
|
||||
deepCopy,
|
||||
isDomainAllowed,
|
||||
isObjectEmpty,
|
||||
fileTypeFromMimeType,
|
||||
randomInt,
|
||||
@@ -461,3 +462,133 @@ describe('sleepWithAbort', () => {
|
||||
clearTimeoutSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDomainAllowed', () => {
|
||||
describe('when no allowed domains are specified', () => {
|
||||
it('should allow all domains when allowedDomains is empty', () => {
|
||||
expect(isDomainAllowed('https://example.com', { allowedDomains: '' })).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow all domains when allowedDomains contains only whitespace', () => {
|
||||
expect(isDomainAllowed('https://example.com', { allowedDomains: ' ' })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('in strict validation mode', () => {
|
||||
it('should allow exact domain matches', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://example.com', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow domains from a comma-separated list', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://example.com', {
|
||||
allowedDomains: 'test.com,example.com,other.org',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle whitespace in allowed domains list', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://example.com', {
|
||||
allowedDomains: ' test.com , example.com , other.org ',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should block non-matching domains', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://malicious.com', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should block subdomains not set', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://sub.example.com', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with wildcard domains', () => {
|
||||
it('should allow matching wildcard domains', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://test.example.com', {
|
||||
allowedDomains: '*.example.com',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow nested subdomains with wildcards', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://deep.nested.example.com', {
|
||||
allowedDomains: '*.example.com',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should block non-matching domains with wildcards', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://example.org', {
|
||||
allowedDomains: '*.example.com',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle invalid URLs safely', () => {
|
||||
expect(
|
||||
isDomainAllowed('not-a-valid-url', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle URLs with ports', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://example.com:8080/path', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle URLs with authentication', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://user:pass@example.com', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle URLs with query parameters and fragments', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://example.com/path?query=test#fragment', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle IP addresses', () => {
|
||||
expect(
|
||||
isDomainAllowed('https://192.168.1.1', {
|
||||
allowedDomains: '192.168.1.1',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle empty URLs', () => {
|
||||
expect(
|
||||
isDomainAllowed('', {
|
||||
allowedDomains: 'example.com',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user