{#if manifestData}
-
+
{/if}
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 +38,21 @@ 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 +106,11 @@ 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');
@@ -129,96 +127,148 @@ export function createC2paReader(): C2paReaderStore {
// 2. Run Pass 2 ONLY if the official pass wasn't perfectly trusted
if (needsLegacyPass) {
const legacySettings = await getLegacyToolkitSettings();
- const legacyReader = await sdk.reader.fromBlob(source.type || 'application/octet-stream', source, legacySettings);
+ const legacyReaderOrNull = await sdk.reader.fromBlob(
+ source.type || 'application/octet-stream',
+ source,
+ legacySettings,
+ );
+
+ if (!legacyReaderOrNull) {
+ throw new Error('Legacy reader returned null');
+ }
+
+ const legacyReader = legacyReaderOrNull;
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') {
+ if (
+ legacyStore.validation_state === 'Trusted' ||
+ legacyStore.validation_state === 'Valid'
+ ) {
finalStore = legacyStore;
-
+
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 wasActiveUntrusted = p1ActiveV3.some(isTrustError) || p1ActiveV2.some(isTrustError);
+ 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];
-
+
+ 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';
+ 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 || [];
-
- 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;
+ 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[];
+ type ManifestValidationResults = {
+ activeManifest?: { failure?: ValidationStatus[] };
+ };
+ const p1SubValResults = (
+ p1SubManifest as
+ | { validation_results?: ManifestValidationResults }
+ | undefined
+ )?.validation_results;
+ const p1V3_sub = (p1SubValResults?.activeManifest
+ ?.failure || []) as ValidationStatus[];
// 3. Check the Root Deltas (V3 standard)
- const p1Delta = p1Deltas.find((d: any) =>
- d.ingredientAssertionURI?.includes(label) &&
- d.ingredientAssertionURI?.includes('c2pa.ingredient')
+ 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) ||
+ 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.
+ // 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';
+ 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 || {}).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) => {
- ing.trust_source = (isTrusted && ing.active_manifest) ? 'official' : 'none';
+ manifest.ingredients.forEach((ing) => {
+ ing.trust_source =
+ isTrusted && ing.active_manifest ? 'official' : 'none';
});
}
});
@@ -226,20 +276,29 @@ 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) => {
- ing.trust_source = (isTrusted && ing.active_manifest) ? 'official' : 'none';
+ manifest.ingredients.forEach((ing) => {
+ ing.trust_source =
+ isTrusted && ing.active_manifest ? 'official' : 'none';
});
}
});
}
- const { assetMap, dispose: assetMapDisposer } =
- await resultToAssetMap({ manifestStore: finalStore, source });
+ const { assetMap, dispose: assetMapDisposer } = await resultToAssetMap({
+ manifestStore: finalStore,
+ source,
+ });
+
dispose = () => {
assetMapDisposer();
currentReader.free();
@@ -254,10 +313,14 @@ export function createC2paReader(): C2paReaderStore {
const errStr = String(e);
const errName = (e as Record)?.name;
- if (errName === 'InvalidMimeTypeError' || errStr.includes('Unsupported format')) {
+ if (
+ errName === 'InvalidMimeTypeError' ||
+ errStr.includes('Unsupported format')
+ ) {
toast.trigger(unsupportedFileType());
} else if (
- (errName === 'C2pa(PrereleaseError)' || errStr.includes('Prerelease')) &&
+ (errName === 'C2pa(PrereleaseError)' ||
+ errStr.includes('Prerelease')) &&
(await hasLegacyCredentials(source))
) {
openModal(LegacyCredentialModal);
@@ -279,5 +342,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;
}
diff --git a/src/routes/verify/stores/verifyStore.ts b/src/routes/verify/stores/verifyStore.ts
index 8c959881e..94bbe46e6 100644
--- a/src/routes/verify/stores/verifyStore.ts
+++ b/src/routes/verify/stores/verifyStore.ts
@@ -150,7 +150,11 @@ export function createVerifyStore(): VerifyStore {
}
dbg('Reading C2PA source', source);
- c2paReader.read(source);
+
+ if (typeof source !== 'string') {
+ c2paReader.read(source);
+ }
+
dbg('Setting selected source', incomingSource);
selectedSource.set(incomingSource);
},