Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion app/src/components/single-package/SinglePackageTrustSignals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ function AuditCard({ item }: { item: DisplayedAttestation }) {
* broken-image icon). */
function AuditBadge({ item }: { item: DisplayedAttestation }) {
const [broken, setBroken] = useState(false);
const src = httpsLink(item.info.display["image_url"]);
const src = imageSource(item.info.display["image_url"]);
if (!src || broken) {
return <AttesterAvatar attestor={item.attestor} />;
}
Expand Down Expand Up @@ -385,6 +385,23 @@ export function httpsLink(v: unknown): string | undefined {
return s && s.startsWith("https://") ? s : undefined;
}

/** An `image_url` value we'll put in an `<img src>`: an `https` URL, or an
* inline `data:image/svg+xml` URI (a badge a schema derives from the
* attestation's own data). Safe because it renders in an `<img>`, where a
* `<script>` or `onload` inside the SVG does not execute — the value must never
* be inlined into the DOM. Distinct from `httpsLink` (used for the report
* `link`), which stays https-only: a `data:` link target isn't wanted. Anything
* else — `http`, other `data:` types — is rejected. */
function imageSource(v: unknown): string | undefined {
const s = str(v);
if (!s) return undefined;
if (s.startsWith("https://")) return s;
if (s.startsWith("data:image/svg+xml,") || s.startsWith("data:image/svg+xml;")) {
return s;
}
return undefined;
}

function hostOf(url: string): string {
try {
return new URL(url).host;
Expand Down