LibreChat/api/server/services/Config/rum.js
Danny Avila 444d923e29
✂️ chore: Strip Session JWT Forwarding from Browser RUM (#13414)
* fix: disable RUM user JWT auth

* fix: remove stale RUM bootstrap import
2026-05-30 10:34:44 -04:00

139 lines
3.7 KiB
JavaScript

const { isEnabled } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const DEFAULT_RUM_SERVICE_NAME = 'librechat-web';
function parseBooleanEnv(value, defaultValue = false) {
if (value == null || value === '') {
return defaultValue;
}
return isEnabled(value);
}
function parseNumberEnv(value) {
if (value == null || value === '') {
return undefined;
}
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : undefined;
}
function parseCsvEnv(value) {
if (!value) {
return [];
}
return value
.split(',')
.map((item) => item.trim())
.filter(Boolean);
}
function parseUrl(value) {
try {
return new URL(value);
} catch {
return undefined;
}
}
function isLocalhost(url) {
return url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '[::1]';
}
function isSafeRumUrl(url) {
if (url.username || url.password) {
return false;
}
if (url.protocol === 'https:') {
return true;
}
return url.protocol === 'http:' && isLocalhost(url);
}
function isSafeTraceTarget(target) {
if (target.includes('*')) {
return false;
}
const url = parseUrl(target);
if (!url || url.protocol !== 'https:') {
return false;
}
return true;
}
function getRumConfig() {
if (!parseBooleanEnv(process.env.RUM_ENABLED)) {
return undefined;
}
const provider = process.env.RUM_PROVIDER || 'hyperdx';
if (provider !== 'hyperdx') {
logger.warn(`[config] Unsupported RUM provider "${provider}", disabling RUM`);
return undefined;
}
const authMode = process.env.RUM_AUTH_MODE || 'publicToken';
if (authMode !== 'publicToken') {
logger.warn(`[config] Unsupported RUM auth mode "${authMode}", disabling RUM`);
return undefined;
}
const rumUrl = process.env.RUM_URL;
const parsedUrl = rumUrl ? parseUrl(rumUrl) : undefined;
if (!parsedUrl || !isSafeRumUrl(parsedUrl)) {
logger.warn('[config] Invalid RUM_URL, disabling RUM');
return undefined;
}
if (!process.env.RUM_PUBLIC_TOKEN) {
logger.warn('[config] RUM publicToken mode requires RUM_PUBLIC_TOKEN, disabling RUM');
return undefined;
}
const rawTracePropagationTargets = parseCsvEnv(process.env.RUM_TRACE_PROPAGATION_TARGETS);
const tracePropagationTargets = rawTracePropagationTargets.filter(isSafeTraceTarget);
if (rawTracePropagationTargets.length !== tracePropagationTargets.length) {
logger.info('[config] Ignored unsafe RUM trace propagation targets');
}
const configuredSampleRate = parseNumberEnv(process.env.RUM_SAMPLE_RATE);
const sampleRate =
configuredSampleRate != null && configuredSampleRate >= 0 && configuredSampleRate <= 1
? configuredSampleRate
: undefined;
const consoleCapture = parseBooleanEnv(process.env.RUM_CONSOLE_CAPTURE);
const advancedNetworkCapture = parseBooleanEnv(process.env.RUM_ADVANCED_NETWORK_CAPTURE);
if (consoleCapture) {
logger.warn('[config] RUM console capture is enabled and may collect sensitive browser logs');
}
if (advancedNetworkCapture) {
logger.warn('[config] RUM advanced network capture is enabled and may collect payload data');
}
return {
provider: 'hyperdx',
enabled: true,
url: parsedUrl.href.replace(/\/$/, ''),
serviceName: process.env.RUM_SERVICE_NAME || DEFAULT_RUM_SERVICE_NAME,
authMode,
publicToken: process.env.RUM_PUBLIC_TOKEN,
...(tracePropagationTargets.length > 0 ? { tracePropagationTargets } : {}),
consoleCapture,
disableReplay: parseBooleanEnv(process.env.RUM_DISABLE_REPLAY, true),
advancedNetworkCapture,
...(sampleRate != null ? { sampleRate } : {}),
...(process.env.RUM_ENVIRONMENT ? { environment: process.env.RUM_ENVIRONMENT } : {}),
};
}
module.exports = { getRumConfig };