Files
iptvnator/apps/electron-backend/src/app/util/secure-https.ts
T
4grayand4gray e8aa7c3a34 [codex] Add scoped EPG security trust controls (#1054)
* Add scoped EPG security trust controls

* fix: address scoped trust review feedback

---------

Co-authored-by: 4gray <fourgray@proton.me>
2026-06-12 20:46:24 +02:00

62 lines
2.0 KiB
TypeScript

import { Agent } from 'node:https';
import type { LookupFunction } from 'node:net';
import { normalizeHost } from '@iptvnator/shared/interfaces';
import type { ValidatedRequestAgentFactory } from './validated-axios';
const INSECURE_TLS_ENV = 'IPTVNATOR_ALLOW_INSECURE_TLS';
/**
* Whether TLS certificate validation is disabled for remote playlist fetches.
*
* Secure by default. Validation is only disabled when the operator explicitly
* opts in via `IPTVNATOR_ALLOW_INSECURE_TLS=1` (or `=true`). Some IPTV
* providers serve self-signed or otherwise invalid certificates, so the
* opt-out exists — but it must never be the default, otherwise every playlist
* fetch is silently exposed to network MITM.
*/
export function isInsecureTlsAllowed(): boolean {
const value = process.env[INSECURE_TLS_ENV]?.trim().toLowerCase();
return value === '1' || value === 'true';
}
function shouldTrustInvalidCertificateForHost(
url: URL | undefined,
trustedHosts: readonly string[]
): boolean {
if (!url) {
return false;
}
const host = normalizeHost(url.hostname);
return trustedHosts.some(
(trustedHost) => normalizeHost(trustedHost) === host
);
}
/**
* Builds the agent factory used for remote playlist fetches.
*
* Certificates are validated unless insecure TLS has been explicitly opted in
* (see {@link isInsecureTlsAllowed}). The validated request layer supplies a
* DNS lookup pinned to the addresses approved for each redirect hop.
*/
export function createPlaylistAgentFactory(
options: {
trustedInsecureTlsHosts?: readonly string[];
} = {}
): ValidatedRequestAgentFactory & {
createHttpsAgent(lookup?: LookupFunction, url?: URL): Agent;
} {
const trustedHosts = options.trustedInsecureTlsHosts ?? [];
return {
createHttpsAgent: (lookup, url) =>
new Agent({
lookup,
rejectUnauthorized:
!isInsecureTlsAllowed() &&
!shouldTrustInvalidCertificateForHost(url, trustedHosts),
}),
};
}