/** * Registry artifact proxy * * GET /_emdash/api/admin/plugins/registry/artifact?did=&slug=&version=&kind=&index= * * Proxies an icon / screenshot / banner image referenced by a registry * release record so the admin UI can display it without cross-origin * requests to arbitrary publisher hosting. * * Trust model (CRITICAL): the proxy never accepts an artifact URL from the * client. The caller addresses an artifact by its coordinates * `(did, slug, version, kind, index)`; the server resolves the *declared* * URL from the validated release record fetched from the configured * aggregator. The proxy can therefore only ever fetch a URL the publisher * declared in their signed release — not an arbitrary caller-supplied URL. * * The publisher-declared URL is still untrusted (an attacker who controls a * publisher record, or the aggregator, can point it anywhere), so the * resolved URL passes through the SSRF defences (`assertSafeArtifactUrl`, * re-validated on every redirect hop) before any fetch, and only allowlisted * image content types are served back. */ import type { Did } from "@atcute/lexicons"; import { evaluateRegistryReleaseWithdrawal } from "@premium-cms/registry-client/withdrawal"; import type { APIRoute } from "astro"; import { requirePerm } from "#api/authorize.js"; import { apiError } from "#api/error.js"; import { verifyChecksum } from "#api/handlers/registry.js"; import { assertSafeArtifactUrl } from "#api/index.js"; import { fetchRegistryArtifactUrl } from "../../../../../../registry/artifact-fetch.js"; import { coerceRegistryConfig, validateAggregatorUrl } from "../../../../../../registry/config.js"; export const prerender = false; /** * Image content types the proxy will pass through. Anything else is rejected. * * SVG is deliberately excluded: it is active content (an `