From ddbf05524b77d2c8b158755eaa36e9d9a5fab9fd Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 16 Aug 2026 16:09:15 +0200 Subject: [PATCH] feat(http): set custom User-Agent on outbound integration requests (#6606) * feat(http): set custom User-Agent on outbound integration requests Set a default 'User-Agent: Homarr/ (+https://homarr.dev)' header on all outbound HTTP requests made via fetchWithTrustedCertificatesAsync, so Homarr's integration traffic is distinguishable in reverse-proxy, WAF, and security-monitoring logs instead of appearing as generic undici. Closes #6604 * refactor(version): centralize Homarr version in @homarr/version package Replace fragile deep-relative 'package.json' imports across the monorepo with a single source of truth: the new @homarr/version leaf package reads the version once from the root package.json and exports it as VERSION. Consumers now import by package name incl. the new User-Agent header in fetchWithTrustedCertificatesAsync (packages/core): - core, api (info router), analytics, request-handler (update checker) This also removes all manually-maintained relative version imports. * fix(http): normalize User-Agent injection with Headers Object-spreading fetchOptions.headers breaks when callers pass a Headers instance (spread yields no entries) or a tuple array (numeric keys), and is case-sensitive. Build a Headers instance and set the default User-Agent only when no case-insensitive User-Agent entry exists, via a shared helper in both fetch paths. Adds unit tests covering record, Headers, tuple-array, and casing variants. * revert(version): undo version refactor and tests, keep single-file change Keep the User-Agent change as a single edit to packages/core/src/infrastructure/http/request.ts. Remove the @homarr/version package, the consumer migrations, and the unit tests. --- packages/core/src/infrastructure/http/request.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/core/src/infrastructure/http/request.ts b/packages/core/src/infrastructure/http/request.ts index b3c3ea354..20af8232f 100644 --- a/packages/core/src/infrastructure/http/request.ts +++ b/packages/core/src/infrastructure/http/request.ts @@ -14,6 +14,10 @@ import { UndiciHttpAgent } from "@homarr/core/infrastructure/http"; import type { TrustedCertificateHostname } from "../certificates/hostnames"; import { withTimeoutAsync } from "./timeout"; +import packageJson from "../../../../../package.json"; + +export const getDefaultUserAgent = () => `Homarr/${packageJson.version} (+https://homarr.dev)`; + export const createCustomCheckServerIdentity = ( trustedHostnames: TrustedCertificateHostname[], ): typeof checkServerIdentity => { @@ -79,6 +83,10 @@ export const fetchWithTrustedCertificatesAsync = async ( async (signal) => fetch(url, { ...fetchOptions, + headers: { + "User-Agent": getDefaultUserAgent(), + ...fetchOptions.headers, + }, signal, dispatcher: agent, }), @@ -89,6 +97,10 @@ export const fetchWithTrustedCertificatesAsync = async ( const { bodyTimeout: _bodyTimeout, dispatcher: _dispatcher, ...fetchOptions } = options ?? {}; return fetch(url, { ...fetchOptions, + headers: { + "User-Agent": getDefaultUserAgent(), + ...fetchOptions.headers, + }, dispatcher: agent, }); };