Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
357 lines
11 KiB
TypeScript
357 lines
11 KiB
TypeScript
import { createMockExecuteFunction } from 'n8n-nodes-base/test/nodes/Helpers';
|
|
import { NodeOperationError } from 'n8n-workflow';
|
|
import type {
|
|
ICredentialDataDecryptedObject,
|
|
IExecuteFunctions,
|
|
INode,
|
|
ISupplyDataFunctions,
|
|
} from 'n8n-workflow';
|
|
|
|
import { checkDomainRestrictions } from '../checkDomainRestrictions';
|
|
|
|
describe('checkDomainRestrictions', () => {
|
|
let mockNode: INode;
|
|
let mockExecuteFunctions: IExecuteFunctions;
|
|
let mockSupplyDataFunctions: ISupplyDataFunctions;
|
|
|
|
beforeEach(() => {
|
|
mockNode = {
|
|
id: 'test-node',
|
|
name: 'Test Node',
|
|
type: 'test',
|
|
typeVersion: 1,
|
|
position: [0, 0] as [number, number],
|
|
parameters: {},
|
|
};
|
|
|
|
mockExecuteFunctions = createMockExecuteFunction({}, mockNode);
|
|
mockSupplyDataFunctions = mockExecuteFunctions as unknown as ISupplyDataFunctions;
|
|
});
|
|
|
|
describe('when allowedDomainsType is "domains"', () => {
|
|
it('should throw error when allowedDomains is empty', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: '',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).toThrow(NodeOperationError);
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).toThrow(
|
|
'No allowed domains specified. Configure allowed domains or change restriction setting.',
|
|
);
|
|
});
|
|
|
|
it('should throw error when allowedDomains is whitespace only', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: ' ',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).toThrow(NodeOperationError);
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).toThrow(
|
|
'No allowed domains specified. Configure allowed domains or change restriction setting.',
|
|
);
|
|
});
|
|
|
|
it('should throw error when allowedDomains is undefined', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).toThrow(NodeOperationError);
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).toThrow(
|
|
'No allowed domains specified. Configure allowed domains or change restriction setting.',
|
|
);
|
|
});
|
|
|
|
it('should throw error when URL is not in allowed domains', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com,test.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://notallowed.com');
|
|
}).toThrow(NodeOperationError);
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://notallowed.com');
|
|
}).toThrow(
|
|
'Domain not allowed: This credential is restricted from accessing https://notallowed.com. Only the following domains are allowed: example.com,test.com',
|
|
);
|
|
});
|
|
|
|
it('should not throw error when URL is in allowed domains (exact match)', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should not throw error when URL is in allowed domains (comma-separated list)', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com,test.com,another.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://test.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should not throw error when URL matches wildcard domain', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: '*.example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://sub.example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should work with IExecuteFunctions context', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should work with ISupplyDataFunctions context', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockSupplyDataFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('when allowedDomainsType is "none"', () => {
|
|
it('should not throw error when URL matches credentials URL', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
url: 'https://example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should throw error when URL does not match credentials URL', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
url: 'https://example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://different.com');
|
|
}).toThrow(NodeOperationError);
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://different.com');
|
|
}).toThrow(
|
|
'Domain not allowed: This credential is restricted from accessing https://different.com. ',
|
|
);
|
|
});
|
|
|
|
it('should not throw error when credentials URL key does not exist', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://any-url.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should use custom credentialsUrlKey parameter', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
baseUrl: 'https://custom.example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(
|
|
mockExecuteFunctions,
|
|
credentials,
|
|
'https://custom.example.com',
|
|
'baseUrl',
|
|
);
|
|
}).not.toThrow();
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(
|
|
mockExecuteFunctions,
|
|
credentials,
|
|
'https://different.com',
|
|
'baseUrl',
|
|
);
|
|
}).toThrow(NodeOperationError);
|
|
});
|
|
|
|
it('should work with IExecuteFunctions context', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
url: 'https://example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should work with ISupplyDataFunctions context', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
url: 'https://example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockSupplyDataFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('when allowedDomainsType is undefined or other value', () => {
|
|
it('should not throw error when allowedDomainsType is undefined', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://any-url.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should not throw error when allowedDomainsType is empty string', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: '',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://any-url.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should not throw error when allowedDomainsType is other value', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'other' as any,
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://any-url.com');
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle URLs with paths and query parameters', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(
|
|
mockExecuteFunctions,
|
|
credentials,
|
|
'https://example.com/api/v1/endpoint?param=value',
|
|
);
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should handle URLs with ports', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com:8080');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should handle case-insensitive domain matching', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'EXAMPLE.COM',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should handle domains with trailing dots', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com.',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should handle multiple domains with spaces in allowedDomains', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'domains',
|
|
allowedDomains: 'example.com, test.com , another.com',
|
|
};
|
|
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://test.com');
|
|
}).not.toThrow();
|
|
});
|
|
|
|
it('should handle exact URL match for "none" type with different protocols', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
url: 'https://example.com',
|
|
};
|
|
|
|
// Should throw because protocol is different
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'http://example.com');
|
|
}).toThrow(NodeOperationError);
|
|
});
|
|
|
|
it('should handle exact URL match for "none" type with different paths', () => {
|
|
const credentials: ICredentialDataDecryptedObject = {
|
|
allowedHttpRequestDomains: 'none',
|
|
url: 'https://example.com',
|
|
};
|
|
|
|
// Should throw because path is different
|
|
expect(() => {
|
|
checkDomainRestrictions(mockExecuteFunctions, credentials, 'https://example.com/path');
|
|
}).toThrow(NodeOperationError);
|
|
});
|
|
});
|
|
});
|