@credo-ts/core
Version:
<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1
1 lines • 5.16 kB
Source Map (JSON)
{"version":3,"file":"crlFetcher.mjs","names":[],"sources":["../../../../src/modules/x509/utils/crlFetcher.ts"],"sourcesContent":["import type { AgentContext } from '../../../agent'\nimport { fetchWithTimeout } from '../../../utils/fetch'\nimport { CacheModuleConfig } from '../../cache'\nimport { X509Error } from '../X509Error'\nimport { isX509CrlSummary, type X509CrlSummary } from './crlSummary'\n\nexport interface CrlFetchOptions {\n url: string\n agentContext: AgentContext\n\n /**\n * Maximum CRL fetch time after which request will be aborted\n * @default 5000 (5 seconds)\n */\n timeoutMs?: number\n\n /**\n * Maximum size in bytes for CRL downloads\n * @default 10485760 (10 MB)\n */\n maxSizeBytes?: number\n}\n\n/**\n * Default maximum CRL size in bytes (10 MB)\n * This is a reasonable limit as most CRLs are much smaller.\n * Very large CRLs (>10 MB) are uncommon and may indicate issues.\n */\nconst DEFAULT_MAX_CRL_SIZE_BYTES = 10 * 1024 * 1024 // 10 MB\n\n/**\n * Default CRL fetch timeout in milliseconds (5 seconds)\n */\nconst DEFAULT_TIMEOUT_MILLISECONDS = 5000\n\n// Versioned so future summary shape changes can bump the key. Distinct from the legacy\n// `x509:crl:${url}` raw-bytes key, which this version no longer reads or writes but which older\n// Credo versions sharing the cache may still use.\nfunction crlSummaryCacheKey(url: string): string {\n return `x509:crl-summary:v1:${url}`\n}\n\n/**\n * Fetches a CRL from a given URL with size limit and timeout enforcement.\n *\n * This does NOT cache: caching is the responsibility of the caller, which should only cache a CRL\n * after it has been verified (see {@link setCachedCrlSummary}), so unverified or expired CRLs are\n * never persisted.\n *\n * @throws {X509Error} If the fetch fails, returns a non-OK status, or exceeds the size limit\n */\nexport async function fetchCrl(options: CrlFetchOptions): Promise<Uint8Array> {\n const {\n url,\n timeoutMs = DEFAULT_TIMEOUT_MILLISECONDS,\n agentContext,\n maxSizeBytes = DEFAULT_MAX_CRL_SIZE_BYTES,\n } = options\n\n try {\n const response = await fetchWithTimeout(agentContext.config.agentDependencies.fetch, url, {\n timeoutMs,\n maxResponseBytes: maxSizeBytes,\n })\n\n if (!response.ok) {\n throw new X509Error(`Failed to fetch CRL from ${url}: HTTP ${response.status} ${response.statusText}`)\n }\n\n return new Uint8Array(await response.arrayBuffer())\n } catch (error) {\n if (error instanceof X509Error) {\n throw error\n }\n throw new X509Error(`Failed to fetch CRL from ${url}: ${error instanceof Error ? error.message : String(error)}`, {\n cause: error instanceof Error ? error : undefined,\n })\n }\n}\n\n/**\n * Return the cached verified CRL summary for a URL, or `null` if not cached. Values that do not\n * match the expected summary shape are treated as a cache miss, never as an error.\n *\n * Summaries are cached in the global scope: CRLs are public data, shared across all agent contexts.\n */\nexport async function getCachedCrlSummary(agentContext: AgentContext, url: string): Promise<X509CrlSummary | null> {\n const cache = agentContext.resolve(CacheModuleConfig).cache\n const cached = await cache.get<unknown>(agentContext, crlSummaryCacheKey(url), { scope: 'global' }).catch(() => null)\n return isX509CrlSummary(cached) ? cached : null\n}\n\n/**\n * Cache the summary of a verified CRL for a URL.\n *\n * Callers must only cache summaries derived from CRLs that have passed verification, and should\n * derive `expiresInSeconds` from the CRL's `nextUpdate` so an expired CRL is never served from the\n * cache.\n *\n * Summaries are cached in the global scope: CRLs are public data, shared across all agent contexts.\n */\nexport async function setCachedCrlSummary(\n agentContext: AgentContext,\n url: string,\n summary: X509CrlSummary,\n expiresInSeconds: number\n): Promise<void> {\n if (expiresInSeconds <= 0) return\n const cache = agentContext.resolve(CacheModuleConfig).cache\n await cache\n .set(agentContext, crlSummaryCacheKey(url), summary, expiresInSeconds, { scope: 'global' })\n .catch(() => null)\n}\n"],"mappings":";;;;;;;;;;;AA4BA,MAAM,6BAA6B;;;;AAKnC,MAAM,+BAA+B;AAKrC,SAAS,mBAAmB,KAAqB;CAC/C,OAAO,uBAAuB;AAChC;;;;;;;;;;AAWA,eAAsB,SAAS,SAA+C;CAC5E,MAAM,EACJ,KACA,YAAY,8BACZ,cACA,eAAe,+BACb;CAEJ,IAAI;EACF,MAAM,WAAW,MAAM,iBAAiB,aAAa,OAAO,kBAAkB,OAAO,KAAK;GACxF;GACA,kBAAkB;EACpB,CAAC;EAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,UAAU,4BAA4B,IAAI,SAAS,SAAS,OAAO,GAAG,SAAS,YAAY;EAGvG,OAAO,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC;CACpD,SAAS,OAAO;EACd,IAAI,iBAAiB,WACnB,MAAM;EAER,MAAM,IAAI,UAAU,4BAA4B,IAAI,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAAK,EAChH,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;AACF;;;;;;;AAQA,eAAsB,oBAAoB,cAA4B,KAA6C;CAEjH,MAAM,SAAS,MADD,aAAa,QAAQ,iBAAiB,CAAC,CAAC,MAC3B,IAAa,cAAc,mBAAmB,GAAG,GAAG,EAAE,OAAO,SAAS,CAAC,CAAC,CAAC,YAAY,IAAI;CACpH,OAAO,iBAAiB,MAAM,IAAI,SAAS;AAC7C;;;;;;;;;;AAWA,eAAsB,oBACpB,cACA,KACA,SACA,kBACe;CACf,IAAI,oBAAoB,GAAG;CAE3B,MADc,aAAa,QAAQ,iBAAiB,CAAC,CAAC,MAEnD,IAAI,cAAc,mBAAmB,GAAG,GAAG,SAAS,kBAAkB,EAAE,OAAO,SAAS,CAAC,CAAC,CAC1F,YAAY,IAAI;AACrB"}