feat: add support for w3c vc data model v2#69
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughUpgrades jsonld-signatures dependency and updates the context loader accordingly. Adds VC v2 support alongside v1.1, introducing version-aware mandatory pointers, date-field validation, and base-vs-derived proof checks. Updates ECDSA-SD-2023 signing/deriving/verifying flows and tests to handle both versions. README samples switch to v2 context and fields. Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant VC as w3c-vc
participant Ctx as @trustvc/w3c-context
App->>VC: signCredential(credential, options)
VC->>Ctx: detect version via @context[0]
Note right of VC: Build mandatory pointers<br/>v1.1: /issuer, /issuanceDate<br/>v2: /issuer, (/validFrom if present)
VC-->>App: signed credential
sequenceDiagram
participant App
participant VC as w3c-vc
App->>VC: deriveCredential(credential, pointers)
VC->>VC: Check proofValue → base or derived?
alt Not base
VC-->>App: Error (must derive from base)
else Base
alt No /credentialSubject pointers
VC->>VC: Auto-add /credentialSubject
end
VC-->>App: derived credential (ECDSA-SD-2023)
end
sequenceDiagram
participant App
participant VC as w3c-vc
App->>VC: verifyCredential(credential)
VC->>VC: Check proofValue → base or derived?
alt Base
VC-->>App: verified=false, error "deriveCredential() first"
else Derived
VC->>VC: Validate dates (v1.1 vs v2), status, proof
VC-->>App: verification result
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 5
🔭 Outside diff range comments (3)
packages/w3c-vc/src/lib/helper/index.ts (3)
90-95: Date regex accepts invalid formats; escape literal dotsThe regex uses
.which matches any character. This can let invalid timestamps pass. Escape them to require literal dots.Apply this diff:
-const dateRegex = new RegExp( - '-?([1-9][0-9]{3,}|0[0-9]{3})' + - '-(0[1-9]|1[0-2])' + - '-(0[1-9]|[12][0-9]|3[01])' + - 'T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](.[0-9]+)?|(24:00:00(.0+)?))' + - '(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?', -); +const dateRegex = new RegExp( + '-?([1-9][0-9]{3,}|0[0-9]{3})' + + '-(0[1-9]|1[0-2])' + + '-(0[1-9]|[12][0-9]|3[01])' + + 'T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?|(24:00:00(\\.0+)?))' + + '(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?', +);
106-117: assertDateString reads raw property; use jsonld.getValues and type-checkIf the property is an array or object,
.test(value)can behave unexpectedly. Read via jsonld.getValues, ensure single string.Apply this diff:
-function assertDateString({ - credential, - prop, -}: { - credential: VerifiableCredential; - prop: string; -}): void { - const value = credential[prop]; - if (!dateRegex.test(value)) { - throw new Error(`"${prop}" must be a valid date: ${value}`); - } -} +function assertDateString({ + credential, + prop, +}: { + credential: VerifiableCredential; + prop: string; +}): void { + const values = jsonld.getValues(credential, prop); + if (values.length < 1) { + throw new Error(`"${prop}" must be present when provided.`); + } + if (values.length > 1) { + throw new Error(`"${prop}" property can only have one value.`); + } + const value = values[0]; + if (typeof value !== 'string' || !dateRegex.test(value)) { + throw new Error(`"${prop}" must be a valid date: ${value}`); + } +}
260-273: Misleading error message for unsupported proof typesThe thrown message mentions only BbsBlsSignature2020. If the mapping doesn’t include a proof type, the error should say “unsupported” to cover all cases.
Apply this diff:
- if (!proofTypeMapping[proofType]) { - throw new Error('"proof" type is not of BbsBlsSignature2020.'); - } + if (!proofTypeMapping[proofType]) { + throw new Error('"proof" type is unsupported.'); + }Note: Update any tests asserting on the old message.
🧹 Nitpick comments (5)
packages/w3c-context/package.json (1)
37-38: Manual verification needed for jsonld-signatures v11 compatibilityWe’ve upgraded to
"jsonld-signatures": "^11.5.0"in packages/w3c-context/package.json (lines 37–38), but the DI APIs (e.g.,extendContextLoader) saw changes between 10.x and 11.x. The sandbox CI didn’t produce loader-related errors, so please verify locally on Node 18:
- Run
npm -w packages/w3c-context testand confirmextendContextLoaderbehaves as before.- Run
npm -w packages/w3c-vc testand validate sign/verify/derive flows.If you encounter regressions, pin the version to
~11.5.0and revisit compatibility later.packages/w3c-vc/README.md (1)
116-119: Nit: Mention that /issuer and the version-specific date field are auto-mandatoryYour implementation auto-includes /issuer and either /issuanceDate (v1.1) or /validFrom (v2) as mandatory pointers. Consider adding a short note so users don’t redundantly include them.
packages/w3c-vc/src/lib/w3c-vc.test.ts (3)
396-612: Good coverage for dual-version ECDSA-SD-2023; add a future-date negative test and adjust messages if helper changes
- Consider adding a test for v2 where validFrom is in the future to assert the before-date error is thrown.
- If you accept the helper change to a generic unsupported proof error, update the BBS+ unsupported-suite test expectation accordingly.
Here’s a test you can append (outside this selected range):
it('should fail verification when validFrom is in the future (v2.0)', async () => { const future = new Date(Date.now() + 60_000).toISOString(); const testCredential = { ...ecdsaCredentialV2_0, validFrom: future }; const signed = await signCredential(testCredential, ecdsaKeyPair, 'ecdsa-sd-2023'); expect(signed.error).toBeUndefined(); const res = await verifyCredential(signed.signed as VerifiableCredential); expect(res.verified).toBe(false); expect(res.error).toMatch(/before the "validFrom"/); });And, if you adopt the generic error string in helper:
- expect(verificationResult.error).equals('"proof" type is not of BbsBlsSignature2020.'); + expect(verificationResult.error).equals('"proof" type is unsupported.');
396-612: Potential secret leakage flagged by Gitleaks – confirm keys are test-only and consider allowlistingStatic analysis flagged a generic API key. The ECDSA test key at Line 106–107 appears to be a test-only secret. Please confirm it is non-prod. To silence false positives safely, add a gitleaks allowlist for this test path.
Add a .gitleaks.toml (or update it) like:
title = "gitleaks config" [extend] # keep defaults [allowlist] paths = [ '''packages/w3c-vc/src/lib/w3c-vc.test.ts''', ] # or allow specific test-only patterns: regexes = [ '''z42tmUXTVn3n9BihE6NhdMpvVBTnFTgmb6fw18o5Ud6puhRW''', ]If this key is not test-only, rotate immediately and purge history.
I can open a PR adding the allowlist or swap in ephemeral test keys loaded from env if preferred.
109-170: Type safety: replace pervasiveanyin test fixtures with concrete typesUsing
anyfor credential fixtures and reveal documents loses compile-time checks, especially after the v2 schema split. PreferRawVerifiableCredential/ContextDocument/key-pair types from your lib to catch shape regressions earlier.Example:
-const modifiedCredential: any = { +import type { RawVerifiableCredential, ContextDocument } from './types'; +const modifiedCredential: RawVerifiableCredential = { ... -const revealDocument: any = { +const revealDocument: ContextDocument = {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
packages/w3c-context/package.json(1 hunks)packages/w3c-context/src/lib/index.ts(2 hunks)packages/w3c-vc/README.md(3 hunks)packages/w3c-vc/src/lib/helper/index.ts(2 hunks)packages/w3c-vc/src/lib/w3c-vc.test.ts(3 hunks)packages/w3c-vc/src/lib/w3c-vc.ts(5 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/w3c-vc/src/lib/w3c-vc.ts (1)
packages/w3c-context/src/lib/index.ts (1)
CredentialContextVersion(83-86)
packages/w3c-vc/src/lib/helper/index.ts (1)
packages/w3c-context/src/lib/index.ts (1)
CredentialContextVersion(83-86)
packages/w3c-vc/src/lib/w3c-vc.test.ts (2)
packages/w3c-vc/src/lib/types.ts (1)
VerifiableCredential(40-40)packages/w3c-vc/src/lib/w3c-vc.ts (3)
signCredential(79-176)deriveCredential(290-395)verifyCredential(184-281)
🪛 Gitleaks (8.27.2)
packages/w3c-vc/src/lib/w3c-vc.test.ts
193-193: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (8)
packages/w3c-vc/README.md (3)
101-112: LGTM: Switch to VC v2 context and v2 temporal fields in ECDSA-SD-2023 exampleUsing https://www.w3.org/ns/credentials/v2 and validFrom/validUntil aligns with VC DM v2. The example looks correct.
196-207: LGTM: Verification example reflects VC v2 fieldsThe context and validFrom/validUntil fields align with the VC v2 example. No issues spotted.
321-332: LGTM: Derivation example is consistent with VC v2The v2 context and date fields are consistent. The selective disclosure pointer pathing is correct.
packages/w3c-context/src/lib/index.ts (1)
3-3: Import change to jsonld-signatures default is fine, but double-check ESM/CJS interopjsonld-signatures typically provides a default export; with tsup + Node 18 this should be fine. Ensure tsconfig has esModuleInterop or the bundler config aligns; otherwise switch to
import * as jsonldSignatures.packages/w3c-vc/src/lib/w3c-vc.ts (4)
11-16: LGTM! Import changes align with W3C VC v2 support.The addition of
CredentialContextVersionimport enables proper version detection for handling both v1.1 and v2 credentials.
114-137: Well-implemented version-aware mandatory pointers logic.The implementation correctly:
- Detects the credential version from the context
- Applies appropriate date field requirements (v2:
validFromwhen present, v1.1:issuanceDate)- Ensures issuer is always mandatory
- Properly merges user-provided mandatory pointers while avoiding duplicates
352-363: Smart handling of credentialSubject in selective disclosure.The logic correctly ensures that when no specific credentialSubject properties are selected, the entire credentialSubject is included to maintain credential validity per W3C VC specification. This prevents accidentally creating invalid credentials during selective disclosure.
124-126: validFrom Date Format Validation Already PresentThe existing
_checkCredentiallogic inpackages/w3c-vc/src/lib/helper/index.tsalready callsassertDateString({ credential, prop: 'validFrom' })for v2 credentials and enforces a single value viajsonld.getValues. This covers both format correctness (per the XML Schema dateTime regex) and cardinality, so no additional validation is required.Likely an incorrect or invalid review comment.
| return jsonldSignatures.extendContextLoader(customDocLoader); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Harden the loader: consider an allowlist and scheme checks before remote fetch
While extendContextLoader wraps the custom loader, you’re still allowing arbitrary remote fetches when a context/URL isn’t in cache. To mitigate SSRF/LDoS risks:
- Restrict to https URLs and a known allowlist (w3id.org, w3.org, your own domains).
- Optionally cap payload size and add a timeout.
Here’s a sketch (apply outside the selected range) to harden the loader in customDocLoader:
// Add near the top
const ALLOWED_HOSTS = new Set([
'www.w3.org',
'w3id.org',
'trustvc.io',
'trustvc.github.io',
]);
// Inside customDocLoader before fetch():
const parsed = new URL(url);
if (parsed.protocol !== 'https:') {
throw new Error(`Unsupported scheme for document loading: ${parsed.protocol}`);
}
if (!ALLOWED_HOSTS.has(parsed.hostname)) {
throw new Error(`Refusing to load remote document from unapproved host: ${parsed.hostname}`);
}
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), 5000);
const results = await fetch(url, { redirect: 'follow', signal: controller.signal });
clearTimeout(id);# [1.3.0-alpha.3](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-issuer@1.3.0-alpha.2...@trustvc/w3c-issuer@1.3.0-alpha.3) (2025-08-13) ### Features * add support for w3c vc data model v2 ([#69](#69)) ([e4219ee](e4219ee))
|
🎉 This PR is included in version 1.3.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [1.3.0-alpha.3](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-context@1.3.0-alpha.2...@trustvc/w3c-context@1.3.0-alpha.3) (2025-08-13) ### Features * add support for w3c vc data model v2 ([#69](#69)) ([e4219ee](e4219ee))
|
🎉 This PR is included in version 1.3.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [1.3.0-alpha.3](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-credential-status@1.3.0-alpha.2...@trustvc/w3c-credential-status@1.3.0-alpha.3) (2025-08-13) ### Features * add support for w3c vc data model v2 ([#69](#69)) ([e4219ee](e4219ee))
|
🎉 This PR is included in version 1.3.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [1.3.0-alpha.3](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-vc@1.3.0-alpha.2...@trustvc/w3c-vc@1.3.0-alpha.3) (2025-08-13) ### Features * add support for w3c vc data model v2 ([#69](#69)) ([e4219ee](e4219ee))
|
🎉 This PR is included in version 1.3.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [1.3.0-alpha.3](https://github.com/TrustVC/w3c/compare/@trustvc/w3c-cli@1.3.0-alpha.2...@trustvc/w3c-cli@1.3.0-alpha.3) (2025-08-13) ### Features * add support for w3c vc data model v2 ([#69](#69)) ([e4219ee](e4219ee))
|
🎉 This PR is included in version 1.3.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [1.3.0-alpha.3](https://github.com/TrustVC/w3c/compare/@trustvc/w3c@1.3.0-alpha.2...@trustvc/w3c@1.3.0-alpha.3) (2025-08-13) ### Features * add support for w3c vc data model v2 ([#69](#69)) ([e4219ee](e4219ee))
|
🎉 This PR is included in version 1.3.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary by CodeRabbit
New Features
Documentation
Chores