UNPKG

@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

104 lines (103 loc) • 5.66 kB
import { injectable } from "../../../plugins/index.mjs"; import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.mjs"; import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.mjs"; import { CREDENTIALS_CONTEXT_V2_URL } from "../constants.mjs"; import { omitUndefinedFields } from "../../w3c-di/proof-processing/normalization.mjs"; import { createProofVerificationIssue } from "../../w3c-di/W3cDataIntegrityError.mjs"; import "../../w3c-di/internal.mjs"; import { DI_SPEC_CONTEXT_HASHES } from "../jsonld/contexts/contextHashes.mjs"; import { DEFAULT_CONTEXTS } from "../jsonld/contexts/defaultContexts.mjs"; import jsonld_default from "../jsonld/jsonld.mjs"; import { JsonLdModuleConfig } from "../jsonld/JsonLdModuleConfig.mjs"; import { W3cV2CredentialsModuleConfig } from "../W3cV2CredentialsModuleConfig.mjs"; //#region src/modules/vc/data-integrity/W3cV2DataIntegrityContextValidator.ts let W3cV2DataIntegrityContextValidator = class W3cV2DataIntegrityContextValidator { constructor(jsonLdModuleConfig, w3cV2CredentialsModuleConfig) { this.jsonLdModuleConfig = jsonLdModuleConfig; this.w3cV2CredentialsModuleConfig = w3cV2CredentialsModuleConfig; } async validate(agentContext, inputDocument) { const normalisedInputDocument = omitUndefinedFields(inputDocument); const validatedDocument = { ...normalisedInputDocument }; const result = { validated: false, validatedDocument, warnings: [], errors: [] }; const contextValue = normaliseContext(validatedDocument["@context"]); if (validatedDocument["@context"] === void 0 || validatedDocument["@context"] === null) { result.errors.push(createProofVerificationIssue("Missing top-level @context in secured document", "Verification must not perform context injection (§2.4.2). Document must have explicit @context.")); result.validated = false; result.validatedDocument = null; return result; } const triggerErrors = []; if (contextValue[0] !== "https://www.w3.org/ns/credentials/v2") triggerErrors.push(createProofVerificationIssue("@context does not start with the required VC 2.0 context", `Document @context must start with '${CREDENTIALS_CONTEXT_V2_URL}'`)); const nestedContextPaths = collectAllNestedContextPaths(validatedDocument); if (nestedContextPaths.length > 0) triggerErrors.push(createProofVerificationIssue("Nested @context detected in document", `Nested @context found at path(s): ${nestedContextPaths.join(", ")}`)); for (const contextEntry of contextValue) { if (typeof contextEntry !== "string") continue; if (contextEntry === "https://www.w3.org/ns/credentials/v2") continue; if (!(contextEntry in DI_SPEC_CONTEXT_HASHES)) continue; const bundledContextIssue = this.verifyContextIsBundled(contextEntry); if (bundledContextIssue) { triggerErrors.push(bundledContextIssue); break; } } if (triggerErrors.length > 0) { if (this.w3cV2CredentialsModuleConfig.recompactInvalidContexts) try { result.validatedDocument = await jsonld_default.compact(normalisedInputDocument, [CREDENTIALS_CONTEXT_V2_URL], { documentLoader: this.jsonLdModuleConfig.documentLoader(agentContext), compactToRelative: false }); result.warnings.push(...triggerErrors); } catch (error) { result.errors.push(createProofVerificationIssue("Context recompaction failed (§4.6 step 3.1)", error instanceof Error ? error.message : "JSON-LD compaction failed")); } else result.errors.push(...triggerErrors); } if (result.errors.length === 0) result.validated = true; else { result.validated = false; result.validatedDocument = null; } return result; } /** * Verifies a spec-pinned context URI (§2.4). The §2.4 hash-pinning mechanism exists for * processors that don't vendor the canonical contexts and must instead fetch and hash-verify * them over the network; Credo vendors all spec-required contexts, so a bundled copy is * trusted as canonical and no network fetch or hash comparison is performed. */ verifyContextIsBundled(contextUri) { if (hasBundledContext(contextUri)) return; return createProofVerificationIssue("Unable to verify context hash (§2.4)", `Context '${contextUri}' is not available as a bundled context and cannot be hash-verified`); } }; W3cV2DataIntegrityContextValidator = __decorate([injectable(), __decorateMetadata("design:paramtypes", [typeof JsonLdModuleConfig === "undefined" ? Object : JsonLdModuleConfig, typeof W3cV2CredentialsModuleConfig === "undefined" ? Object : W3cV2CredentialsModuleConfig])], W3cV2DataIntegrityContextValidator); function normaliseContext(context) { if (context === void 0 || context === null) return []; return Array.isArray(context) ? context : [context]; } function collectAllNestedContextPaths(value, path = []) { if (value === null || typeof value !== "object") return []; if (Array.isArray(value)) return value.flatMap((item, i) => collectAllNestedContextPaths(item, [...path, String(i)])); const obj = value; const paths = []; for (const [key, entry] of Object.entries(obj)) { const nextPath = [...path, key]; const isTopLevelContext = path.length === 0 && key === "@context"; if (key === "@context" && !isTopLevelContext) paths.push(nextPath.join(".")); paths.push(...collectAllNestedContextPaths(entry, nextPath)); } return paths; } function hasBundledContext(contextUrl) { if (contextUrl in DEFAULT_CONTEXTS) return true; return contextUrl.split("#")[0] in DEFAULT_CONTEXTS; } //#endregion export { W3cV2DataIntegrityContextValidator }; //# sourceMappingURL=W3cV2DataIntegrityContextValidator.mjs.map