From 3eb913149449db8af60b63f5b153370276bc97a7 Mon Sep 17 00:00:00 2001 From: Andy Parsons Date: Thu, 30 Apr 2026 13:28:44 -0400 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20fix=20all=20ESLint=20errors=20(103?= =?UTF-8?q?=20=E2=86=92=200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unused imports and variables across 17 files - Replace `any` types with specific inline types for C2PA assertion accesses, validation status objects, and trust-annotated manifest types - Fix unused export prop in AssetInfoIssuerDate (let → const) - Fix svelte-ignore → eslint-disable-next-line for {@html} in IssuedBySection - Auto-fixed padding, prefer-const, and shorthand attribute violations Co-Authored-By: Claude Sonnet 4.6 --- .../EmbeddedIcon/EmbeddedIcon.svelte | 2 +- src/lib/asset.ts | 23 ++++-- src/lib/crjson.ts | 30 +++++-- src/lib/sdk.ts | 2 + src/lib/selectors/doNotTrain.ts | 14 +++- src/lib/selectors/editsAndActivity.ts | 18 ++-- src/lib/selectors/generativeInfo.ts | 30 +++++-- src/lib/selectors/producer.ts | 12 ++- src/lib/selectors/reviewRatings.ts | 3 +- src/lib/selectors/socialAccounts.ts | 5 +- src/lib/selectors/web3Info.ts | 4 +- src/lib/selectors/website.ts | 7 +- .../AssetInfo/AssetInfoIssuerDate.svelte | 4 +- .../AboutSection/AboutSection.svelte | 5 +- .../AboutSection/IssuedBySection.svelte | 2 + .../verify/components/TreeView/TreeL1.svelte | 1 - src/routes/verify/stores/c2paReader.ts | 82 ++++++++----------- 17 files changed, 147 insertions(+), 97 deletions(-) diff --git a/src/components/EmbeddedIcon/EmbeddedIcon.svelte b/src/components/EmbeddedIcon/EmbeddedIcon.svelte index cf93a929e..2c89a618b 100644 --- a/src/components/EmbeddedIcon/EmbeddedIcon.svelte +++ b/src/components/EmbeddedIcon/EmbeddedIcon.svelte @@ -3,7 +3,7 @@ --> diff --git a/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte b/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte index 8dc14aaa6..de003cbf5 100644 --- a/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte +++ b/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte @@ -12,7 +12,8 @@ export let trustSource: 'official' | 'legacy' | 'none' = 'none'; // Extract extended X.509 fields (Catching various Rust/JS SDK naming conventions) - $: sigInfo = manifestData.signatureInfo as any; + type ExtendedSigInfo = { organization_unit?: string; organizational_unit?: string; organizationUnit?: string; org_unit?: string; ou?: string; country?: string; country_name?: string; countryName?: string; c?: string }; + $: sigInfo = manifestData.signatureInfo as ExtendedSigInfo; $: orgUnit = sigInfo?.organization_unit || sigInfo?.organizational_unit || sigInfo?.organizationUnit || sigInfo?.org_unit || sigInfo?.ou; $: country = sigInfo?.country || sigInfo?.country_name || sigInfo?.countryName || sigInfo?.c; @@ -26,7 +27,7 @@ commonName={manifestData.signatureInfo?.common_name} issuer={manifestData.signatureInfo?.issuer} organizationalUnit={orgUnit} - country={country} + {country} {trustSource} /> {/if} diff --git a/src/routes/verify/components/DetailedInfo/AboutSection/IssuedBySection.svelte b/src/routes/verify/components/DetailedInfo/AboutSection/IssuedBySection.svelte index 21159186c..9f9d060e7 100644 --- a/src/routes/verify/components/DetailedInfo/AboutSection/IssuedBySection.svelte +++ b/src/routes/verify/components/DetailedInfo/AboutSection/IssuedBySection.svelte @@ -22,6 +22,7 @@ if (issuer) params.set('o', issuer); if (organizationalUnit) params.set('ou', organizationalUnit); if (country) params.set('c', country); + return `https://spec.c2pa.org/conformance-explorer/?${params.toString()}`; })(); @@ -71,6 +72,7 @@ {#if showTooltip} (showTooltip = !showTooltip)} >
+ {@html $_('sidebar.verify.about.issuedby.tooltip')}
diff --git a/src/routes/verify/components/TreeView/TreeL1.svelte b/src/routes/verify/components/TreeView/TreeL1.svelte index 94e0e6902..f8d110f7a 100644 --- a/src/routes/verify/components/TreeView/TreeL1.svelte +++ b/src/routes/verify/components/TreeView/TreeL1.svelte @@ -32,7 +32,6 @@ $: removeL1 = transformScale === 0.125 ? true : false; $: scale = 0.5 / transformScale; $: L1margin = transformScale >= 0.25 ? 0.5 : transformScale / 0.25; - $: trustSource = $assetStore.trustSource;
; @@ -33,32 +33,11 @@ const mimeTypeCorrections = { 'image/dng': 'image/x-adobe-dng', }; -import { legacyToCrJson, type CrJson } from '$lib/crjson'; - -// Helper to extract all manifest labels that failed trust validation in a given crJSON store -function getUntrustedManifestLabels(store: CrJson): Set { - const untrustedLabels = new Set(); - const isTrustError = (s: any) => s.code.includes('signingCredential'); - - // 1. Check Root - const rootFailures = store.validationResults?.activeManifest?.failure || []; - const activeLabel = store.manifests[0]?.label; - if (activeLabel && rootFailures.some(isTrustError)) { - untrustedLabels.add(activeLabel); - } - - // 2. Check EVERY manifest in the store (Active + Ingredients) - for (const manifest of store.manifests) { - // Check the manifest object itself - const mFailures = manifest.validationResults?.failure || []; - const mStatuses = manifest.validationStatus || []; - if (mFailures.some(isTrustError) || mStatuses.some(isTrustError)) { - untrustedLabels.add(manifest.label); - } - } - - return untrustedLabels; -} +type ValidationStatus = { code: string }; +type TrustSource = 'official' | 'legacy' | 'none'; +type TrustedIngredient = { trust_source?: TrustSource; active_manifest?: string; validation_status?: ValidationStatus[] }; +type TrustedManifest = { trust_source?: TrustSource; ingredients?: TrustedIngredient[] }; +type ValidationDelta = { ingredientAssertionURI?: string; validationDeltas?: { failure?: ValidationStatus[] } }; export function createC2paReader(): C2paReaderStore { let dispose: () => void; @@ -112,7 +91,7 @@ export function createC2paReader(): C2paReaderStore { // PASS 1: Validate against Official Trust List const officialSettings = await getOfficialToolkitSettings(); - let reader = await sdk.reader.fromBlob(source.type || 'application/octet-stream', source, officialSettings); + const reader = await sdk.reader.fromBlob(source.type || 'application/octet-stream', source, officialSettings); if (!reader) { throw new Error('No C2PA manifest found in this file'); @@ -139,18 +118,17 @@ export function createC2paReader(): C2paReaderStore { reader.free(); currentReader = legacyReader; - const isTrustError = (s: any) => s.code.includes('signingCredential.untrusted') || s.code.includes('signingCredential.invalid'); - const isCryptoValid = (s: any) => s.code.includes('signingCredential.trusted') || s.code.includes('claimSignature.validated'); + const isTrustError = (s: ValidationStatus) => s.code.includes('signingCredential.untrusted') || s.code.includes('signingCredential.invalid'); // 4a. Tag the Active Manifest - const p1ActiveV3 = rawManifestStore.validation_results?.activeManifest?.failure || []; - const p1ActiveV2 = rawManifestStore.manifests?.[rawManifestStore.active_manifest || '']?.validation_status || []; + const p1ActiveV3 = (rawManifestStore.validation_results?.activeManifest?.failure || []) as ValidationStatus[]; + const p1ActiveV2 = (rawManifestStore.manifests?.[rawManifestStore.active_manifest || '']?.validation_status || []) as ValidationStatus[]; const wasActiveUntrusted = p1ActiveV3.some(isTrustError) || p1ActiveV2.some(isTrustError); const isFinalTrusted = finalStore.validation_state === 'Trusted'; if (finalStore.manifests && finalStore.active_manifest && finalStore.manifests[finalStore.active_manifest]) { - const activeMan = finalStore.manifests[finalStore.active_manifest]; - + const activeMan = finalStore.manifests[finalStore.active_manifest] as unknown as TrustedManifest; + // If the root is Trusted, default to official, then downgrade if Pass 1 failed. if (isFinalTrusted) { activeMan.trust_source = wasActiveUntrusted ? 'legacy' : 'official'; @@ -160,32 +138,32 @@ export function createC2paReader(): C2paReaderStore { } // 4b. Tag ALL Ingredients across the entire provenance tree - const p1Deltas = rawManifestStore.validation_results?.ingredientDeltas || []; - - Object.entries(finalStore.manifests || {}).forEach(([label, manifest]: [string, any]) => { + const p1Deltas = (rawManifestStore.validation_results?.ingredientDeltas || []) as ValidationDelta[]; + + (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { const p1Manifest = rawManifestStore.manifests?.[label]; - + if (manifest.ingredients && p1Manifest?.ingredients) { - manifest.ingredients.forEach((ingredient: any, index: number) => { + manifest.ingredients.forEach((ingredient, index) => { // ZERO TRUST DEFAULT: Unverified assets get no trust credentials ingredient.trust_source = 'none'; if (ingredient.active_manifest) { - const p1Ing = p1Manifest.ingredients[index]; + const p1Ing = p1Manifest.ingredients?.[index]; const subLabel = ingredient.active_manifest; const p1SubManifest = subLabel ? rawManifestStore.manifests?.[subLabel] : null; // 1. Check the Ingredient Pointer (V2 standard) - const p1V2_ing = p1Ing?.validation_status || []; + const p1V2_ing = (p1Ing?.validation_status || []) as ValidationStatus[]; // 2. Check the actual Sub-Manifest directly (V2 + V3 standards) // NOTE: In V3, the sub-manifest's own results are stored under .activeManifest - const p1V2_sub = p1SubManifest?.validation_status || []; - const p1V3_sub = p1SubManifest?.validation_results?.activeManifest?.failure || []; + const p1V2_sub = (p1SubManifest?.validation_status || []) as ValidationStatus[]; + const p1V3_sub = (p1SubManifest?.validation_results?.activeManifest?.failure || []) as ValidationStatus[]; // 3. Check the Root Deltas (V3 standard) - const p1Delta = p1Deltas.find((d: any) => - d.ingredientAssertionURI?.includes(label) && + const p1Delta = p1Deltas.find((d) => + d.ingredientAssertionURI?.includes(label) && d.ingredientAssertionURI?.includes('c2pa.ingredient') ); const p1V3_delta = p1Delta?.validationDeltas?.failure || []; @@ -212,12 +190,13 @@ export function createC2paReader(): C2paReaderStore { // Fallback: If root is Trusted, entire tree is official. const isTrusted = finalStore.validation_state === 'Trusted'; - Object.entries(finalStore.manifests || {}).forEach(([label, manifest]: [string, any]) => { + (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { if (label === finalStore.active_manifest) { manifest.trust_source = isTrusted ? 'official' : 'none'; } + if (manifest.ingredients) { - manifest.ingredients.forEach((ing: any) => { + manifest.ingredients.forEach((ing) => { ing.trust_source = (isTrusted && ing.active_manifest) ? 'official' : 'none'; }); } @@ -226,12 +205,13 @@ export function createC2paReader(): C2paReaderStore { } else { // Pass 1 had no trust issues (Could be Trusted or Hard Invalid) const isTrusted = finalStore.validation_state === 'Trusted'; - Object.entries(finalStore.manifests || {}).forEach(([label, manifest]: [string, any]) => { + (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { if (label === finalStore.active_manifest) { manifest.trust_source = isTrusted ? 'official' : 'none'; } + if (manifest.ingredients) { - manifest.ingredients.forEach((ing: any) => { + manifest.ingredients.forEach((ing) => { ing.trust_source = (isTrusted && ing.active_manifest) ? 'official' : 'none'; }); } @@ -239,7 +219,8 @@ export function createC2paReader(): C2paReaderStore { } const { assetMap, dispose: assetMapDisposer } = - await resultToAssetMap({ manifestStore: finalStore, source }); + await resultToAssetMap({ manifestStore: finalStore, source }); + dispose = () => { assetMapDisposer(); currentReader.free(); @@ -279,5 +260,6 @@ export function createC2paReader(): C2paReaderStore { async function hasLegacyCredentials(source: Blob | File): Promise { const legacySdk = await getLegacySdk(); const legacyResult = await legacySdk.processImage(source); + return legacyResult.exists; } From c8b42e403aae8e977fdde44326649ffdc7d646da Mon Sep 17 00:00:00 2001 From: Sherif Hanna Date: Thu, 30 Apr 2026 19:33:06 +0000 Subject: [PATCH 2/2] fix: resolve all svelte-check and SSR build errors after SDK migration --- package.json | 1 + pnpm-lock.yaml | 25 +++ .../EmbeddedIcon/EmbeddedIcon.svelte | 6 +- src/lib/asset.ts | 33 ++- src/lib/crjson.ts | 13 +- src/lib/exif.ts | 26 ++- src/lib/selectors/autoDubInfo.ts | 33 ++- src/lib/selectors/doNotTrain.ts | 45 ++++- src/lib/selectors/editsAndActivity.ts | 29 ++- src/lib/selectors/generativeInfo.ts | 66 ++++-- src/lib/selectors/producer.ts | 36 +++- src/lib/selectors/reviewRatings.ts | 18 +- src/lib/selectors/socialAccounts.ts | 22 +- src/lib/selectors/validationResult.ts | 7 +- src/lib/selectors/web3Info.ts | 18 +- src/lib/selectors/website.ts | 35 +++- .../AssetInfo/AssetInfoIssuerDate.svelte | 6 +- .../AboutSection/AboutSection.svelte | 4 +- src/routes/verify/stores/c2paReader.ts | 189 +++++++++--------- vite.config.js | 7 +- 20 files changed, 447 insertions(+), 172 deletions(-) diff --git a/package.json b/package.json index c606b8dde..333d3f24d 100644 --- a/package.json +++ b/package.json @@ -154,6 +154,7 @@ "svelte-css-vars": "^0.0.3", "svelte-focus-trap": "^1.2.0", "svelte-i18n": "^3.7.4", + "intl-messageformat": "9.13.0", "svelte-meta-tags": "^3.0.4", "svelte-modals": "^1.3.0", "tippy.js": "^6.3.7" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4697719b..743ac479d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: interactjs: specifier: ^1.10.19 version: 1.10.27 + intl-messageformat: + specifier: 9.13.0 + version: 9.13.0 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -1809,78 +1812,92 @@ packages: resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.0': resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.0': resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.0': resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.0': resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.0': resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.0': resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.3': resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.3': resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.3': resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.3': resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.3': resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.3': resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.3': resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.3': resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} @@ -3347,41 +3364,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} diff --git a/src/components/EmbeddedIcon/EmbeddedIcon.svelte b/src/components/EmbeddedIcon/EmbeddedIcon.svelte index 2c89a618b..6476e0b11 100644 --- a/src/components/EmbeddedIcon/EmbeddedIcon.svelte +++ b/src/components/EmbeddedIcon/EmbeddedIcon.svelte @@ -4,17 +4,13 @@ -{#if issuer}{$_('sidebar.verify.about.issuedby')} {issuer}{/if} +
+ {#if issuer}{$_('sidebar.verify.about.issuedby')} {issuer}{/if} +
diff --git a/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte b/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte index de003cbf5..a3ba3b7d2 100644 --- a/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte +++ b/src/routes/verify/components/DetailedInfo/AboutSection/AboutSection.svelte @@ -24,8 +24,8 @@ {#if manifestData.signatureInfo?.common_name || manifestData.signatureInfo?.issuer} ; export type ReadableSource = Readable; export interface C2paReaderStore extends Readable { - read: (source: Blob | File) => Promise; + read: (source: Blob | File | string) => Promise; clear: () => void; } @@ -45,11 +45,16 @@ export function createC2paReader(): C2paReaderStore { return { subscribe, - read: async (source: Blob | File) => { + read: async (source: Blob | File | string) => { set({ state: 'loading' }); dispose?.(); try { + if (typeof source === 'string') { + const response = await fetch(source); + source = await response.blob(); + } + const sdk = await getSdk(); const sourceType = source instanceof Blob ? source.type : ''; const normalizedSourceType = sourceType.toLowerCase().trim(); @@ -109,98 +114,101 @@ export function createC2paReader(): C2paReaderStore { if (needsLegacyPass) { const legacySettings = await getLegacyToolkitSettings(); const legacyReader = await sdk.reader.fromBlob(source.type || 'application/octet-stream', source, legacySettings); - const legacyStore = await legacyReader.manifestStore(); - - // 3. ONLY ADOPT Pass 2 if it actually solved the problem (State is now Trusted/Valid) - if (legacyStore.validation_state === 'Trusted' || legacyStore.validation_state === 'Valid') { - finalStore = legacyStore; - - reader.free(); - currentReader = legacyReader; - - const isTrustError = (s: ValidationStatus) => s.code.includes('signingCredential.untrusted') || s.code.includes('signingCredential.invalid'); - - // 4a. Tag the Active Manifest - const p1ActiveV3 = (rawManifestStore.validation_results?.activeManifest?.failure || []) as ValidationStatus[]; - const p1ActiveV2 = (rawManifestStore.manifests?.[rawManifestStore.active_manifest || '']?.validation_status || []) as ValidationStatus[]; - const wasActiveUntrusted = p1ActiveV3.some(isTrustError) || p1ActiveV2.some(isTrustError); - const isFinalTrusted = finalStore.validation_state === 'Trusted'; - - if (finalStore.manifests && finalStore.active_manifest && finalStore.manifests[finalStore.active_manifest]) { - const activeMan = finalStore.manifests[finalStore.active_manifest] as unknown as TrustedManifest; - - // If the root is Trusted, default to official, then downgrade if Pass 1 failed. - if (isFinalTrusted) { - activeMan.trust_source = wasActiveUntrusted ? 'legacy' : 'official'; - } else { - activeMan.trust_source = 'none'; + + if (legacyReader) { + const legacyStore = await legacyReader.manifestStore(); + + // 3. ONLY ADOPT Pass 2 if it actually solved the problem (State is now Trusted/Valid) + if (legacyStore.validation_state === 'Trusted' || legacyStore.validation_state === 'Valid') { + finalStore = legacyStore; + + reader.free(); + currentReader = legacyReader; + + const isTrustError = (s: ValidationStatus) => s.code.includes('signingCredential.untrusted') || s.code.includes('signingCredential.invalid'); + + // 4a. Tag the Active Manifest + const p1ActiveV3 = (rawManifestStore.validation_results?.activeManifest?.failure || []) as ValidationStatus[]; + const p1ActiveV2 = (rawManifestStore.manifests?.[rawManifestStore.active_manifest || '']?.validation_status || []) as ValidationStatus[]; + const wasActiveUntrusted = p1ActiveV3.some(isTrustError) || p1ActiveV2.some(isTrustError); + const isFinalTrusted = finalStore.validation_state === 'Trusted'; + + if (finalStore.manifests && finalStore.active_manifest && finalStore.manifests[finalStore.active_manifest]) { + const activeMan = finalStore.manifests[finalStore.active_manifest] as unknown as TrustedManifest; + + // If the root is Trusted, default to official, then downgrade if Pass 1 failed. + if (isFinalTrusted) { + activeMan.trust_source = wasActiveUntrusted ? 'legacy' : 'official'; + } else { + activeMan.trust_source = 'none'; + } } - } - // 4b. Tag ALL Ingredients across the entire provenance tree - const p1Deltas = (rawManifestStore.validation_results?.ingredientDeltas || []) as ValidationDelta[]; - - (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { - const p1Manifest = rawManifestStore.manifests?.[label]; - - if (manifest.ingredients && p1Manifest?.ingredients) { - manifest.ingredients.forEach((ingredient, index) => { - // ZERO TRUST DEFAULT: Unverified assets get no trust credentials - ingredient.trust_source = 'none'; - - if (ingredient.active_manifest) { - const p1Ing = p1Manifest.ingredients?.[index]; - const subLabel = ingredient.active_manifest; - const p1SubManifest = subLabel ? rawManifestStore.manifests?.[subLabel] : null; - - // 1. Check the Ingredient Pointer (V2 standard) - const p1V2_ing = (p1Ing?.validation_status || []) as ValidationStatus[]; - - // 2. Check the actual Sub-Manifest directly (V2 + V3 standards) - // NOTE: In V3, the sub-manifest's own results are stored under .activeManifest - const p1V2_sub = (p1SubManifest?.validation_status || []) as ValidationStatus[]; - const p1V3_sub = (p1SubManifest?.validation_results?.activeManifest?.failure || []) as ValidationStatus[]; - - // 3. Check the Root Deltas (V3 standard) - const p1Delta = p1Deltas.find((d) => - d.ingredientAssertionURI?.includes(label) && - d.ingredientAssertionURI?.includes('c2pa.ingredient') - ); - const p1V3_delta = p1Delta?.validationDeltas?.failure || []; - - // AGGREGATE: Did ANY layer in the sub-manifest chain fail Trust in Pass 1? - const hasTrustError = - p1V3_delta.some(isTrustError) || - p1V2_ing.some(isTrustError) || - p1V2_sub.some(isTrustError) || - p1V3_sub.some(isTrustError); - - if (isFinalTrusted) { - // If the Root is trusted, the chain is intact. - // It only earns 'Official' if it explicitly cleared the local error gauntlet. - ingredient.trust_source = hasTrustError ? 'legacy' : 'official'; + // 4b. Tag ALL Ingredients across the entire provenance tree + const p1Deltas = (rawManifestStore.validation_results?.ingredientDeltas || []) as ValidationDelta[]; + + (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { + const p1Manifest = rawManifestStore.manifests?.[label]; + + if (manifest.ingredients && p1Manifest?.ingredients) { + manifest.ingredients.forEach((ingredient, index) => { + // ZERO TRUST DEFAULT: Unverified assets get no trust credentials + ingredient.trust_source = 'none'; + + if (ingredient.active_manifest) { + const p1Ing = p1Manifest.ingredients?.[index]; + const subLabel = ingredient.active_manifest; + const p1SubManifest = (subLabel ? rawManifestStore.manifests?.[subLabel] : null) as Record | null; + + // 1. Check the Ingredient Pointer (V2 standard) + const p1V2_ing = (p1Ing?.validation_status || []) as ValidationStatus[]; + + // 2. Check the actual Sub-Manifest directly (V2 + V3 standards) + // NOTE: In V3, the sub-manifest's own results are stored under .activeManifest + const p1V2_sub = (p1SubManifest?.validation_status || []) as ValidationStatus[]; + const p1V3_sub = ((p1SubManifest?.validation_results as Record | undefined)?.activeManifest as Record | undefined)?.failure as ValidationStatus[] | undefined || []; + + // 3. Check the Root Deltas (V3 standard) + const p1Delta = p1Deltas.find((d) => + d.ingredientAssertionURI?.includes(label) && + d.ingredientAssertionURI?.includes('c2pa.ingredient') + ); + const p1V3_delta = p1Delta?.validationDeltas?.failure || []; + + // AGGREGATE: Did ANY layer in the sub-manifest chain fail Trust in Pass 1? + const hasTrustError = + p1V3_delta.some(isTrustError) || + p1V2_ing.some(isTrustError) || + p1V2_sub.some(isTrustError) || + p1V3_sub.some(isTrustError); + + if (isFinalTrusted) { + // If the Root is trusted, the chain is intact. + // It only earns 'Official' if it explicitly cleared the local error gauntlet. + ingredient.trust_source = hasTrustError ? 'legacy' : 'official'; + } } - } - }); - } - }); - - } else { - legacyReader.free(); - - // Fallback: If root is Trusted, entire tree is official. - const isTrusted = finalStore.validation_state === 'Trusted'; - (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { - if (label === finalStore.active_manifest) { - manifest.trust_source = isTrusted ? 'official' : 'none'; - } + }); + } + }); - if (manifest.ingredients) { - manifest.ingredients.forEach((ing) => { - ing.trust_source = (isTrusted && ing.active_manifest) ? 'official' : 'none'; - }); - } - }); + } else { + legacyReader.free(); + + // Fallback: If root is Trusted, entire tree is official. + const isTrusted = finalStore.validation_state === 'Trusted'; + (Object.entries(finalStore.manifests || {}) as Array<[string, TrustedManifest]>).forEach(([label, manifest]) => { + if (label === finalStore.active_manifest) { + manifest.trust_source = isTrusted ? 'official' : 'none'; + } + + if (manifest.ingredients) { + manifest.ingredients.forEach((ing) => { + ing.trust_source = (isTrusted && ing.active_manifest) ? 'official' : 'none'; + }); + } + }); + } } } else { // Pass 1 had no trust issues (Could be Trusted or Hard Invalid) @@ -239,6 +247,7 @@ export function createC2paReader(): C2paReaderStore { toast.trigger(unsupportedFileType()); } else if ( (errName === 'C2pa(PrereleaseError)' || errStr.includes('Prerelease')) && + typeof source !== 'string' && (await hasLegacyCredentials(source)) ) { openModal(LegacyCredentialModal); diff --git a/vite.config.js b/vite.config.js index 5a29118a2..ed52a5904 100644 --- a/vite.config.js +++ b/vite.config.js @@ -18,6 +18,11 @@ const config = { allow: ['assets', 'locales'], }, }, + resolve: { + alias: { + 'intl-messageformat': path.resolve(__dirname, 'node_modules/intl-messageformat/intl-messageformat.esm.js'), + }, + }, build: { minify: 'terser', sourcemap: true, @@ -29,7 +34,7 @@ const config = { }, }, ssr: { - noExternal: ['svelte-i18n'], + noExternal: ['svelte-i18n', 'intl-messageformat'], }, define: { __SUPPORTED_LOCALES__: JSON.stringify(getSupportedLocales()),