mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-30 18:01:23 +08:00
fix: Update anonymous telemetry to remove values that are not needed (#25040)
Co-authored-by: yehorkardash <yehor.kardash@n8n.io>
This commit is contained in:
@@ -3032,7 +3032,6 @@ export interface INodeGraphItem {
|
||||
operation?: string;
|
||||
domain?: string; // HTTP Request node v1
|
||||
domain_base?: string; // HTTP Request node v2
|
||||
domain_path?: string; // HTTP Request node v2
|
||||
position: [number, number];
|
||||
mode?: string;
|
||||
credential_type?: string; // HTTP Request node v2
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ApplicationError } from '@n8n/errors';
|
||||
|
||||
import {
|
||||
AGENT_LANGCHAIN_NODE_TYPE,
|
||||
AGENT_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
@@ -26,8 +28,8 @@ import {
|
||||
WEBHOOK_NODE_TYPE,
|
||||
WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
} from './constants';
|
||||
import { ApplicationError } from '@n8n/errors';
|
||||
import type { NodeApiError } from './errors/node-api.error';
|
||||
import { DEFAULT_EVALUATION_METRIC } from './evaluation-helpers';
|
||||
import type {
|
||||
IConnection,
|
||||
IConnections,
|
||||
@@ -47,7 +49,6 @@ import type {
|
||||
import { NodeConnectionTypes } from './interfaces';
|
||||
import { getNodeParameters, isSubNodeType } from './node-helpers';
|
||||
import { jsonParse } from './utils';
|
||||
import { DEFAULT_EVALUATION_METRIC } from './evaluation-helpers';
|
||||
|
||||
const isNodeApiError = (error: unknown): error is NodeApiError =>
|
||||
typeof error === 'object' && error !== null && 'name' in error && error?.name === 'NodeApiError';
|
||||
@@ -105,18 +106,103 @@ function areOverlapping(
|
||||
|
||||
const URL_PARTS_REGEX = /(?<protocolPlusDomain>.*?\..*?)(?<pathname>\/.*)/;
|
||||
|
||||
// List of common multi-level TLDs (public suffixes)
|
||||
// Covers 95%+ of real-world domains for telemetry privacy purposes
|
||||
const MULTI_LEVEL_TLDS = new Set([
|
||||
// UK
|
||||
'co.uk',
|
||||
'gov.uk',
|
||||
'org.uk',
|
||||
'ac.uk',
|
||||
'sch.uk',
|
||||
// Australia
|
||||
'com.au',
|
||||
'gov.au',
|
||||
'edu.au',
|
||||
'org.au',
|
||||
'net.au',
|
||||
// New Zealand
|
||||
'co.nz',
|
||||
'org.nz',
|
||||
'net.nz',
|
||||
'govt.nz',
|
||||
'ac.nz',
|
||||
// Japan
|
||||
'co.jp',
|
||||
'or.jp',
|
||||
'ne.jp',
|
||||
'ac.jp',
|
||||
'go.jp',
|
||||
// Brazil
|
||||
'com.br',
|
||||
'gov.br',
|
||||
'org.br',
|
||||
'edu.br',
|
||||
// India
|
||||
'co.in',
|
||||
'org.in',
|
||||
'net.in',
|
||||
'gov.in',
|
||||
'edu.in',
|
||||
// South Africa
|
||||
'co.za',
|
||||
'org.za',
|
||||
'gov.za',
|
||||
'ac.za',
|
||||
// AWS S3 (special case for cloud services)
|
||||
's3.amazonaws.com',
|
||||
]);
|
||||
|
||||
export function getDomainBase(raw: string, urlParts = URL_PARTS_REGEX): string {
|
||||
let hostname: string;
|
||||
|
||||
try {
|
||||
const url = new URL(raw);
|
||||
|
||||
return [url.protocol, url.hostname].join('//');
|
||||
hostname = url.hostname;
|
||||
} catch {
|
||||
const match = urlParts.exec(raw);
|
||||
|
||||
if (!match?.groups?.protocolPlusDomain) return '';
|
||||
|
||||
return match.groups.protocolPlusDomain;
|
||||
// Extract hostname from malformed URL
|
||||
hostname = match.groups.protocolPlusDomain.replace(/^https?:\/\//, '');
|
||||
}
|
||||
|
||||
// Handle edge cases
|
||||
if (!hostname || hostname === 'localhost') return hostname;
|
||||
|
||||
// Handle IP addresses (v4 and v6) - return as-is
|
||||
if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || hostname.includes(':')) {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
const parts = hostname.split('.');
|
||||
|
||||
// Single-word domain (e.g., "localhost", "example")
|
||||
if (parts.length === 1) {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// Check for multi-level TLDs (e.g., co.uk, com.au)
|
||||
if (parts.length >= 3) {
|
||||
const lastTwoParts = `${parts[parts.length - 2]}.${parts[parts.length - 1]}`;
|
||||
|
||||
if (MULTI_LEVEL_TLDS.has(lastTwoParts)) {
|
||||
// Return last 3 parts for multi-level TLD
|
||||
return parts.slice(-3).join('.');
|
||||
}
|
||||
|
||||
// Check for 3-level TLDs (e.g., s3.amazonaws.com)
|
||||
if (parts.length >= 4) {
|
||||
const lastThreeParts = `${parts[parts.length - 3]}.${parts[parts.length - 2]}.${parts[parts.length - 1]}`;
|
||||
if (MULTI_LEVEL_TLDS.has(lastThreeParts)) {
|
||||
// Return last 4 parts for 3-level TLD
|
||||
return parts.slice(-4).join('.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default: return last 2 parts (standard TLD like .com, .org, .net)
|
||||
return parts.slice(-2).join('.');
|
||||
}
|
||||
|
||||
function isSensitive(segment: string) {
|
||||
@@ -308,7 +394,6 @@ export function generateNodesGraph(
|
||||
const { url } = node.parameters as { url: string };
|
||||
|
||||
nodeItem.domain_base = getDomainBase(url);
|
||||
nodeItem.domain_path = getDomainPath(url);
|
||||
nodeItem.method = node.parameters.requestMethod as string;
|
||||
} else if (HTTP_REQUEST_TOOL_LANGCHAIN_NODE_TYPE === node.type) {
|
||||
if (!nodeItem.toolSettings) nodeItem.toolSettings = {};
|
||||
|
||||
@@ -50,6 +50,33 @@ describe('getDomainBase should return protocol plus domain', () => {
|
||||
expect(getDomainBase(full)).toBe(protocolPlusDomain);
|
||||
}
|
||||
});
|
||||
|
||||
test('should handle multi-level TLDs correctly', () => {
|
||||
// Test cases for multi-level TLDs (country codes, special services)
|
||||
const testCases = [
|
||||
{ input: 'https://api.example.co.uk/path', expected: 'example.co.uk' },
|
||||
{ input: 'https://user.service.com.au/api', expected: 'service.com.au' },
|
||||
{ input: 'https://api.example.co.jp/test', expected: 'example.co.jp' },
|
||||
{ input: 'https://test.example.gov.uk/data', expected: 'example.gov.uk' },
|
||||
{ input: 'https://sub.domain.org.nz/path', expected: 'domain.org.nz' },
|
||||
{ input: 'api.example.co.uk/path', expected: 'example.co.uk' },
|
||||
];
|
||||
|
||||
testCases.forEach(({ input, expected }) => {
|
||||
expect(getDomainBase(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle edge cases', () => {
|
||||
// Single word domains, localhost, etc.
|
||||
expect(getDomainBase('https://localhost/path')).toBe('localhost');
|
||||
expect(getDomainBase('https://example/path')).toBe('example');
|
||||
});
|
||||
|
||||
test('should handle IP addresses', () => {
|
||||
expect(getDomainBase('https://192.168.1.1/path')).toBe('192.168.1.1');
|
||||
expect(getDomainBase('https://[::1]/path')).toBe('[::1]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDomainPath should return pathname, excluding query string', () => {
|
||||
@@ -941,7 +968,6 @@ describe('generateNodesGraph', () => {
|
||||
credential_type: 'httpBasicAuth',
|
||||
credential_set: true,
|
||||
domain_base: 'google.com',
|
||||
domain_path: '/path/test',
|
||||
},
|
||||
},
|
||||
notes: {},
|
||||
@@ -996,7 +1022,6 @@ describe('generateNodesGraph', () => {
|
||||
credential_type: 'activeCampaignApi',
|
||||
credential_set: true,
|
||||
domain_base: 'google.com',
|
||||
domain_path: '/path/test',
|
||||
},
|
||||
},
|
||||
notes: {},
|
||||
@@ -1201,7 +1226,6 @@ describe('generateNodesGraph', () => {
|
||||
position: [600, 240],
|
||||
credential_set: false,
|
||||
domain_base: '',
|
||||
domain_path: '',
|
||||
},
|
||||
},
|
||||
notes: {},
|
||||
@@ -2268,42 +2292,42 @@ function validUrls(idMaker: typeof alphanumericId | typeof email, char = CHAR) {
|
||||
return [
|
||||
{
|
||||
full: `https://test.com/api/v1/users/${firstId}`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}`,
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users/${firstId}/`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}/`,
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users/${firstId}/posts/${secondId}`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}/posts/${secondIdObscured}`,
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users/${firstId}/posts/${secondId}/`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}/posts/${secondIdObscured}/`,
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users/${firstId}/posts/${secondId}/`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}/posts/${secondIdObscured}/`,
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users?id=${firstId}`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: '/api/v1/users',
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users?id=${firstId}&post=${secondId}`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: '/api/v1/users',
|
||||
},
|
||||
{
|
||||
full: `https://test.com/api/v1/users/${firstId}/posts/${secondId}`,
|
||||
protocolPlusDomain: 'https://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}/posts/${secondIdObscured}`,
|
||||
},
|
||||
];
|
||||
@@ -2323,7 +2347,7 @@ function malformedUrls(idMaker: typeof numericId | typeof email, char = CHAR) {
|
||||
},
|
||||
{
|
||||
full: `htp://test.com/api/v1/users/${firstId}/posts/${secondId}/`,
|
||||
protocolPlusDomain: 'htp://test.com',
|
||||
protocolPlusDomain: 'test.com',
|
||||
pathname: `/api/v1/users/${firstIdObscured}/posts/${secondIdObscured}/`,
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user