snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
163
packages/server-fetch/src/helpers.ts
Normal file
163
packages/server-fetch/src/helpers.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { lookup } from 'node:dns';
|
||||
import net from 'node:net';
|
||||
|
||||
import { domainPattern, ipv4Ranges, ipv4WithPortPattern, ipv6Ranges } from './constants';
|
||||
|
||||
export const isValidDomain = (domain: string) => domainPattern.test(domain);
|
||||
|
||||
export const unwrapBrackets = (address: string) => (address.startsWith('[') && address.endsWith(']') ? address.slice(1, -1) : address);
|
||||
|
||||
export const isIpValid = (ip: string): boolean => net.isIP(unwrapBrackets(ip)) !== 0;
|
||||
|
||||
export const isIpInCidrRange = (ip: string, cidr: string): boolean => {
|
||||
const [network, prefixStr] = cidr.split('/');
|
||||
const ipUnwrapped = unwrapBrackets(ip);
|
||||
if (net.isIPv4(ipUnwrapped)) {
|
||||
const prefix = prefixStr ? parseInt(prefixStr, 10) : 32;
|
||||
if (!network || !net.isIPv4(network)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const toNum = (ipString: string) =>
|
||||
ipString.split('.').reduce((accumulated, octet) => (accumulated << 8) + parseInt(octet, 10), 0) >>> 0;
|
||||
const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
|
||||
return (toNum(ipUnwrapped) & mask) === (toNum(network) & mask);
|
||||
}
|
||||
if (net.isIPv6(ipUnwrapped)) {
|
||||
const prefix = prefixStr ? parseInt(prefixStr, 10) : 128;
|
||||
if (!network || !net.isIPv6(unwrapBrackets(network))) {
|
||||
return false;
|
||||
}
|
||||
const toBigInt = (address: string): bigint => {
|
||||
const parts = unwrapBrackets(address).split(':');
|
||||
const nonEmptyParts = parts.filter((part) => part.length > 0);
|
||||
const fill = 8 - nonEmptyParts.reduce((count, part) => count + (part.includes('.') ? 2 : 1), 0);
|
||||
const groups: string[] = [];
|
||||
let filled = false;
|
||||
for (const part of parts) {
|
||||
if (part === '') {
|
||||
if (!filled) {
|
||||
for (let index = 0; index < fill; index++) groups.push('0');
|
||||
filled = true;
|
||||
}
|
||||
} else if (part.includes('.')) {
|
||||
if (!net.isIPv4(part)) {
|
||||
return 0n;
|
||||
}
|
||||
const num = part.split('.').reduce((accumulated, octet) => (accumulated << 8) + parseInt(octet, 10), 0) >>> 0;
|
||||
groups.push((num >>> 16).toString(16), (num & 0xffff).toString(16));
|
||||
} else {
|
||||
groups.push(part);
|
||||
}
|
||||
}
|
||||
return groups.reduce((value, hexGroup) => (value << 16n) + BigInt(parseInt(hexGroup, 16)), 0n);
|
||||
};
|
||||
const mask = prefix === 0 ? 0n : ((1n << BigInt(prefix)) - 1n) << (128n - BigInt(prefix));
|
||||
return (toBigInt(ip) & mask) === (toBigInt(network) & mask);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const isIpInAnyRange = (ip: string): boolean => {
|
||||
const ranges = ip.includes(':') ? ipv6Ranges : ipv4Ranges;
|
||||
return ranges.some((range) => isIpInCidrRange(ip, range));
|
||||
};
|
||||
|
||||
export const normalizeAllowlistEntry = (entry: string): string => {
|
||||
const trimmed = entry.trim();
|
||||
if (!trimmed) {
|
||||
return '';
|
||||
}
|
||||
if (isValidDomain(trimmed)) {
|
||||
return trimmed.toLowerCase();
|
||||
}
|
||||
if (trimmed.startsWith('[')) {
|
||||
return trimmed;
|
||||
}
|
||||
if (trimmed.includes(':') && net.isIPv6(trimmed)) {
|
||||
return `[${trimmed}]`;
|
||||
}
|
||||
return trimmed.toLowerCase();
|
||||
};
|
||||
|
||||
export const normalizeHostForAllowlistMatch = (hostOrIp: string): string => {
|
||||
if (hostOrIp.startsWith('[')) {
|
||||
return hostOrIp;
|
||||
}
|
||||
if (hostOrIp.includes(':') && net.isIPv6(hostOrIp)) {
|
||||
return `[${hostOrIp}]`;
|
||||
}
|
||||
return hostOrIp.toLowerCase();
|
||||
};
|
||||
|
||||
export const parseIpv4WithPort = (input: string): { ip: string; port?: string } | null => {
|
||||
const match = input.match(ipv4WithPortPattern);
|
||||
if (match) {
|
||||
return { ip: match[1]!, port: match[2] };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const allowlistedIpResolved = (ipOrDomain: string, port: string | undefined, wasUrlParsed: boolean): string => {
|
||||
if (wasUrlParsed || !port) {
|
||||
return ipOrDomain;
|
||||
}
|
||||
if (ipOrDomain.includes(':')) {
|
||||
return `[${ipOrDomain}]:${port}`;
|
||||
}
|
||||
return `${ipOrDomain}:${port}`;
|
||||
};
|
||||
|
||||
export function nslookup(hostname: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
lookup(hostname, (err, address) => {
|
||||
if (err) reject(err);
|
||||
else resolve(address);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function checkDirectIp(ip: string): boolean {
|
||||
return /^(\d+\.\d+\.\d+\.\d+|\[?[0-9a-fA-F:]+]?)$/.test(ip);
|
||||
}
|
||||
|
||||
export function extractHostname(urlString: string): string | null {
|
||||
try {
|
||||
const { hostname } = new URL(urlString);
|
||||
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
||||
return hostname.slice(1, -1);
|
||||
}
|
||||
return hostname;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildPinnedUrl(originalUrl: string, resolvedIp: string): string {
|
||||
try {
|
||||
const url = new URL(originalUrl);
|
||||
|
||||
let ipAddress: string;
|
||||
|
||||
const ipv4WithPortMatch = resolvedIp.match(/^(\d+\.\d+\.\d+\.\d+)(?::(\d+))$/);
|
||||
if (ipv4WithPortMatch) {
|
||||
ipAddress = ipv4WithPortMatch[1];
|
||||
} else if (resolvedIp.includes(':') && !resolvedIp.includes('.')) {
|
||||
const ipv6WithPortMatch = resolvedIp.match(/^(\[[0-9a-fA-F:]+\])(?::(\d+))$/);
|
||||
if (ipv6WithPortMatch) {
|
||||
ipAddress = ipv6WithPortMatch[1];
|
||||
} else {
|
||||
ipAddress = resolvedIp.startsWith('[') && resolvedIp.endsWith(']') ? resolvedIp : `[${resolvedIp}]`;
|
||||
}
|
||||
} else {
|
||||
// IPv4 without port
|
||||
ipAddress = resolvedIp;
|
||||
}
|
||||
|
||||
url.hostname = ipAddress;
|
||||
|
||||
return url.toString();
|
||||
} catch {
|
||||
return originalUrl;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user