diff --git a/index.html b/index.html index 5f8b583..967c0e9 100644 --- a/index.html +++ b/index.html @@ -425,10 +425,10 @@
-The `json-card` render suite uses JSON templates to transform a -[=verifiable credential=] into a standardized JSON card format. This format +The `card` render suite uses JSON templates to transform a +[=verifiable credential=] into a standardized data display format. This format enables wallets to display credentials in a responsive card layout with key data highlighted and configurable additional fields. Wallets that implement this method can render the standardized JSON output in their own card UI @@ -437,20 +437,20 @@
-The template is a JSON object that matches the `json-card` output structure. +The template is a JSON object that matches the `card` output structure. String values in the template can be JSON pointer strings (as specified in [[[RFC6901]]]) that reference fields in the [=verifiable credential=]. When processing the template, JSON pointer strings are evaluated against the credential data and replaced with the resolved values. The template MUST conform to the JSON template schema defined below, and the resulting output -MUST conform to the `json-card` output schema. Compound data across multiple +MUST conform to the `card` output schema. Compound data across multiple fields is not supported; each field references a single JSON pointer.
-The template for a `json-card` render suite MUST be a JSON object that +The template for a `card` render suite MUST be a JSON object that conforms to the following structure. The template structure matches the output structure, but string values can be either literal strings or JSON pointer strings (starting with `/`) that reference fields in the @@ -540,7 +540,7 @@
+ title="JSON Schema for card template">
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
@@ -610,12 +610,12 @@ JSON Template Schema
- The following example shows a valid `json-card` template with JSON pointer + The following example shows a valid `card` template with JSON pointer strings:
+ title="Example card template">
{
"name": "/credentialSubject/degree/name",
"description": "University Degree Credential",
@@ -646,9 +646,9 @@ JSON Template Schema
- The output of a `json-card` template MUST be a JSON object that conforms to + The output of a `card` template MUST be a JSON object that conforms to the following structure:
@@ -723,11 +723,11 @@- The following example shows a valid `json-card` output: + The following example shows a valid `card` output:
+ title="Example card output">
{
"name": "Bachelor of Science and Arts",
"description": "University Degree Credential",
@@ -760,7 +760,7 @@ JSON Card Output Schema
Template Processing
- When processing a `json-card` template, the following steps MUST be
+ When processing a `card` template, the following steps MUST be
performed:
@@ -795,7 +795,7 @@ Template Processing
- Validate the resulting JSON object against the `json-card` output schema. If
+ Validate the resulting JSON object against the `card` output schema. If
validation fails, processing MUST stop and an error MUST be returned.
@@ -814,12 +814,12 @@ Template Processing
+ title="Basic usage of the card render suite">
{
...
"renderMethod": {
"type": "TemplateRenderMethod",
- "renderSuite": "json-card",
+ "renderSuite": "card",
// the JSON template is embedded in the VC
"template": "data:application/json;base64,eyJuYW1lIjogIi9jcmVkZW50aWFsU3ViamVjdC9kZWdyZWUvbmFtZSIsICJkZXNjcmlwdGlvbiI6ICJVbml2ZXJzaXR5IERlZ3JlZSBDcmVkZW50aWFsIiwgImZpZWxkcyI6IFt7ImxhYmVsIjogIkluc3RpdHV0aW9uIiwgInZhbHVlIjogIi9pc3N1ZXIifV19"
}
@@ -832,12 +832,12 @@ Template Processing
+ title="A remotely hosted JSON template for a card render template">
{
...
"renderMethod": {
"type": "TemplateRenderMethod",
- "renderSuite": "json-card",
+ "renderSuite": "card",
"template": {
// this JSON template is fetched from the Web
"id": "https://degree.example/credential-templates/bachelors.json",
@@ -853,15 +853,15 @@ Template Processing
+ title="A remotely hosted card render method">
{
...
"renderMethod": {
// this render method is fetched from the Web
- "id": "https://degrees.example/bachelors-json-card.jsonld",
+ "id": "https://degrees.example/bachelors-card.jsonld",
"mediaType": "application/ld+json",
"type": "TemplateRenderMethod",
- "renderSuite": "json-card",
+ "renderSuite": "card",
"digestMultibase": "zQmG270iEu5h6JqWAPdhyxz2dRerWC85Wg6wFl9znFCwYxAp"
}
diff --git a/threat-model/diagrams/dfd.mmd b/threat-model/diagrams/dfd.mmd
new file mode 100644
index 0000000..6383813
--- /dev/null
+++ b/threat-model/diagrams/dfd.mmd
@@ -0,0 +1,110 @@
+%% Data Flow Diagram — W3C Verifiable Credentials Render Method Threat Model
+%% Reused vc-data-model components are grayed with identifiers removed;
+%% Render Method components are colored and carry active identifiers.
+%% E = External Entity (blue rectangle) P = Process (green rounded rect)
+%% F = Data Flow (labelled arrow) S = Data Store (pink cylinder)
+%% O = Data Object (mauve trapezoid) C = Contained System (dashed box)
+flowchart LR
+
+ %% ---------- Issuer System (C1) ----------
+ subgraph C1["C1: Issuer System"]
+ direction TB
+ E1["E1: Issuer"]
+
+ %% -- reused context (grayed, unnumbered) --
+ GP1("Establish Confidence")
+ GP2("Assemble Verifiable Credential")
+ GP3("Issue Verifiable Credential")
+ GP4("Publish Status List")
+ GP5("Update Status List")
+ GS1[("Status List Storage")]
+
+ %% -- new render-method components --
+ P1("P1: Publish Render Template")
+ P2("P2: Update Render Template")
+ O1[/"O1: Render Template"/]
+ S1[("S1: Render Template Storage")]
+
+ GP1 --> GP2
+ GP2 --> GP3
+ GP4 --> GS1
+ GP5 --> GS1
+ P1 --> O1
+ P2 --> O1
+ O1 --> S1
+ end
+
+ %% ---------- Holder System (C2) ----------
+ subgraph C2["C2: Holder System"]
+ direction TB
+ E2["E2: Holder"]
+
+ %% -- reused context (grayed, unnumbered) --
+ GP6("Store Verifiable Credential")
+ GP7("Engage in Workflow (Holder)")
+ GP8("Generate Presentation")
+ GS2[("Holder Storage")]
+
+ %% -- new render-method components --
+ P3("P3: Render")
+
+ GP6 --> GS2
+ GS2 --> GP8
+ GP7 --> GP8
+ end
+
+ %% ---------- Verifier System (C3) ----------
+ subgraph C3["C3: Verifier System"]
+ direction TB
+ E3["E3: Verifier"]
+
+ %% -- reused context (grayed, unnumbered) --
+ GP9("Engage in Workflow (Verifier)")
+ GP10("Verify Presentation")
+ GP11("Verify Credential Status")
+ GP12("Validate Credential(s)")
+
+ %% -- new render-method components --
+ %% Same logical Render process (P3) as in the Holder System.
+ P3v("P3: Render")
+
+ GP9 --> GP10
+ GP10 --> GP11
+ GP11 --> GP12
+ end
+
+ %% ---------- Force C1 -> C2 -> C3 left-to-right ordering ----------
+ C1 ~~~ C2
+ C2 ~~~ C3
+
+ %% ---------- Reused cross-boundary flows (grayed, unnumbered) ----------
+ GP7 -. "Establish Confidence in Subject" .-> GP1
+ GP3 -. "Deliver Verifiable Credential" .-> GP6
+ GP8 -. "Exchange Presentation" .-> GP9
+ GP11 -. "Check Credential Status" .-> GS1
+
+ %% ---------- New render-method Data Flows (labelled arrows) ----------
+ %% F1 retrieves the Render Template (O1) from storage into the Render process.
+ P3 -- "F1: Retrieve Render Template (O1)" --> S1
+ P3v -- "F1: Retrieve Render Template (O1)" --> S1
+ P3 -- "F2: Observe Rendering" --> E2
+ P3v -- "F2: Observe Rendering" --> E3
+
+ %% ---------- Element styling ----------
+ classDef entity fill:#c9daf8,stroke:#000,color:#000;
+ classDef process fill:#d9ead3,stroke:#000,color:#000,rx:10,ry:10;
+ classDef store fill:#f4cccc,stroke:#000,color:#000;
+ classDef object fill:#c27ba0,stroke:#000,color:#000;
+ classDef ctxProcess fill:#eeeeee,stroke:#999,color:#999,rx:10,ry:10;
+ classDef ctxStore fill:#eeeeee,stroke:#999,color:#999;
+
+ class E1,E2,E3 entity;
+ class P1,P2,P3,P3v process;
+ class S1 store;
+ class O1 object;
+ class GP1,GP2,GP3,GP4,GP5,GP6,GP7,GP8,GP9,GP10,GP11,GP12 ctxProcess;
+ class GS1,GS2 ctxStore;
+
+ style C1 fill:#fff0e5,stroke:#333,stroke-dasharray:6 4;
+ style C2 fill:#fff0e5,stroke:#333,stroke-dasharray:6 4;
+ style C3 fill:#fff0e5,stroke:#333,stroke-dasharray:6 4;
diff --git a/threat-model/diagrams/dfd.svg b/threat-model/diagrams/dfd.svg
new file mode 100644
index 0000000..17e421b
--- /dev/null
+++ b/threat-model/diagrams/dfd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/threat-model/index.html b/threat-model/index.html
new file mode 100644
index 0000000..b0d549a
--- /dev/null
+++ b/threat-model/index.html
@@ -0,0 +1,488 @@
+
+
+
+
+
+ Verifiable Credentials Render Method Threat Model
+
+
+
+
+
+
+
+
+
+
+
+
+This document describes a threat model for the
+Verifiable Credentials
+Render Method specification. It identifies and analyzes security and
+privacy threats specific to the publication, retrieval, and processing of
+render methods used to produce a human-readable rendering of a
+[=verifiable credential=].
+
+
+
+
+This threat model is a work in progress. It is published alongside the
+Verifiable Credentials Render Method specification to inform implementers and
+ecosystem designers of the security and privacy considerations relevant to
+deployments of the specification. Feedback and proposed additions are
+welcome via the
+issue tracker.
+
+
+
+ Introduction
+
+Threat modeling is a vital part of specification development. This document
+provides a threat model for the Verifiable Credentials Render Method
+specification, which defines how an [=issuer=] can associate one or more
+render templates with a [=verifiable credential=] so that a [=holder=] or
+[=verifier=] can produce a consistent, human-readable rendering of that
+credential.
+
+
+This threat model follows the methodology described in the
+W3C Threat Modeling
+Guide and classifies threats using the STRIDE taxonomy. Threats are
+grouped into five categories:
+
+
+ -
+Target Threats are threats the specification was designed
+to address.
+
+ -
+Implementation Threats are threats that implementers must
+handle; the specification leaves the choice of mitigation open.
+
+ -
+Deployment Threats are threats that arise from how systems
+are configured and operated.
+
+ -
+External Threats are real-world risks acknowledged as out
+of scope for this version of the specification.
+
+ -
+Dependency Threats are threats inherited from technologies
+the specification depends upon.
+
+
+
+
+ Description
+
+The Verifiable Credentials Render Method specification builds on the
+Verifiable Credentials Data Model. For this threat model, we consider a
+minimal instantiation focused on the render method lifecycle, consisting of
+three roles: the [=E1|Issuer=] (E1), who publishes and
+maintains the render templates associated with a [=verifiable credential=];
+the [=E2|Holder=] (E2), who renders a credential to review
+it; and the [=E3|Verifier=] (E3), who renders a credential
+as part of evaluating it. The elements and flows introduced by the Render
+Method specification are shown in full color in the data flow diagram below;
+elements reused from the
+Verifiable
+Credentials Data Model threat model are shown grayed as context.
+
+
+Note: We do NOT model potential attackers as
+first-class elements, as over-characterizing attackers can lead to analysis
+bias that is better avoided.
+
+
+
+
+ Data Flow Diagram for the Render Method Ecosystem
+
+The data flow diagram below illustrates the key elements and data flows
+introduced by the Render Method specification. Within the
+[=C1|Issuer System=] (C1), the issuer runs the [=P1|Publish Render Template=]
+(P1) and [=P2|Update Render Template=] (P2) processes, which write to
+[=S1|Render Template Storage=] (S1). Within the [=C2|Holder System=] (C2)
+and the [=C3|Verifier System=] (C3), the [=P3|Render=] (P3) process
+retrieves a render template ([=F1|F1=]) and produces a rendering that the
+holder or verifier observes ([=F2|F2=]). Elements drawn from the Verifiable
+Credentials Data Model threat model are shown grayed for context and are not
+re-numbered here.
+
+
+
+
+
+The following notation is used in the diagram description and dictionary:
+
+
+ -
+E denotes an External Entity (a human or legal person
+outside the system boundary)
+
+ -
+P denotes a Process (a running algorithm that transforms
+data)
+
+ -
+F denotes a Data Flow (movement of data between elements)
+
+ -
+S denotes a Data Store (persistent or ephemeral storage)
+
+ -
+O denotes a Data Object (a self-contained, transferable
+artifact)
+
+ -
+C denotes a Contained System Boundary (a collection of
+elements under a single authority)
+
+
+
+
+
+ Dictionary
+
+
+The dictionary defines the data flow diagram elements introduced by the
+Render Method specification. Elements reused from the
+Verifiable
+Credentials Data Model threat model appear grayed in the diagram for
+context and are defined in that document rather than repeated here.
+
+
+
+
+
+ E1 Issuer
+
+
+An entity that issues a [=verifiable credential=] and, using the Render
+Method specification, associates one or more render methods with it so that
+consumers have an issuer-intended way to present the credential. The issuer
+runs [=P1|Publish Render Template=] and [=P2|Update Render Template=] and
+operates [=S1|Render Template Storage=].
+
+
+
+
+ E2 Holder
+
+
+A person or organization that holds a [=verifiable credential=] and renders
+it, by way of the [=P3|Render=] process, in order to view or review it. The
+holder is the person who observes the rendering ([=F2|Observe Rendering=])
+within the [=C2|holder system=].
+
+
+
+
+ E3 Verifier
+
+
+An entity that receives and evaluates a [=verifiable credential=] and renders
+it, by way of the [=P3|Render=] process, as part of reviewing it. The
+verifier is the person or operator who observes the rendering
+([=F2|Observe Rendering=]) within the [=C3|verifier system=].
+
+
+
+
+ P1 Publish Render
+Template
+
+
+The process by which an [=issuer=] creates a [=O1|render template=] and makes
+it available in [=S1|Render Template Storage=], whether by hosting it for
+later retrieval or by embedding it so that it travels inside the credential.
+
+
+
+
+ P2 Update Render
+Template
+
+
+The process by which an [=issuer=] changes a previously published
+[=O1|render template=] in [=S1|Render Template Storage=], for example to
+update branding or layout over the lifetime of a long-lived credential.
+Updating a template that is not bound to the credential by a digest changes
+how previously issued credentials render.
+
+
+
+
+ P3 Render
+
+
+The process that combines a [=O1|render template=] retrieved via
+[=F1|Retrieve Render Template=] with claim values from a
+[=verifiable credential=] and produces output a person perceives, which can
+be visual, auditory, tactile, or textual. This is the same logical process
+whether it runs within the [=C2|holder system=] or the [=C3|verifier
+system=]. For the `html` render suite it executes template-supplied code
+inside a confined rendering environment; for the `card` and `nfc` render
+suites it produces a standardized output or emits a static payload without
+executing template-supplied code. The specification defines the confinement
+for the `html` suite in terms of required security outcomes rather than a
+specific mechanism. A sandboxed browser iframe with a Content Security Policy
+is one common way to achieve those outcomes, but it is an implementation
+detail, and equivalent isolation can be provided by other means.
+
+
+
+
+ F1 Retrieve Render
+Template
+
+
+The data flow by which the [=P3|Render=] process obtains the
+[=O1|render template=] from [=S1|Render Template Storage=]. When the template
+is referenced by location, this flow is a network request that reveals to the
+[=issuer=] or template host that a credential is being rendered; when the
+template is embedded, the template travels inside the credential and no such
+request is made.
+
+
+
+
+ F2 Observe Rendering
+
+
+The data flow by which the output of the [=P3|Render=] process reaches the
+[=holder=] or [=verifier=] who observes it. This flow represents the
+rendered presentation as it is perceived by a person.
+
+
+
+
+ O1 Render Template
+
+
+The self-contained, transferable artifact that determines how a
+[=verifiable credential=] is presented. Depending on the render suite it is a
+JSON template producing a standardized card output (`card`), an HTML fragment
+carrying its own CSS and JavaScript (`html`), or a static binary payload
+(`nfc`). A render template is produced by [=P1|Publish Render Template=],
+optionally changed by [=P2|Update Render Template=], held in
+[=S1|Render Template Storage=], carried by [=F1|Retrieve Render Template=],
+and consumed by [=P3|Render=]. It can be referenced by location or embedded
+in the credential, and it can be bound to the credential by a cryptographic
+digest.
+
+
+
+
+ S1 Render Template
+Storage
+
+
+The storage, within or operated by the [=C1|issuer system=], that holds
+published [=O1|render templates=] for retrieval by consumers. When a template
+is embedded in the credential rather than hosted, this storage is effectively
+the credential itself as it resides in the holder's possession.
+
+
+
+
+ C1 Issuer System
+
+
+The system boundary, typically a server or service operated by or for the
+[=issuer=], within which [=P1|Publish Render Template=] and
+[=P2|Update Render Template=] run and [=S1|Render Template Storage=] resides.
+
+
+
+
+ C2 Holder System
+
+
+The system boundary, typically a mobile or desktop digital wallet
+application, within which the holder's instance of the [=P3|Render=] process
+runs and where the holder observes the rendering.
+
+
+
+
+ C3 Verifier System
+
+
+The system boundary, typically a verification application or service, within
+which the verifier's instance of the [=P3|Render=] process runs and where the
+verifier observes the rendering.
+
+
+
+
+
+
+
+
+ Stakeholders
+
+ E1 Issuer
+
+An issuer is an entity that issues [=verifiable credentials=] and, using the
+Render Method specification, associates render templates with them.
+
+
+The issuer's primary goal is that its credentials are presented to people in
+the way it intends, so that an observer forms an accurate understanding of
+what the issuer attested. The issuer also has an interest in controlling how
+that intended presentation evolves: some issuers want the presentation fixed
+at issuance and protected against modification, while others want to update
+branding or layout over the lifetime of a long-lived credential. Issuers that
+host their templates have a further interest in not turning template
+retrieval into a means of tracking where and when their credentials are
+displayed.
+
+
+ E2 Holder
+
+A holder is a person or organization that holds [=verifiable credentials=]
+and renders them to view or review them, typically within a digital wallet.
+
+
+The holder's primary goal is to see a faithful, usable rendering of a
+credential. Holders have a strong interest in privacy, in that rendering a
+credential does not reveal to the issuer or a third party that, when, or
+where the credential is being displayed. Holders also have an interest in
+safety and availability: rendering untrusted template content does not
+compromise their device or expose their data, and rendering works within the
+constraints of their device, including when offline.
+
+
+ E3 Verifier
+
+A verifier is an entity that evaluates [=verifiable credentials=] and renders
+them as part of reviewing them, for example to let a person confirm a
+credential against the issuer's intended presentation.
+
+
+The verifier's primary goal is that the rendering it shows accurately
+reflects the issuer's intent and the credential's actual claims, so that a
+person is not misled into accepting a credential that does not meet the
+verifier's purpose. Like the holder, the verifier has an interest in
+rendering untrusted content safely, without compromising the verifier
+system.
+
+
+
+
+ Threats
+
+
+
+
+
+
+
+ Contributing
+
+This threat model is maintained alongside the Verifiable Credentials Render
+Method specification. Feedback, bug reports, and proposed additions are
+welcome via the
+issue
+tracker.
+
+
+ Adding a Threat
+
+To add a new threat to the model, provide the following components:
+
+
+ - A short, descriptive name
+ -
+A unique ID (letter T followed by an integer,
+e.g., T1)
+
+ -
+A description of the threat with enough detail for readers to understand
+its scope
+
+ -
+One or more responses, each with a name, unique ID (letter R
+ followed by an integer), response type (Eliminate, Reduce,
+Transfer, or Accept), and description
+
+ -
+A list of affected DFD elements drawn from the dictionary above
+
+ -
+The threat taxonomy used (e.g., STRIDE) and the threat class within that
+taxonomy
+
+
+
+Create a YAML file following the pattern of the Data Integrity threat
+model's threat files, then register it in threats/outline.yaml
+by adding its filename to the files list and its threat ID to
+the appropriate category in the categories list. No changes to
+index.html are required.
+
+
+
+
+
+
+
diff --git a/threat-model/threats/js-yaml.min.js b/threat-model/threats/js-yaml.min.js
new file mode 100644
index 0000000..bdd8eef
--- /dev/null
+++ b/threat-model/threats/js-yaml.min.js
@@ -0,0 +1,2 @@
+/*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).jsyaml={})}(this,(function(e){"use strict";function t(e){return null==e}var n={isNothing:t,isObject:function(e){return"object"==typeof e&&null!==e},toArray:function(e){return Array.isArray(e)?e:t(e)?[]:[e]},repeat:function(e,t){var n,i="";for(n=0;nl&&(t=i-l+(o=" ... ").length),n-i>l&&(n=i+l-(a=" ...").length),{str:o+e.slice(t,n).replace(/\t/g,"→")+a,pos:i-t+o.length}}function l(e,t){return n.repeat(" ",t-e.length)+e}var c=function(e,t){if(t=Object.create(t||null),!e.buffer)return null;t.maxLength||(t.maxLength=79),"number"!=typeof t.indent&&(t.indent=1),"number"!=typeof t.linesBefore&&(t.linesBefore=3),"number"!=typeof t.linesAfter&&(t.linesAfter=2);for(var i,r=/\r?\n|\r|\0/g,o=[0],c=[],s=-1;i=r.exec(e.buffer);)c.push(i.index),o.push(i.index+i[0].length),e.position<=i.index&&s<0&&(s=o.length-2);s<0&&(s=o.length-1);var u,p,f="",d=Math.min(e.line+t.linesAfter,c.length).toString().length,h=t.maxLength-(t.indent+d+3);for(u=1;u<=t.linesBefore&&!(s-u<0);u++)p=a(e.buffer,o[s-u],c[s-u],e.position-(o[s]-o[s-u]),h),f=n.repeat(" ",t.indent)+l((e.line-u+1).toString(),d)+" | "+p.str+"\n"+f;for(p=a(e.buffer,o[s],c[s],e.position,h),f+=n.repeat(" ",t.indent)+l((e.line+1).toString(),d)+" | "+p.str+"\n",f+=n.repeat("-",t.indent+d+3+p.pos)+"^\n",u=1;u<=t.linesAfter&&!(s+u>=c.length);u++)p=a(e.buffer,o[s+u],c[s+u],e.position-(o[s]-o[s+u]),h),f+=n.repeat(" ",t.indent)+l((e.line+u+1).toString(),d)+" | "+p.str+"\n";return f.replace(/\n$/,"")},s=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],u=["scalar","sequence","mapping"];var p=function(e,t){if(t=t||{},Object.keys(t).forEach((function(t){if(-1===s.indexOf(t))throw new o('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')})),this.options=t,this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.representName=t.representName||null,this.defaultStyle=t.defaultStyle||null,this.multi=t.multi||!1,this.styleAliases=function(e){var t={};return null!==e&&Object.keys(e).forEach((function(n){e[n].forEach((function(e){t[String(e)]=n}))})),t}(t.styleAliases||null),-1===u.indexOf(this.kind))throw new o('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')};function f(e,t){var n=[];return e[t].forEach((function(e){var t=n.length;n.forEach((function(n,i){n.tag===e.tag&&n.kind===e.kind&&n.multi===e.multi&&(t=i)})),n[t]=e})),n}function d(e){return this.extend(e)}d.prototype.extend=function(e){var t=[],n=[];if(e instanceof p)n.push(e);else if(Array.isArray(e))n=n.concat(e);else{if(!e||!Array.isArray(e.implicit)&&!Array.isArray(e.explicit))throw new o("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");e.implicit&&(t=t.concat(e.implicit)),e.explicit&&(n=n.concat(e.explicit))}t.forEach((function(e){if(!(e instanceof p))throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(e.loadKind&&"scalar"!==e.loadKind)throw new o("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(e.multi)throw new o("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")})),n.forEach((function(e){if(!(e instanceof p))throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object.")}));var i=Object.create(d.prototype);return i.implicit=(this.implicit||[]).concat(t),i.explicit=(this.explicit||[]).concat(n),i.compiledImplicit=f(i,"implicit"),i.compiledExplicit=f(i,"explicit"),i.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function i(e){e.multi?(n.multi[e.kind].push(e),n.multi.fallback.push(e)):n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;e=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},octal:function(e){return e>=0?"0o"+e.toString(8):"-0o"+e.toString(8).slice(1)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),x=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");var I=/^[-+]?[0-9]+e/;var S=new p("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!x.test(e)||"_"===e[e.length-1])},construct:function(e){var t,n;return n="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:n*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||n.isNegativeZero(e))},represent:function(e,t){var i;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(n.isNegativeZero(e))return"-0.0";return i=e.toString(10),I.test(i)?i.replace("e",".e"):i},defaultStyle:"lowercase"}),O=b.extend({implicit:[A,v,C,S]}),j=O,T=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),N=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");var F=new p("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:function(e){return null!==e&&(null!==T.exec(e)||null!==N.exec(e))},construct:function(e){var t,n,i,r,o,a,l,c,s=0,u=null;if(null===(t=T.exec(e))&&(t=N.exec(e)),null===t)throw new Error("Date resolve error");if(n=+t[1],i=+t[2]-1,r=+t[3],!t[4])return new Date(Date.UTC(n,i,r));if(o=+t[4],a=+t[5],l=+t[6],t[7]){for(s=t[7].slice(0,3);s.length<3;)s+="0";s=+s}return t[9]&&(u=6e4*(60*+t[10]+ +(t[11]||0)),"-"===t[9]&&(u=-u)),c=new Date(Date.UTC(n,i,r,o,a,l,s)),u&&c.setTime(c.getTime()-u),c},instanceOf:Date,represent:function(e){return e.toISOString()}});var E=new p("tag:yaml.org,2002:merge",{kind:"scalar",resolve:function(e){return"<<"===e||null===e}}),M="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";var L=new p("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i=0,r=e.length,o=M;for(n=0;n64)){if(t<0)return!1;i+=6}return i%8==0},construct:function(e){var t,n,i=e.replace(/[\r\n=]/g,""),r=i.length,o=M,a=0,l=[];for(t=0;t>16&255),l.push(a>>8&255),l.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return 0===(n=r%4*6)?(l.push(a>>16&255),l.push(a>>8&255),l.push(255&a)):18===n?(l.push(a>>10&255),l.push(a>>2&255)):12===n&&l.push(a>>4&255),new Uint8Array(l)},predicate:function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)},represent:function(e){var t,n,i="",r=0,o=e.length,a=M;for(t=0;t>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return 0===(n=o%3)?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}}),_=Object.prototype.hasOwnProperty,D=Object.prototype.toString;var U=new p("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,n,i,r,o,a=[],l=e;for(t=0,n=l.length;t>10),56320+(e-65536&1023))}for(var ie=new Array(256),re=new Array(256),oe=0;oe<256;oe++)ie[oe]=te(oe)?1:0,re[oe]=te(oe);function ae(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||K,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}function le(e,t){var n={name:e.filename,buffer:e.input.slice(0,-1),position:e.position,line:e.line,column:e.position-e.lineStart};return n.snippet=c(n),new o(t,n)}function ce(e,t){throw le(e,t)}function se(e,t){e.onWarning&&e.onWarning.call(null,le(e,t))}var ue={YAML:function(e,t,n){var i,r,o;null!==e.version&&ce(e,"duplication of %YAML directive"),1!==n.length&&ce(e,"YAML directive accepts exactly one argument"),null===(i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]))&&ce(e,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),o=parseInt(i[2],10),1!==r&&ce(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=o<2,1!==o&&2!==o&&se(e,"unsupported YAML version of the document")},TAG:function(e,t,n){var i,r;2!==n.length&&ce(e,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],G.test(i)||ce(e,"ill-formed tag handle (first argument) of the TAG directive"),P.call(e.tagMap,i)&&ce(e,'there is a previously declared suffix for "'+i+'" tag handle'),V.test(r)||ce(e,"ill-formed tag prefix (second argument) of the TAG directive");try{r=decodeURIComponent(r)}catch(t){ce(e,"tag prefix is malformed: "+r)}e.tagMap[i]=r}};function pe(e,t,n,i){var r,o,a,l;if(t1&&(e.result+=n.repeat("\n",t-1))}function be(e,t){var n,i,r=e.tag,o=e.anchor,a=[],l=!1;if(-1!==e.firstTabInLine)return!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=a),i=e.input.charCodeAt(e.position);0!==i&&(-1!==e.firstTabInLine&&(e.position=e.firstTabInLine,ce(e,"tab characters must not be used in indentation")),45===i)&&z(e.input.charCodeAt(e.position+1));)if(l=!0,e.position++,ge(e,!0,-1)&&e.lineIndent<=t)a.push(null),i=e.input.charCodeAt(e.position);else if(n=e.line,we(e,t,3,!1,!0),a.push(e.result),ge(e,!0,-1),i=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&0!==i)ce(e,"bad indentation of a sequence entry");else if(e.lineIndentt?g=1:e.lineIndent===t?g=0:e.lineIndentt?g=1:e.lineIndent===t?g=0:e.lineIndentt)&&(y&&(a=e.line,l=e.lineStart,c=e.position),we(e,t,4,!0,r)&&(y?g=e.result:m=e.result),y||(de(e,f,d,h,g,m,a,l,c),h=g=m=null),ge(e,!0,-1),s=e.input.charCodeAt(e.position)),(e.line===o||e.lineIndent>t)&&0!==s)ce(e,"bad indentation of a mapping entry");else if(e.lineIndent=0))break;0===o?ce(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):u?ce(e,"repeat of an indentation width identifier"):(p=t+o-1,u=!0)}if(Q(a)){do{a=e.input.charCodeAt(++e.position)}while(Q(a));if(35===a)do{a=e.input.charCodeAt(++e.position)}while(!J(a)&&0!==a)}for(;0!==a;){for(he(e),e.lineIndent=0,a=e.input.charCodeAt(e.position);(!u||e.lineIndentp&&(p=e.lineIndent),J(a))f++;else{if(e.lineIndent
0){for(r=a,o=0;r>0;r--)(a=ee(l=e.input.charCodeAt(++e.position)))>=0?o=(o<<4)+a:ce(e,"expected hexadecimal character");e.result+=ne(o),e.position++}else ce(e,"unknown escape sequence");n=i=e.position}else J(l)?(pe(e,n,i,!0),ye(e,ge(e,!1,t)),n=i=e.position):e.position===e.lineStart&&me(e)?ce(e,"unexpected end of the document within a double quoted scalar"):(e.position++,i=e.position)}ce(e,"unexpected end of the stream within a double quoted scalar")}(e,d)?y=!0:!function(e){var t,n,i;if(42!==(i=e.input.charCodeAt(e.position)))return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!z(i)&&!X(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&ce(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),P.call(e.anchorMap,n)||ce(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],ge(e,!0,-1),!0}(e)?function(e,t,n){var i,r,o,a,l,c,s,u,p=e.kind,f=e.result;if(z(u=e.input.charCodeAt(e.position))||X(u)||35===u||38===u||42===u||33===u||124===u||62===u||39===u||34===u||37===u||64===u||96===u)return!1;if((63===u||45===u)&&(z(i=e.input.charCodeAt(e.position+1))||n&&X(i)))return!1;for(e.kind="scalar",e.result="",r=o=e.position,a=!1;0!==u;){if(58===u){if(z(i=e.input.charCodeAt(e.position+1))||n&&X(i))break}else if(35===u){if(z(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&me(e)||n&&X(u))break;if(J(u)){if(l=e.line,c=e.lineStart,s=e.lineIndent,ge(e,!1,-1),e.lineIndent>=t){a=!0,u=e.input.charCodeAt(e.position);continue}e.position=o,e.line=l,e.lineStart=c,e.lineIndent=s;break}}a&&(pe(e,r,o,!1),ye(e,e.line-l),r=o=e.position,a=!1),Q(u)||(o=e.position+1),u=e.input.charCodeAt(++e.position)}return pe(e,r,o,!1),!!e.result||(e.kind=p,e.result=f,!1)}(e,d,1===i)&&(y=!0,null===e.tag&&(e.tag="?")):(y=!0,null===e.tag&&null===e.anchor||ce(e,"alias node should not have any properties")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===g&&(y=c&&be(e,h))),null===e.tag)null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);else if("?"===e.tag){for(null!==e.result&&"scalar"!==e.kind&&ce(e,'unacceptable node kind for !> tag; it should be "scalar", not "'+e.kind+'"'),s=0,u=e.implicitTypes.length;s"),null!==e.result&&f.kind!==e.kind&&ce(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+f.kind+'", not "'+e.kind+'"'),f.resolve(e.result,e.tag)?(e.result=f.construct(e.result,e.tag),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):ce(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")}return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||y}function ke(e){var t,n,i,r,o=e.position,a=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap=Object.create(null),e.anchorMap=Object.create(null);0!==(r=e.input.charCodeAt(e.position))&&(ge(e,!0,-1),r=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==r));){for(a=!0,r=e.input.charCodeAt(++e.position),t=e.position;0!==r&&!z(r);)r=e.input.charCodeAt(++e.position);for(i=[],(n=e.input.slice(t,e.position)).length<1&&ce(e,"directive name must not be less than one character in length");0!==r;){for(;Q(r);)r=e.input.charCodeAt(++e.position);if(35===r){do{r=e.input.charCodeAt(++e.position)}while(0!==r&&!J(r));break}if(J(r))break;for(t=e.position;0!==r&&!z(r);)r=e.input.charCodeAt(++e.position);i.push(e.input.slice(t,e.position))}0!==r&&he(e),P.call(ue,n)?ue[n](e,n,i):se(e,'unknown document directive "'+n+'"')}ge(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,ge(e,!0,-1)):a&&ce(e,"directives end mark is expected"),we(e,e.lineIndent-1,4,!1,!0),ge(e,!0,-1),e.checkLineBreaks&&H.test(e.input.slice(o,e.position))&&se(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&me(e)?46===e.input.charCodeAt(e.position)&&(e.position+=3,ge(e,!0,-1)):e.position=55296&&i<=56319&&t+1=56320&&n<=57343?1024*(i-55296)+n-56320+65536:i}function Re(e){return/^\n* /.test(e)}function Be(e,t,n,i,r,o,a,l){var c,s,u=0,p=null,f=!1,d=!1,h=-1!==i,g=-1,m=De(s=Ye(e,0))&&s!==Oe&&!_e(s)&&45!==s&&63!==s&&58!==s&&44!==s&&91!==s&&93!==s&&123!==s&&125!==s&&35!==s&&38!==s&&42!==s&&33!==s&&124!==s&&61!==s&&62!==s&&39!==s&&34!==s&&37!==s&&64!==s&&96!==s&&function(e){return!_e(e)&&58!==e}(Ye(e,e.length-1));if(t||a)for(c=0;c=65536?c+=2:c++){if(!De(u=Ye(e,c)))return 5;m=m&&qe(u,p,l),p=u}else{for(c=0;c=65536?c+=2:c++){if(10===(u=Ye(e,c)))f=!0,h&&(d=d||c-g-1>i&&" "!==e[g+1],g=c);else if(!De(u))return 5;m=m&&qe(u,p,l),p=u}d=d||h&&c-g-1>i&&" "!==e[g+1]}return f||d?n>9&&Re(e)?5:a?2===o?5:2:d?4:3:!m||a||r(e)?2===o?5:2:1}function Ke(e,t,n,i,r){e.dump=function(){if(0===t.length)return 2===e.quotingType?'""':"''";if(!e.noCompatMode&&(-1!==Te.indexOf(t)||Ne.test(t)))return 2===e.quotingType?'"'+t+'"':"'"+t+"'";var a=e.indent*Math.max(1,n),l=-1===e.lineWidth?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-a),c=i||e.flowLevel>-1&&n>=e.flowLevel;switch(Be(t,c,e.indent,l,(function(t){return function(e,t){var n,i;for(n=0,i=e.implicitTypes.length;n"+Pe(t,e.indent)+We(Me(function(e,t){var n,i,r=/(\n+)([^\n]*)/g,o=(l=e.indexOf("\n"),l=-1!==l?l:e.length,r.lastIndex=l,He(e.slice(0,l),t)),a="\n"===e[0]||" "===e[0];var l;for(;i=r.exec(e);){var c=i[1],s=i[2];n=" "===s[0],o+=c+(a||n||""===s?"":"\n")+He(s,t),a=n}return o}(t,l),a));case 5:return'"'+function(e){for(var t,n="",i=0,r=0;r=65536?r+=2:r++)i=Ye(e,r),!(t=je[i])&&De(i)?(n+=e[r],i>=65536&&(n+=e[r+1])):n+=t||Fe(i);return n}(t)+'"';default:throw new o("impossible error: invalid scalar style")}}()}function Pe(e,t){var n=Re(e)?String(t):"",i="\n"===e[e.length-1];return n+(i&&("\n"===e[e.length-2]||"\n"===e)?"+":i?"":"-")+"\n"}function We(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function He(e,t){if(""===e||" "===e[0])return e;for(var n,i,r=/ [^ ]/g,o=0,a=0,l=0,c="";n=r.exec(e);)(l=n.index)-o>t&&(i=a>o?a:l,c+="\n"+e.slice(o,i),o=i+1),a=l;return c+="\n",e.length-o>t&&a>o?c+=e.slice(o,a)+"\n"+e.slice(a+1):c+=e.slice(o),c.slice(1)}function $e(e,t,n,i){var r,o,a,l="",c=e.tag;for(r=0,o=n.length;r tag resolver accepts not "'+s+'" style');i=c.represent[s](t,s)}e.dump=i}return!0}return!1}function Ve(e,t,n,i,r,a,l){e.tag=null,e.dump=n,Ge(e,n,!1)||Ge(e,n,!0);var c,s=Ie.call(e.dump),u=i;i&&(i=e.flowLevel<0||e.flowLevel>t);var p,f,d="[object Object]"===s||"[object Array]"===s;if(d&&(f=-1!==(p=e.duplicates.indexOf(n))),(null!==e.tag&&"?"!==e.tag||f||2!==e.indent&&t>0)&&(r=!1),f&&e.usedDuplicates[p])e.dump="*ref_"+p;else{if(d&&f&&!e.usedDuplicates[p]&&(e.usedDuplicates[p]=!0),"[object Object]"===s)i&&0!==Object.keys(e.dump).length?(!function(e,t,n,i){var r,a,l,c,s,u,p="",f=e.tag,d=Object.keys(n);if(!0===e.sortKeys)d.sort();else if("function"==typeof e.sortKeys)d.sort(e.sortKeys);else if(e.sortKeys)throw new o("sortKeys must be a boolean or a function");for(r=0,a=d.length;r1024)&&(e.dump&&10===e.dump.charCodeAt(0)?u+="?":u+="? "),u+=e.dump,s&&(u+=Le(e,t)),Ve(e,t+1,c,!0,s)&&(e.dump&&10===e.dump.charCodeAt(0)?u+=":":u+=": ",p+=u+=e.dump));e.tag=f,e.dump=p||"{}"}(e,t,e.dump,r),f&&(e.dump="&ref_"+p+e.dump)):(!function(e,t,n){var i,r,o,a,l,c="",s=e.tag,u=Object.keys(n);for(i=0,r=u.length;i1024&&(l+="? "),l+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),Ve(e,t,a,!1,!1)&&(c+=l+=e.dump));e.tag=s,e.dump="{"+c+"}"}(e,t,e.dump),f&&(e.dump="&ref_"+p+" "+e.dump));else if("[object Array]"===s)i&&0!==e.dump.length?(e.noArrayIndent&&!l&&t>0?$e(e,t-1,e.dump,r):$e(e,t,e.dump,r),f&&(e.dump="&ref_"+p+e.dump)):(!function(e,t,n){var i,r,o,a="",l=e.tag;for(i=0,r=n.length;i",e.dump=c+" "+e.dump)}return!0}function Ze(e,t){var n,i,r=[],o=[];for(Je(e,r,o),n=0,i=o.length;n-
+ An [=E1|issuer=] generally has a strong interest in how the
+ [=verifiable credentials=] it issues are presented to people. Without a
+ render method asserted by the issuer, the choice of how to display a
+ credential falls entirely to the [=E2|holder=] or [=E3|verifier=] software
+ that performs the [=P3|Render=] process. That software can present the
+ issuer's credential in a way that goes against the issuer's desires, for
+ example by using a layout that omits or de-emphasizes important claims,
+ reorders or relabels fields, drops disclaimers or validity information, or
+ applies branding that misrepresents the issuer. The person viewing the
+ credential then forms an incorrect understanding of what the issuer actually
+ attested, even though the underlying credential is authentic and unmodified.
+ This is the primary threat the Render Method specification is designed to
+ address: it gives the issuer a way to assert, within the credential itself, a
+ correct and intended presentation, published during
+ [=P1|Publish Render Template=] to [=S1|Render Template Storage=] and
+ retrieved by consumers via [=F1|Retrieve Render Template=].
+response:
+ - id: R1
+ name: Issuer-Asserted Render Method
+ type: Reduce
+ description: >-
+ The [=E1|issuer=] asserts one or more render methods in the
+ [=verifiable credential=], giving consumers an issuer-intended way to
+ present the credential rather than inventing their own. This does not
+ compel a consumer to use the issuer's presentation, but it makes the
+ issuer's intended rendering available and unambiguous.
+ - id: R2
+ name: Bind the Presentation to the Credential
+ type: Reduce
+ description: >-
+ So that the asserted presentation is not silently substituted or
+ altered, the issuer treats the render template as an integrity-protected
+ external resource. Because the render template is an external resource
+ whose content affects how the credential is presented, the integrity
+ considerations for it are a subset of
+ Tampering
+ with Unprotected External Resources in the [[[VC-DATA-MODEL-2.0]]]
+ threat model; readers are referred there for the full analysis and
+ responses. An issuer that intends to update branding or layout over time
+ instead deliberately leaves the template mutable, accepting that
+ previously issued credentials render using the updated template.
diff --git a/threat-model/threats/t2-unsafe-rendering-of-untrusted-data.yaml b/threat-model/threats/t2-unsafe-rendering-of-untrusted-data.yaml
new file mode 100644
index 0000000..dc9639f
--- /dev/null
+++ b/threat-model/threats/t2-unsafe-rendering-of-untrusted-data.yaml
@@ -0,0 +1,64 @@
+id: T2
+name: Unsafe Rendering of Untrusted Data
+elements: [P3, F1, O1, S1, C2, C3]
+tags: [security]
+taxonomyName: STRIDE
+taxonomyClass: Elevation of Privilege
+description: >-
+ The [=P3|Render=] process combines a render template retrieved via
+ [=F1|Retrieve Render Template=] with claim values taken from a
+ [=verifiable credential=], and produces output a person perceives, which can
+ be visual, auditory, tactile, or textual. In the `html` render suite the
+ template is an HTML fragment that carries its own CSS and JavaScript, and
+ that code runs in order to render the credential. Both the template code and
+ the credential values placed in the data block are untrusted input from the
+ point of view of the software running the [=P3|Render=] process. Template
+ code, or a credential value it consumes, can attempt to reach the
+ surrounding [=C2|holder system=] or [=C3|verifier system=], navigate away, or
+ load and contact external locations, giving hostile content access to the
+ context in which it renders. Successful verification of the credential's
+ securing mechanism confirms that the content is authentic, not that it is
+ safe to render, so hostile content can arrive inside a well-formed, verified
+ credential. At the level of the data model this threat is a render-time
+ instance of
+ Code
+ Injection via Credential Content in the [[[VC-DATA-MODEL-2.0]]] threat
+ model; the additional concern here is that the render method deliberately
+ executes template-supplied code as part of rendering.
+response:
+ - id: R1
+ name: Confine the Render Process
+ type: Reduce
+ description: >-
+ The `html` render suite runs the template code in a confined rendering
+ environment rather than disabling the code it relies on. The
+ specification defines this in terms of required security outcomes: the
+ environment allows the template's JavaScript to render the credential
+ while preventing it from navigating away, reaching the surrounding
+ host page, and loading or contacting external content. How the
+ environment enforces those outcomes is an implementation detail, and
+ sandboxing can be achieved in a variety of ways. A browser based
+ implementation, for example, can realize it by hosting the template in a
+ sandboxed iframe that permits scripting but blocks navigation and
+ top-level access, together with wrapper code that adds a Content
+ Security Policy denying network requests; other environments provide
+ equivalent isolation through different mechanisms. Whatever the
+ mechanism, the environment further limits the resources and capabilities
+ exposed to the template so that hostile template code has as little to
+ act on as possible.
+ - id: R2
+ name: Validate Rendered Data and Prefer Constrained Suites
+ type: Reduce
+ description: >-
+ Software performing the [=P3|Render=] process treats all template and
+ credential content as untrusted regardless of verification outcome and
+ validates it against the expected shape for the render suite in use. The
+ `card` render suite constrains a template to data that produces a
+ standardized JSON output validated against a fixed schema, and the `nfc`
+ render suite carries a static payload, so neither executes
+ template-supplied code; selecting a constrained suite where it meets the
+ use case removes the code-execution surface entirely. Further analysis
+ and responses appear in
+ Code
+ Injection via Credential Content in the [[[VC-DATA-MODEL-2.0]]]
+ threat model.
diff --git a/threat-model/threats/t3-tracking-via-template-and-resource-fetches.yaml b/threat-model/threats/t3-tracking-via-template-and-resource-fetches.yaml
new file mode 100644
index 0000000..51fcd00
--- /dev/null
+++ b/threat-model/threats/t3-tracking-via-template-and-resource-fetches.yaml
@@ -0,0 +1,57 @@
+id: T3
+name: Tracking via Template and Resource Fetches
+elements: [P3, F1, O1, S1, E1, C1]
+tags: [privacy]
+taxonomyName: STRIDE
+taxonomyClass: Information Disclosure
+description: >-
+ A render method can reference its template, and the template can in turn
+ reference further resources such as images, by location rather than
+ embedding them. When the [=P3|Render=] process resolves such a reference over
+ the network via [=F1|Retrieve Render Template=], the request reveals to the
+ [=E1|issuer=] or to whoever hosts the resource that a particular credential
+ is being rendered, along with the requester's network address, timing, and
+ other request metadata. An issuer that assigns a distinct template or
+ resource location per credential turns each render into a signal that
+ identifies the specific credential and correlates the times and places it is
+ displayed. In the `html` render suite, a template that is permitted to load
+ external content could also encode credential data into the URLs of
+ resources it fetches, such as image requests, exfiltrating rendered data to
+ an external location. Embedding the template and its resources in the
+ credential avoids the network requests entirely, at the cost of increasing
+ the size of the credential, which can be significant for constrained
+ devices. This threat is the render-time expression of
+ Device
+ Tracking and Fingerprinting in the [[[VC-DATA-MODEL-2.0]]] threat model,
+ to which readers are referred for the general analysis.
+response:
+ - id: R1
+ name: Block Network Access During Rendering
+ type: Reduce
+ description: >-
+ The rendering environment prevents the template from initiating its own
+ network requests, so that a template cannot fetch per-user resources or
+ encode credential data into outbound requests while rendering. The
+ specification requires this as a security outcome for the `html` render
+ suite; how it is enforced is an implementation detail. A browser based
+ implementation, for example, can enforce it with a Content Security
+ Policy applied by the wrapper code, while other rendering
+ environments deny network egress by other means. Whatever the mechanism,
+ blocking network access during rendering closes the linked-resource
+ exfiltration vector for content loaded while the template runs.
+ - id: R2
+ name: Embed, Cache, or Relay Template Retrieval
+ type: Reduce
+ description: >-
+ Where the retrieval of a template cannot be avoided, the requester
+ protects it rather than revealing each render to the host. As the
+ specification notes, template references are aggressively cached on the
+ client so that a template is fetched at most once, and retrieval is
+ performed through Oblivious HTTP or a similar relay that disassociates
+ the requester from the requested origin. An issuer that prioritizes
+ offline use and unlinkability embeds the template and its resources in
+ the credential, accepting the increase in credential size. The general
+ responses in
+ Device
+ Tracking and Fingerprinting in the [[[VC-DATA-MODEL-2.0]]] threat
+ model apply here as well.
diff --git a/threat-model/threats/t4-harmful-rendering-output.yaml b/threat-model/threats/t4-harmful-rendering-output.yaml
new file mode 100644
index 0000000..8f26e96
--- /dev/null
+++ b/threat-model/threats/t4-harmful-rendering-output.yaml
@@ -0,0 +1,43 @@
+id: T4
+name: Harmful Rendering Output
+elements: [P3, F1, O1, C2, C3]
+tags: [security]
+taxonomyName: STRIDE
+taxonomyClass: Denial of Service
+description: >-
+ Because a render template controls the visual, auditory, and tactile output
+ produced by the [=P3|Render=] process, a hostile template can be crafted to
+ harm the person viewing it rather than to inform them. A template can drive
+ the display with a rapidly flashing or strobing pattern capable of
+ triggering a seizure in a person with photosensitive epilepsy, or emit
+ sudden loud, screeching, or otherwise disruptive sound. The harm lands on
+ the human observing the rendering, in the physical environment around the
+ [=C2|holder system=] or [=C3|verifier system=], rather than on the data or
+ the software, and the underlying credential can be entirely authentic. The
+ ability of arbitrary displayed or played content to affect a susceptible
+ person is a real-world condition of any rendering technology and is outside
+ what the Render Method specification itself governs, so it is treated as an
+ external threat.
+response:
+ - id: R1
+ name: Acknowledge as an External Condition
+ type: Accept
+ description: >-
+ The possibility that arbitrary visual or auditory output can affect a
+ susceptible person exists for any technology that renders content and is
+ not something the Render Method specification eliminates. This threat is
+ documented so that implementers and deployers are aware of it and can
+ apply platform-level and accessibility protections appropriate to their
+ context.
+ - id: R2
+ name: Constrain and Vet Rendering Output
+ type: Reduce
+ description: >-
+ Deployers reduce exposure by favoring render suites and output modes
+ that constrain what a template can do to the person, such as the `card`
+ suite, which produces a standardized, static output rather than
+ animated or sound-producing content, and by honoring platform
+ accessibility settings that limit motion, flashing, and sound. Where
+ templates originate from parties a deployer does not trust, the
+ rendering output is reviewed or transformed to a safe static form before
+ it is shown.
diff --git a/threat-model/threats/t5-inherited-rendering-environment-attack-surface.yaml b/threat-model/threats/t5-inherited-rendering-environment-attack-surface.yaml
new file mode 100644
index 0000000..d037ba6
--- /dev/null
+++ b/threat-model/threats/t5-inherited-rendering-environment-attack-surface.yaml
@@ -0,0 +1,51 @@
+id: T5
+name: Inherited Rendering Environment Attack Surface
+elements: [P3, C2, C3]
+tags: [security]
+taxonomyName: STRIDE
+taxonomyClass: Elevation of Privilege
+description: >-
+ The `html` render suite performs the [=P3|Render=] process by executing
+ template code inside a confined rendering environment and relies on that
+ environment's isolation mechanisms to keep the code from navigating away,
+ reaching the surrounding host page, or contacting the network. The
+ specification defines these confinement requirements as security outcomes
+ and leaves the mechanism that enforces them to the implementation; a sandbox
+ can be provided in a variety of ways. A common realization is a browser,
+ which enforces confinement through the iframe `sandbox` attribute, Content
+ Security Policy, and `srcdoc` isolation, but other environments provide
+ equivalent isolation through different mechanisms. Whatever mechanism is
+ chosen, the safety of rendering inherits the security of that underlying
+ mechanism: a defect that lets the confinement be escaped, a policy be
+ bypassed, or the isolation between the template and the host be broken
+ becomes a defect in the render process, even when the render method is used
+ exactly as specified. The suite also inherits the ambient risk that a
+ rendering environment, or its configuration, does not enforce the required
+ outcomes faithfully. This is a dependency threat: the Render Method
+ specification does not implement these protections itself but depends on the
+ chosen environment enforcing them correctly and being available.
+response:
+ - id: R1
+ name: Rely on Maintained Rendering Environments
+ type: Transfer
+ description: >-
+ Deployers perform the [=P3|Render=] process using an actively maintained
+ rendering environment that receives timely security updates, such as a
+ current browser engine, so that responsibility for the correctness of
+ the environment's isolation mechanisms rests with its vendor and known
+ defects are patched promptly.
+ - id: R2
+ name: Defense in Depth Beyond a Single Mechanism
+ type: Reduce
+ description: >-
+ Rather than relying on a single isolation primitive, the rendering
+ environment combines the protections that achieve the specification's
+ required outcomes: confinement of the executing template, denial of
+ network egress, digest checking of retrieved template code, and
+ prevention of host access, so that a failure in one mechanism does not by
+ itself expose the [=C2|holder system=] or [=C3|verifier system=]. Because
+ the choice of sandboxing mechanism is an implementation detail, an
+ environment providing equivalent isolation is selected wherever the
+ render method runs, whether or not it is a browser, and constrained
+ suites such as `card` and `nfc`, which do not execute template code, are
+ preferred where they meet the use case.
diff --git a/threat-model/threats/threat-model.css b/threat-model/threats/threat-model.css
new file mode 100644
index 0000000..27e0b05
--- /dev/null
+++ b/threat-model/threats/threat-model.css
@@ -0,0 +1,158 @@
+table.visual-terminology,
+table.dfd-dictionary {
+ padding: 1em;
+ border: 1px solid black;
+}
+
+table.visual-terminology th,
+table.visual-terminology td,
+table.dfd-dictionary th,
+table.dfd-dictionary td {
+ border-bottom: 1px solid black;
+ border-collapse: collapse;
+ padding: .5em;
+}
+
+table.visual-terminology tr {
+ border-collapse: collapse;
+}
+
+table.visual-terminology th {
+ width:11em;
+ font-weight: bold;
+}
+
+table.visual-terminology td.term-def {
+
+}
+
+
+table.visual-terminology td.term-img {
+ width: 11em;
+}
+
+
+table.dfd-dictionary {
+}
+
+
+table.dfd-dictionary td.dfd-id {
+ width: 3em;
+ font-weight: bold;
+
+}
+table.dfd-dictionary td.dfd-name {
+ width: 10em;
+}
+table.dfd-dictionary td.dfd-type {
+ width: 6em;
+}
+
+#threat-toc {
+ counter-reset: threat;
+}
+
+ol.threat-toc {
+ list-style: none;
+}
+
+ol.threat-toc li::before {
+ content: "T" attr(value) ". ";
+ font-weight: bold;
+}
+
+ol.threat-toc li.section-title::before {
+ font-weight: bold;
+ counter-increment: none;
+ content: "";
+}
+
+ol.threat-toc li.section-title {
+ font-weight: bold;
+ font-style: italic;
+}
+
+table.threat td.threat-name span.index-link {
+ position:absolute;
+ right: 10px;
+ height: 1em;
+ font-size: small;
+ margin-top: -0.7em;
+ top: 50%;
+}
+ol.threat-toc h5 {
+ margin:0;
+}
+
+table.threat {
+ border: thin solid black;
+ border-collapse:collapse;
+ margin: 1em 0 1em 0;
+ width:100%;
+ padding-left:30px;
+}
+table.threat a.self-link {
+ left:-35px !important;
+}
+table.threat h3,
+table.threat h4,
+table.threat h5 {
+ color:black;
+ margin:0 0 0 -5px;
+ font-weight: bold;
+ font-style:normal;
+}
+table.threat td {
+ padding: 1em;
+ position:relative;
+ border-bottom: thin solid black;
+}
+
+table.threat td.threat-name {
+ font-weight:bold;
+ background-color: #c9daf7;
+}
+
+table.threat td.response-name {
+ font-weight:bold;
+ background-color: #d9ead4;
+}
+
+table.threat td.affected-component {
+ background-color: #fff2ce;
+}
+table.threat span.component-entry {
+ font-weight:bold;
+}
+table.threat td.analysis-taxonomy {
+ background-color: #fce5ce;
+}
+
+p.threatCategory {
+ font-size: larger;
+ font-weight: bolder;
+ margin:0;
+}
+
+section.threatDetail {
+ visibility: hidden;
+ height:1px;
+}
+
+span.threat-tag {
+ border-radius: 8px;
+ padding: 2px 5px;
+ color: white;
+ margin: 0px 2px;
+ font-weight: normal;
+ color: black;
+ background-color: #a9c9e8;
+}
+
+span.threat-tag.threat-tag-security {
+ background-color: #f4a7a3;
+}
+
+span.threat-tag.threat-tag-privacy {
+ background-color: #f8c49a;
+}
\ No newline at end of file
diff --git a/threat-model/threats/threat-model.js b/threat-model/threats/threat-model.js
new file mode 100644
index 0000000..8af7bb9
--- /dev/null
+++ b/threat-model/threats/threat-model.js
@@ -0,0 +1,420 @@
+/*
+Copyright 2026 Legendary Requirements
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+(function () {
+
+ // set up debugger output
+ // you can toggle different debugging namespaces by adjusting the
+ // value passed via a debug query parameter.
+ // e.g., ?debug=* will turn on all debugging output
+ // while ?debug=render will turn on just the render debugging output
+ // the value is a regex against debugging namespaces
+ ///////////////////////////////////////////////////////////////////////
+ if (typeof debug === "undefined") {
+ var debug = function () { };
+ } else {
+ const params = new URLSearchParams(window.location.search);
+ const debug_param = params.get('debug');
+ if (debug_param) {
+ localStorage.debug = debug_param;
+ console.log(`Debugging ${debug_param} enabled`);
+ }
+ }
+
+ // Set up globals
+ ///////////////////////////////////////////////////////////////////////
+ var threats = []; // Populated by calling RegisterThreats
+ var threatCategories = []; // Populated by calling RegisterCategories
+ var elementLabels = {}; // Populated by calling registerElements
+
+
+ // loadDefinitions(config)
+ // Loads the threat model definitions from YAML files: first the outline
+ // (threat categories, element labels, and the list of threat files),
+ // then each threat file the outline lists. The outline location can be
+ // overridden with the threatModelOutline configuration option; threat
+ // files are resolved relative to the outline's directory.
+ ///////////////////////////////////////////////////////////////////////
+ async function loadDefinitions(config) {
+ if (typeof jsyaml === "undefined") {
+ throw new Error("js-yaml is not loaded; add a