mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 13:16:16 -08:00
* Add scoped EPG security trust controls * fix: address scoped trust review feedback --------- Co-authored-by: 4gray <fourgray@proton.me>
62 lines
2.0 KiB
TypeScript
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),
|
|
}),
|
|
};
|
|
}
|