@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
69 lines (68 loc) • 3.15 kB
JavaScript
import { X509Error } from "../X509Error.mjs";
import { fetchWithTimeout } from "../../../utils/fetch.mjs";
import { CacheModuleConfig } from "../../cache/CacheModuleConfig.mjs";
import "../../cache/index.mjs";
import { isX509CrlSummary } from "./crlSummary.mjs";
//#region src/modules/x509/utils/crlFetcher.ts
/**
* Default maximum CRL size in bytes (10 MB)
* This is a reasonable limit as most CRLs are much smaller.
* Very large CRLs (>10 MB) are uncommon and may indicate issues.
*/
const DEFAULT_MAX_CRL_SIZE_BYTES = 10485760;
/**
* Default CRL fetch timeout in milliseconds (5 seconds)
*/
const DEFAULT_TIMEOUT_MILLISECONDS = 5e3;
function crlSummaryCacheKey(url) {
return `x509:crl-summary:v1:${url}`;
}
/**
* Fetches a CRL from a given URL with size limit and timeout enforcement.
*
* This does NOT cache: caching is the responsibility of the caller, which should only cache a CRL
* after it has been verified (see {@link setCachedCrlSummary}), so unverified or expired CRLs are
* never persisted.
*
* @throws {X509Error} If the fetch fails, returns a non-OK status, or exceeds the size limit
*/
async function fetchCrl(options) {
const { url, timeoutMs = DEFAULT_TIMEOUT_MILLISECONDS, agentContext, maxSizeBytes = DEFAULT_MAX_CRL_SIZE_BYTES } = options;
try {
const response = await fetchWithTimeout(agentContext.config.agentDependencies.fetch, url, {
timeoutMs,
maxResponseBytes: maxSizeBytes
});
if (!response.ok) throw new X509Error(`Failed to fetch CRL from ${url}: HTTP ${response.status} ${response.statusText}`);
return new Uint8Array(await response.arrayBuffer());
} catch (error) {
if (error instanceof X509Error) throw error;
throw new X509Error(`Failed to fetch CRL from ${url}: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : void 0 });
}
}
/**
* Return the cached verified CRL summary for a URL, or `null` if not cached. Values that do not
* match the expected summary shape are treated as a cache miss, never as an error.
*
* Summaries are cached in the global scope: CRLs are public data, shared across all agent contexts.
*/
async function getCachedCrlSummary(agentContext, url) {
const cached = await agentContext.resolve(CacheModuleConfig).cache.get(agentContext, crlSummaryCacheKey(url), { scope: "global" }).catch(() => null);
return isX509CrlSummary(cached) ? cached : null;
}
/**
* Cache the summary of a verified CRL for a URL.
*
* Callers must only cache summaries derived from CRLs that have passed verification, and should
* derive `expiresInSeconds` from the CRL's `nextUpdate` so an expired CRL is never served from the
* cache.
*
* Summaries are cached in the global scope: CRLs are public data, shared across all agent contexts.
*/
async function setCachedCrlSummary(agentContext, url, summary, expiresInSeconds) {
if (expiresInSeconds <= 0) return;
await agentContext.resolve(CacheModuleConfig).cache.set(agentContext, crlSummaryCacheKey(url), summary, expiresInSeconds, { scope: "global" }).catch(() => null);
}
//#endregion
export { fetchCrl, getCachedCrlSummary, setCachedCrlSummary };
//# sourceMappingURL=crlFetcher.mjs.map