-
Notifications
You must be signed in to change notification settings - Fork 188
Add XmlDSigVerifier wrapper for SignedXml #519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
WalkthroughThis PR modernizes the XML Digital Signature library by introducing a URI-driven type system, centralizing XMLDSig constants, adding a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant XmlDSigVerifier
participant SignedXmlFactory
participant SignedXml
participant KeySelector
participant Crypto
Client->>XmlDSigVerifier: verifySignature(xml, options)
XmlDSigVerifier->>XmlDSigVerifier: resolveOptions(options)
XmlDSigVerifier->>KeySelector: determine key selection strategy
alt keyInfo strategy
KeySelector->>KeySelector: validate truststore & certificate expiration
else publicCert strategy
KeySelector->>KeySelector: use provided certificate
else sharedSecret strategy
KeySelector->>KeySelector: use shared secret key
end
XmlDSigVerifier->>SignedXmlFactory: createSignedXml(resolved options)
SignedXmlFactory->>SignedXml: new SignedXml(config with key)
XmlDSigVerifier->>SignedXml: loadSignature(xml)
SignedXml->>Crypto: verify signature
alt Signature valid
Crypto-->>SignedXml: verification success
SignedXml-->>XmlDSigVerifier: result with signedReferences
XmlDSigVerifier-->>Client: SuccessfulXmlDsigVerificationResult
else Signature invalid or error
Crypto-->>SignedXml: verification failed
SignedXml-->>XmlDSigVerifier: error
alt throwOnError enabled
XmlDSigVerifier->>Client: throw error
else throwOnError disabled
XmlDSigVerifier-->>Client: FailedXmlDsigVerificationResult
end
end
sequenceDiagram
participant Test
participant SignedXml
participant AlgorithmRegistry
participant AlgorithmImpl
participant XMLDSIG_URIS
Test->>SignedXml: new SignedXml({ algorithms, options })
SignedXml->>AlgorithmRegistry: register CanonicalizationAlgorithms, HashAlgorithms, etc.
Test->>SignedXml: loadSignature(xml)
SignedXml->>XMLDSIG_URIS: lookup algorithm URI
XMLDSIG_URIS-->>SignedXml: URI constant
SignedXml->>AlgorithmRegistry: lookup(algorithmURI)
AlgorithmRegistry-->>SignedXml: algorithm class
SignedXml->>AlgorithmImpl: instantiate & execute
AlgorithmImpl->>AlgorithmImpl: process using TransformAlgorithmOptions
AlgorithmImpl->>XMLDSIG_URIS: getAlgorithmName() returns URI
AlgorithmImpl-->>SignedXml: processed result
SignedXml-->>Test: signature loaded & verified
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #519 +/- ##
==========================================
+ Coverage 73.05% 77.61% +4.55%
==========================================
Files 9 11 +2
Lines 902 1157 +255
Branches 239 318 +79
==========================================
+ Hits 659 898 +239
- Misses 143 152 +9
- Partials 100 107 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (15)
src/types.ts (1)
13-19: Preserve IntelliSense by deriving algorithm types from constants (while still allowing custom strings).Current
= stringerodes DX and safety. Recommend unions fromalgorithms.tsvalues plus a widened fallback.@@ -import * as crypto from "crypto"; +import * as crypto from "crypto"; +import { + CANONICALIZATION_ALGORITHMS, + TRANSFORM_ALGORITHMS, + HASH_ALGORITHMS, + SIGNATURE_ALGORITHMS, +} from "./algorithms"; @@ -export type CanonicalizationAlgorithmType = string; +type ValuesOf<T> = T[keyof T]; +export type CanonicalizationAlgorithmType = + | ValuesOf<typeof CANONICALIZATION_ALGORITHMS> + | (string & {}); @@ -export type CanonicalizationOrTransformAlgorithmType = string; +export type CanonicalizationOrTransformAlgorithmType = + | ValuesOf<typeof TRANSFORM_ALGORITHMS> + | (string & {}); @@ -export type HashAlgorithmType = string; +export type HashAlgorithmType = + | ValuesOf<typeof HASH_ALGORITHMS> + | (string & {}); @@ -export type SignatureAlgorithmType = string; +export type SignatureAlgorithmType = + | ValuesOf<typeof SIGNATURE_ALGORITHMS> + | (string & {});Based on learnings.
src/algorithms.ts (1)
1-37: Freeze constants at runtime and annotate legacy algorithms.
as constis compile-time only. Freeze to guard against accidental mutation, and mark SHA‑1 variants as legacy in JSDoc.-export const CANONICALIZATION_ALGORITHMS = { +export const CANONICALIZATION_ALGORITHMS = Object.freeze({ C14N: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", C14N_WITH_COMMENTS: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", EXCLUSIVE_C14N: "http://www.w3.org/2001/10/xml-exc-c14n#", EXCLUSIVE_C14N_WITH_COMMENTS: "http://www.w3.org/2001/10/xml-exc-c14n#WithComments", -} as const; +} as const); @@ -export const TRANSFORM_ALGORITHMS = { +export const TRANSFORM_ALGORITHMS = Object.freeze({ ...CANONICALIZATION_ALGORITHMS, ENVELOPED_SIGNATURE: "http://www.w3.org/2000/09/xmldsig#enveloped-signature", -} as const; +} as const); @@ -export const HASH_ALGORITHMS = { +export const HASH_ALGORITHMS = Object.freeze({ + /** @deprecated SHA-1 is considered weak; prefer SHA256/512. */ SHA1: "http://www.w3.org/2000/09/xmldsig#sha1", SHA256: "http://www.w3.org/2001/04/xmlenc#sha256", SHA512: "http://www.w3.org/2001/04/xmlenc#sha512", -} as const; +} as const); @@ -export const SIGNATURE_ALGORITHMS = { +export const SIGNATURE_ALGORITHMS = Object.freeze({ + /** @deprecated RSA-SHA1 is considered weak; prefer RSA-SHA256/512. */ RSA_SHA1: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", RSA_SHA256: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", RSA_SHA256_MGF1: "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1", RSA_SHA512: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", HMAC_SHA1: "http://www.w3.org/2000/09/xmldsig#hmac-sha1", -} as const; +} as const);test/xml-validator.spec.ts (7)
17-27: Use exported algorithm constants instead of hard-coded URIs.Reduces string typos and couples tests to public API.
-import { XmlValidator, XmlValidatorFactory } from "../src/xml-validator"; +import { XmlValidator, XmlValidatorFactory } from "../src/xml-validator"; +import { + CANONICALIZATION_ALGORITHMS, + HASH_ALGORITHMS, + SIGNATURE_ALGORITHMS, +} from "../src/algorithms"; @@ - const sig = new SignedXml({ + const sig = new SignedXml({ privateKey, - canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", - signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + canonicalizationAlgorithm: CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14N, + signatureAlgorithm: SIGNATURE_ALGORITHMS.RSA_SHA1, }); @@ - digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", - transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], + digestAlgorithm: HASH_ALGORITHMS.SHA1, + transforms: [CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14N], });Apply similarly in other helper/test blocks.
Also applies to: 41-47, 56-63
331-348: Confirm non-standard error-first callback behavior is intentional.Async tests expect
err === nulleven on validation errors. If this is the desired API, document it; otherwise, return errors viaerr.Would you like a PR to switch to Node-style
(err)semantics and update tests accordingly?
550-561: Strengthen maxTransforms coverage with a failing case.Current test only asserts success when within limit; it doesn’t prove enforcement.
Add a case where a signed reference uses 2+ transforms and validator is created with
maxTransforms: 1; expect invalid result or thrown error (per API contract).
114-123: Type-level constraint isn’t verifiable at runtime; consider tsd tests.This test’s comment references a TS compile-time error. Add a tsd test or a build-time check instead.
I can add a small
test-d.tsusingtsdto assertenableWSSecurityModeforbidsidAttributes.
273-301: Add at least one modern-algorithm path (SHA-256 / RSA-SHA256).Most tests use SHA‑1 for simplicity. Include one RSA‑SHA256 + SHA256 case to cover contemporary defaults.
- const sig = new SignedXml({ + const sig = new SignedXml({ privateKey, - canonicalizationAlgorithm: CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14N, - signatureAlgorithm: SIGNATURE_ALGORITHMS.RSA_SHA1, + canonicalizationAlgorithm: CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14N, + signatureAlgorithm: SIGNATURE_ALGORITHMS.RSA_SHA256, }); @@ - digestAlgorithm: HASH_ALGORITHMS.SHA1, + digestAlgorithm: HASH_ALGORITHMS.SHA256,Also applies to: 591-626
456-467: Factory override test is solid; consider one negative path.Add a case where factory returns wrong cert and no override is provided to assert failure path.
1-64: Offer: add helper to DRY signature creation across tests.
createSignedXmlandcreateMultiSignedXmlduplicate algorithm strings and options. Extract a parametric helper that accepts algorithms and xpath to reduce repetition.Happy to push a refactor commit.
src/index.ts (1)
7-14: All exports verified; consider adding JSDoc annotations for clarity.All named exports (XmlSigner, XmlSignerFactory, XmlValidator, XmlValidatorFactory, and algorithm constants) exist and are correctly re-exported. To improve developer experience, add JSDoc comments with
@experimentalor migration path notes on these exports in src/index.ts lines 7-14.test/xml-signer.spec.ts (2)
783-787: Use exported algorithm constants instead of raw URIs to avoid typos and ease future changes.Example:
- signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + import { SIGNATURE_ALGORITHMS, CANONICALIZATION_ALGORITHMS, HASH_ALGORITHMS } from "../src/algorithms"; + signatureAlgorithm: SIGNATURE_ALGORITHMS.RSA_SHA1,Apply similarly for canonicalization and digest algorithms across tests.
Also applies to: 815-820, 847-852
374-394: Add an edge-case test where the target ID contains quotes to ensure URI→XPath conversion handles XPath literal quoting correctly.Example:
it("should sign when URI ID contains quotes", function () { const factory = new XmlSignerFactory({ signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", }); const signer = factory.createSigner(privateKey); signer.addReference({ uri: "#it\"s'id", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", }); const xml = '<root><test id="it"s'id">content</test></root>'; expect(() => signer.sign(xml)).to.not.throw(); });src/xml-signer.ts (2)
296-323: Block re-entrancy and mid-flight mutation during async signing.Set a signingInProgress flag (or set hasBeenSigned upfront) to prevent a second sign() or addReference() while computeSignature is running:
- sign(xml: string, callback?: ErrorFirstCallback<string>): string | void { - if (this.hasBeenSigned) { + private signingInProgress = false; + sign(xml: string, callback?: ErrorFirstCallback<string>): string | void { + if (this.hasBeenSigned || this.signingInProgress) { throw new Error( "This XmlSigner instance has already been used to sign a document. Create a new instance to sign another document.", ); } - - if (callback) { + if (callback) { + this.signingInProgress = true; try { this.signedXml.computeSignature(xml, this.computeOptions, (err, signedXmlInstance) => { if (err) { - callback(err); + this.signingInProgress = false; + callback(err); } else if (signedXmlInstance) { this.hasBeenSigned = true; + this.signingInProgress = false; callback(null, signedXmlInstance.getSignedXml()); } else { + this.signingInProgress = false; callback(new Error("Signing failed: no signed XML instance returned")); } }); } catch (error) { + this.signingInProgress = false; callback(error instanceof Error ? error : new Error("Unknown signing error")); } } else { + this.signingInProgress = true; this.signedXml.computeSignature(xml, this.computeOptions); this.hasBeenSigned = true; + this.signingInProgress = false; return this.signedXml.getSignedXml(); } }
222-226: Optional: for enveloped signatures, consider explicitly selecting the document element regardless of prefix to be resilient to processing instructions or comments before the root.E.g., use the existing "/*" but document the intent in a comment, or use a SignedXml-native empty-URI path exclusively without an XPath if supported.
src/xml-validator.ts (1)
154-159: Nit: trim the trailing space in the single-use error message.- "This XmlValidator instance has already been used. Create a new instance to validate another document. ", + "This XmlValidator instance has already been used. Create a new instance to validate another document.",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
.gitignore(1 hunks).nvmrc(1 hunks)example/new-api-example.js(1 hunks)src/algorithms.ts(1 hunks)src/index.ts(1 hunks)src/types.ts(1 hunks)src/xml-signer.ts(1 hunks)src/xml-validator.ts(1 hunks)test/xml-signer.spec.ts(1 hunks)test/xml-validator.spec.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.309Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.734Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.441Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.441Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/xml-signer.ts
🧬 Code graph analysis (6)
src/algorithms.ts (1)
src/index.ts (4)
CANONICALIZATION_ALGORITHMS(10-10)TRANSFORM_ALGORITHMS(11-11)HASH_ALGORITHMS(12-12)SIGNATURE_ALGORITHMS(13-13)
test/xml-validator.spec.ts (1)
src/xml-validator.ts (2)
XmlValidator(75-245)XmlValidatorFactory(251-293)
test/xml-signer.spec.ts (1)
src/xml-signer.ts (5)
XmlSignerFactory(343-436)XmlSignerFactoryOptions(147-190)XmlSigner(255-337)SigningReference(142-142)ReferenceAttributes(39-46)
example/new-api-example.js (3)
src/xml-signer.ts (1)
XmlSignerFactory(343-436)src/algorithms.ts (4)
SIGNATURE_ALGORITHMS(31-37)CANONICALIZATION_ALGORITHMS(4-9)TRANSFORM_ALGORITHMS(14-17)HASH_ALGORITHMS(22-26)src/xml-validator.ts (1)
XmlValidatorFactory(251-293)
src/xml-signer.ts (2)
src/types.ts (6)
ObjectAttributes(34-43)ComputeSignatureOptionsLocation(81-84)SignatureAlgorithmType(19-19)CanonicalizationAlgorithmType(13-13)ComputeSignatureOptions(98-103)ErrorFirstCallback(11-11)src/signed-xml.ts (1)
getKeyInfoContent(197-226)
src/xml-validator.ts (1)
src/types.ts (1)
ErrorFirstCallback(11-11)
🪛 Biome (2.1.2)
test/xml-signer.spec.ts
[error] 888-888: await is only allowed within async functions and at the top levels of modules.
(parse)
🔇 Additional comments (4)
.gitignore (1)
15-15: LGTM — safe tooling ignore.test/xml-validator.spec.ts (2)
564-570: Validator single-use: avoid consuming the instance for multiple edge sub-tests.Calling
validate("")marks the instance used; keep that behavior but ensure subsequent unrelated tests don’t accidentally reuse the same instance.If isolated per test (as here), ignore.
1-4: All verification checks passed—test/API alignment confirmed.The imports in lines 1-4 are appropriate and correctly aligned:
maxTransformsis actively used in the implementation (lines 12, 77, 116 ofsrc/xml-validator.ts)- SHA-1 algorithms are comprehensively covered in tests across multiple test files
- Tests properly import from the public API (
../src/index).nvmrc (1)
1-1: Align .nvmrc with CI test matrix or add v22 to CI.Your .nvmrc pins to v22.16.0, but CI tests only [16, 18, 20] + latest + lts/*. Local development uses v22 while CI doesn't test it, creating a mismatch risk. Either:
- Change .nvmrc to a tested version (e.g., v20 or use lts/*)
- Add v22 to the CI matrix if v22 is intentional
Verify this alignment is deliberate and update accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
example/new-api-example.js (1)
18-77: Certificate extraction could use library utility.The manual extraction of certificate content (lines 33-36) works but could use the library's
pemToDerutility for robustness. However, the current approach is acceptable for an example demonstrating the API.Consider this alternative:
const { pemToDer } = require("xml-crypto"); // ... const certContent = pemToDer(cert).toString("base64");This would make the example more robust against PEM format variations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
example/new-api-example.js(1 hunks)src/utils.ts(1 hunks)src/xml-signer.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.309Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.734Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.441Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.441Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/xml-signer.tsexample/new-api-example.js
📚 Learning: 2025-10-25T14:41:13.608Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.608Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
example/new-api-example.js
📚 Learning: 2025-10-22T20:36:00.734Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.734Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
example/new-api-example.js
📚 Learning: 2025-10-22T21:03:38.309Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.309Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
example/new-api-example.js
🧬 Code graph analysis (2)
src/xml-signer.ts (3)
src/types.ts (6)
ObjectAttributes(34-43)ComputeSignatureOptionsLocation(81-84)SignatureAlgorithmType(19-19)CanonicalizationAlgorithmType(13-13)ComputeSignatureOptions(98-103)ErrorFirstCallback(11-11)src/utils.ts (1)
buildIdXPath(368-376)src/signed-xml.ts (1)
getKeyInfoContent(197-226)
example/new-api-example.js (3)
src/xml-signer.ts (1)
XmlSignerFactory(376-476)src/algorithms.ts (4)
SIGNATURE_ALGORITHMS(31-37)CANONICALIZATION_ALGORITHMS(4-9)TRANSFORM_ALGORITHMS(14-17)HASH_ALGORITHMS(22-26)src/xml-validator.ts (1)
XmlValidatorFactory(251-293)
🔇 Additional comments (15)
src/utils.ts (2)
335-366: LGTM! XPath literal escaping is correctly implemented.The function correctly handles XPath 1.0's lack of escape mechanism for quotes by switching quote delimiters or using
concat()when both types are present.
368-376: LGTM! ID-based XPath construction is correct and safe.The function properly validates inputs, uses safe literal construction, and generates XPath expressions that handle namespaced ID attributes via
local-name()comparisons.example/new-api-example.js (7)
1-9: LGTM! Correct package import.The example correctly uses
require("xml-crypto")to demonstrate end-user consumption of the library.Based on learnings.
79-109: LGTM! Default key pattern correctly demonstrated.The example clearly shows how to use a factory-configured default private key.
111-138: LGTM! Enveloped signature pattern is correct.The example properly demonstrates enveloped signatures with the correct transform order.
140-171: LGTM! Basic validation pattern is clear.The example demonstrates proper validation with error handling.
173-201: LGTM! Certificate override pattern is correctly demonstrated.The example shows how to override the factory's certificate configuration, with appropriate comments acknowledging the simplified KeyInfo extraction.
203-226: LGTM! Async validation pattern is correct.The example properly demonstrates callback-based validation with error-first callback conventions.
228-294: LGTM! Multiple signatures example is instructive.The example effectively demonstrates both the failure case (validating without loading a specific signature) and the success case (using
loadSignatureto select a specific signature).src/xml-signer.ts (6)
1-12: LGTM! Imports and constants are appropriate.The imports are well-structured and the default ID attributes cover common XML ID attribute casings.
14-225: LGTM! Type definitions are well-designed and type-safe.The use of discriminated unions and the
nevertype to enforce mutual exclusivity betweenenableWSSecurityModeandidAttributesis an excellent type-safety pattern.
232-282: LGTM! Reference handling is correct and safe.The function properly converts signing references to SignedXml format, with special handling for:
- Enveloped signatures (empty URI →
"/*")- Fragment IDs (using
buildIdXPathfor safe escaping)- External URIs (correctly rejected for signing)
The use of
buildIdXPathensures safe XPath literal construction for IDs with quotes, resolving the escaping concerns from past reviews.
288-370: LGTM! XmlSigner class enforces safe single-use pattern.The class correctly prevents common misuse scenarios:
- Adding references after signing
- Reusing a signer instance
- Accessing signature XML before signing
Error handling for async operations is properly implemented with try-catch wrapping.
376-394: LGTM! Factory constructor properly validates required options.The constructor enforces required configuration and safely copies options to prevent external mutations.
396-476: LGTM! Factory signer creation is well-structured.The method correctly:
- Resolves private keys with proper fallback logic
- Filters undefined KeyInfo attributes to maintain clean output
- Configures ID handling for both standard and WS-Security modes
- Applies factory-level references to each signer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (1)
example/new-api-example.js (1)
1-3: Use the package name in examplesExamples should demonstrate end‑user usage via the published package.
-const { XmlSignerFactory, XmlDSigValidator, Algorithms, pemToDer } = require("../lib/index"); +const { XmlSignerFactory, XmlDSigValidator, Algorithms, pemToDer } = require("xml-crypto");Based on learnings.
🧹 Nitpick comments (12)
src/algorithms.ts (1)
1-44: Centralized allow-lists: great; tighten types and immutabilityConsider exporting derived types from these const maps and (optionally) freezing at runtime to prevent accidental mutation.
+// Optionally export strongly-typed unions for downstream usage +export type CanonicalizationAlgorithm = + typeof CANONICALIZATION_ALGORITHMS[keyof typeof CANONICALIZATION_ALGORITHMS]; +export type TransformAlgorithm = + typeof TRANSFORM_ALGORITHMS[keyof typeof TRANSFORM_ALGORITHMS]; +export type HashAlgorithm = + typeof HASH_ALGORITHMS[keyof typeof HASH_ALGORITHMS]; +export type SignatureAlgorithm = + typeof SIGNATURE_ALGORITHMS[keyof typeof SIGNATURE_ALGORITHMS]; + -export const Algorithms = { +export const Algorithms = { canonicalization: CANONICALIZATION_ALGORITHMS, transform: TRANSFORM_ALGORITHMS, hash: HASH_ALGORITHMS, signature: SIGNATURE_ALGORITHMS, -}; +} as const; // If you want runtime immutability too: // export const Algorithms = Object.freeze({ // canonicalization: Object.freeze(CANONICALIZATION_ALGORITHMS), // transform: Object.freeze(TRANSFORM_ALGORITHMS), // hash: Object.freeze(HASH_ALGORITHMS), // signature: Object.freeze(SIGNATURE_ALGORITHMS), // });test/xml-validator.spec.ts (1)
169-182: Duplicate reusability checksBoth blocks assert validator reusability. Keep one to reduce test time/noise.
Also applies to: 264-277
src/signed-xml.ts (2)
556-601: Duplicate ID-search logic; unify and fix messageThe ID lookup is duplicated for QName and non-QName paths and repeats an error string with “ID / Id / Id”. Consider extracting a helper to build the XPath and to check duplicates; also fix the message casing.
- throw new Error( - "Cannot validate a document which contains multiple elements with the " + - "same value for the ID / Id / Id attributes, in order to prevent " + - "signature wrapping attack.", - ); + throw new Error( + "Cannot validate a document which contains multiple elements with the " + + "same value for the ID / Id / id attributes, in order to prevent " + + "signature wrapping attack.", + );Optional extraction sketch:
function xpathForIdAttr(local: string, ns?: string, val?: string) { const pred = ns ? `@*[local-name(.)='${local}' and namespace-uri(.)='${ns}']='${val}'` : `@*[local-name(.)='${local}']='${val}'`; return `//*[@*[${ pred }]]`; // or keep current structure for exact parity }
1404-1421: Avoid redundant xmlns prefix injection when setting QName IdBefore adding
xmlns:${prefix}, check if it already exists on the element to reduce noise.- node.setAttributeNS("http://www.w3.org/2000/xmlns/", `xmlns:${prefix}`, namespace); + if (!node.hasAttributeNS("http://www.w3.org/2000/xmlns/", prefix)) { + node.setAttributeNS("http://www.w3.org/2000/xmlns/", `xmlns:${prefix}`, namespace); + } node.setAttributeNS(namespace, qName, id);src/types.ts (2)
13-20: Widening tostringreduces safety; derive unions from Algorithms while allowing extensionPreserve DX with flexible strings but recover strong types via branded unions derived from
Algorithms.-export type CanonicalizationAlgorithmType = string; -export type CanonicalizationOrTransformAlgorithmType = string; -export type HashAlgorithmType = string; -export type SignatureAlgorithmType = string; +// Derive exact URI unions from the central registry and still allow custom strings. +type _Algos = typeof import("./algorithms").Algorithms; +export type CanonicalizationAlgorithmType = + | _Algos["canonicalization"][keyof _Algos["canonicalization"]] + | (string & {}); +export type CanonicalizationOrTransformAlgorithmType = + | _Algos["transform"][keyof _Algos["transform"]] + | (string & {}); +export type HashAlgorithmType = + | _Algos["hash"][keyof _Algos["hash"]] + | (string & {}); +export type SignatureAlgorithmType = + | _Algos["signature"][keyof _Algos["signature"]] + | (string & {});
51-53: Align options with validator usage: addmaxTransforms?
XmlDSigValidatorpassesmaxTransformsintoSignedXmloptions; define it here to avoid excess-property issues and to support future enforcement at the core.export interface SignedXmlOptions { idMode?: "wssecurity"; idAttribute?: string; idAttributeQNames?: string[]; namespaceMap?: Record<string, string>; privateKey?: crypto.KeyLike; publicCert?: crypto.KeyLike; signatureAlgorithm?: SignatureAlgorithmType; canonicalizationAlgorithm?: CanonicalizationAlgorithmType; inclusiveNamespacesPrefixList?: string | string[]; implicitTransforms?: ReadonlyArray<CanonicalizationOrTransformAlgorithmType>; + /** Maximum allowed number of transforms per Reference (defense-in-depth). */ + maxTransforms?: number; keyInfoAttributes?: Record<string, string>; getKeyInfoContent?(args?: GetKeyInfoContentArgs): string | null; getCertFromKeyInfo?(keyInfo?: Node | null): string | null; objects?: Array<{ content: string; attributes?: ObjectAttributes }>; }src/xmldsig-validator.ts (3)
97-106: Defaulting idAttributeQNames may be misleadingYou default idAttributeQNames to DEFAULT_ID_ATTRIBUTES, then also pass it into SignedXml and later assign idAttributes. Prefer leaving idAttributeQNames undefined unless the caller opts in (so SignedXml’s own defaults apply), or ensure you don’t assign idAttributes when QNames contain prefixes.
- idAttributeQNames: - Array.isArray(options.idAttributeQNames) && options.idAttributeQNames.length > 0 - ? options.idAttributeQNames - : DEFAULT_ID_ATTRIBUTES, + idAttributeQNames: + Array.isArray(options.idAttributeQNames) && options.idAttributeQNames.length > 0 + ? options.idAttributeQNames + : undefined,This reduces double‑configuration and avoids accidental misuse when QNames (e.g., "wsu:Id") are provided. Based on learnings.
46-47: Tighten algorithm typing for implicitTransformsIf available in your types, prefer CanonicalizationOrTransformAlgorithmType[] to catch invalid values at compile time.
- implicitTransforms?: ReadonlyArray<string>; + implicitTransforms?: ReadonlyArray<CanonicalizationOrTransformAlgorithmType>;You may need:
import type { CanonicalizationOrTransformAlgorithmType } from "./types";
146-151: Consider bounding returned signedReferences for large inputsReturning full canonicalized references can be large. Expose an option to cap size or return counts only, keeping a debug path when needed.
example/new-api-example.js (3)
176-199: Reference Type value looks incorrectType is meant to describe the data object being referenced, not a transform. Consider using an appropriate Type URI or omit it.
22-27: Guard pem parsing errors in example helperWrap pemToDer in try/catch to print a concise message for learners.
function extractCertificateContent(certPath) { - const cert = fs.readFileSync(certPath, "utf8"); - // Use the library's pemToDer utility for robust PEM parsing - return pemToDer(cert).toString("base64"); + try { + const cert = fs.readFileSync(certPath, "utf8"); + return pemToDer(cert).toString("base64"); + } catch (e) { + console.error("Failed to read/parse certificate:", e && e.message ? e.message : e); + throw e; + } }
560-606: Examples rely on repo test assetsThese paths won’t exist for consumers. Consider adding a short comment at the top instructing users to replace file reads with their own keys/certs.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
example/new-api-example.js(1 hunks)src/algorithms.ts(1 hunks)src/index.ts(1 hunks)src/signed-xml.ts(8 hunks)src/types.ts(2 hunks)src/utils.ts(2 hunks)src/xmldsig-validator.ts(1 hunks)test/xml-validator.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/utils.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.309Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.734Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.441Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.441Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/signed-xml.tsexample/new-api-example.js
📚 Learning: 2025-10-25T14:41:13.608Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.608Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
example/new-api-example.js
📚 Learning: 2025-10-22T20:36:00.734Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.734Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
example/new-api-example.js
📚 Learning: 2025-10-22T21:03:38.309Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.309Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
example/new-api-example.js
🧬 Code graph analysis (4)
src/xmldsig-validator.ts (1)
src/signed-xml.ts (1)
SignedXml(30-1523)
test/xml-validator.spec.ts (3)
src/algorithms.ts (1)
Algorithms(39-44)src/xmldsig-validator.ts (2)
createSignedXml(157-189)XmlDSigValidator(89-206)src/signed-xml.ts (1)
SignedXml(30-1523)
example/new-api-example.js (4)
src/utils.ts (1)
pemToDer(163-175)src/xml-signer.ts (1)
XmlSignerFactory(376-476)src/algorithms.ts (1)
Algorithms(39-44)src/xmldsig-validator.ts (1)
XmlDSigValidator(89-206)
src/algorithms.ts (1)
src/index.ts (1)
Algorithms(9-9)
🔇 Additional comments (2)
src/index.ts (1)
7-9: Public API surface looks goodRe-exports read clean and preserve backward compatibility with
SignedXml. No issues from me here.src/signed-xml.ts (1)
1187-1219: Keeps ref.uri mutation during signing (intended compatibility)
ref.uri = idis preserved while computing references. This aligns with prior guidance to defer behavioral changes to a separate PR. Looks good.Based on learnings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (5)
test/xmldsig-validator.spec.ts (2)
279-290: “maxTransforms” test doesn’t exercise the limitThis signs with one transform and validates with
maxTransforms: 1, so it can’t fail. Add ≥2 transforms and expect invalid once validator-side enforcement lands. See suggested enforcement in the validator file comment.- const signedXml = createSignedXml(xml); + // Create a signature with two transforms to exceed the limit + const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: canonicalization.EXCLUSIVE_C14N, signatureAlgorithm: signature.RSA_SHA1 }); + sig.addReference({ + xpath: "//*[local-name(.)='test']", + digestAlgorithm: hash.SHA1, + transforms: [transform.ENVELOPED_SIGNATURE, transform.EXCLUSIVE_C14N], + }); + sig.computeSignature(xml); + const signedXml = sig.getSignedXml(); @@ - expect(result.valid).to.be.true; + expect(result.valid).to.be.false; + expect(result.error).to.match(/transform/i);
360-387: Test title claims XmlSigner but uses SignedXmlRename for accuracy or switch to
XmlDSigSignerto exercise the new API.-it("should validate signatures created by XmlSigner", function () { +it("should validate signatures created by SignedXml (XmlSigner compatibility)", function () {src/xmldsig-validator.ts (3)
12-47: Optional: add WS-Security mode toggle to align with examplesExpose an
enableWSSecurityMode?: booleanoption and passidMode: "wssecurity"to SignedXml when true; skip settingidAttributesin that case. This matches the WS-Security profile used in examples.Also applies to: 165-172
159-163: Fix class name in error messageUse the correct name to avoid confusion in logs/tests.
- "XmlValidator requires either a publicCert or getCertFromKeyInfo function in options.", + "XmlDSigValidator requires either a publicCert or getCertFromKeyInfo function in options.",
183-186: Avoid setting legacy idAttributes when using QName-based IDsSetting
idAttributeswith namespaced QNames is ineffective and can conflict. Only set when all entries are unprefixed.- // Set ID attributes on the SignedXml instance - if (this.options.idAttributeQNames) { - signedXml.idAttributes = this.options.idAttributeQNames; - } + // Set legacy idAttributes only for unprefixed names + if (this.options.idAttributeQNames?.every((n) => !n.includes(":"))) { + signedXml.idAttributes = this.options.idAttributeQNames; + }
🧹 Nitpick comments (5)
test/xmldsig-signer.spec.ts (1)
36-38: Prefer Algorithms constants over raw strings for readability and consistencyReplace hardcoded URIs with
Algorithms.signature/*,Algorithms.canonicalization/*, andAlgorithms.hash/*to avoid typos and ease refactoring later. Example:- signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", + signatureAlgorithm: Algorithms.signature.RSA_SHA1, + canonicalizationAlgorithm: Algorithms.canonicalization.EXCLUSIVE_C14N,Also applies to: 62-68, 86-89, 110-115, 138-144, 161-166, 187-191, 210-221, 269-277, 354-359, 573-579
test/xmldsig-validator.spec.ts (1)
24-29: Use transform constants in transforms for claritySwap
canonicalization.EXCLUSIVE_C14Nfortransform.EXCLUSIVE_C14Nwhen populatingReference.transforms. Values are identical today, but using the correct namespace avoids confusion. Example:- transforms: [canonicalization.EXCLUSIVE_C14N], + transforms: [transform.EXCLUSIVE_C14N],Also applies to: 42-46, 57-61, 205-209, 327-333
example/xmldsig-signer-example.js (2)
3-9: Guard fallback import for pre-release compatibilityOlder published versions may not export
XmlDSigSigner/pemToDer. Add a runtime check and message to avoid confusing runtime errors.try { ({ XmlDSigSigner, Algorithms, pemToDer } = require("../lib/index")); } catch (error) { - ({ XmlDSigSigner, Algorithms, pemToDer } = require("xml-crypto")); + ({ XmlDSigSigner, Algorithms, pemToDer } = require("xml-crypto")); + if (!XmlDSigSigner || !pemToDer) { + console.warn("[examples] Installed xml-crypto version does not expose XmlDSigSigner/pemToDer yet. Build locally (npm run build) or use a version that includes the wrappers."); + } }
29-34: Avoid double-base64 if input is already DER
pemToDer(...).toString("base64")is correct for PEM, but ifcertPathever points to DER, this re-encodes arbitary bytes. Consider a small type/format check or keep as-is with a comment clarifying PEM input only.src/xmldsig-signer.ts (1)
342-373: Normalize inclusiveNamespacesPrefixList to an array and widen key type in factoryEnsure consistent shape for downstream code and match privateKey type.
-private createSignedXml(privateKey: crypto.KeyLike): SignedXml { +private createSignedXml(privateKey: crypto.KeyLike | string | Buffer): SignedXml { @@ - return new SignedXml({ + const inclusive = + this.options.inclusiveNamespacesPrefixList == null + ? undefined + : Array.isArray(this.options.inclusiveNamespacesPrefixList) + ? this.options.inclusiveNamespacesPrefixList + : [this.options.inclusiveNamespacesPrefixList]; + + return new SignedXml({ privateKey, signatureAlgorithm: this.options.signatureAlgorithm, canonicalizationAlgorithm: this.options.canonicalizationAlgorithm, - inclusiveNamespacesPrefixList: this.options.inclusiveNamespacesPrefixList, + inclusiveNamespacesPrefixList: inclusive, keyInfoAttributes: keyInfoAttributes, getKeyInfoContent: getKeyInfoContent, objects: this.options.objects, idAttributeQNames: this.options.idAttributeQNames, namespaceMap: this.options.namespaceMap, });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
example/xmldsig-signer-example.js(1 hunks)example/xmldsig-validator-example.js(1 hunks)src/index.ts(1 hunks)src/utils.ts(2 hunks)src/xmldsig-signer.ts(1 hunks)src/xmldsig-validator.ts(1 hunks)test/xmldsig-signer.spec.ts(1 hunks)test/xmldsig-validator.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/index.ts
- src/utils.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.309Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.734Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.441Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.441Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/xmldsig-validator.spec.tstest/xmldsig-signer.spec.tssrc/xmldsig-signer.ts
📚 Learning: 2025-10-25T14:41:13.608Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.608Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
example/xmldsig-validator-example.js
🧬 Code graph analysis (6)
example/xmldsig-signer-example.js (4)
example/xmldsig-validator-example.js (7)
require(12-19)require(209-209)result(33-33)result(65-65)result(94-94)result(126-126)result(150-150)src/index.ts (2)
XmlDSigSigner(7-7)Algorithms(9-9)src/xmldsig-signer.ts (1)
XmlDSigSigner(213-374)src/utils.ts (1)
pemToDer(163-175)
test/xmldsig-validator.spec.ts (2)
src/xmldsig-signer.ts (1)
createSignedXml(342-373)src/xmldsig-validator.ts (2)
createSignedXml(157-189)XmlDSigValidator(89-212)
example/xmldsig-validator-example.js (3)
example/xmldsig-signer-example.js (8)
result(76-76)result(119-119)result(156-156)result(221-221)result(307-307)result(337-337)result1(367-367)result2(379-379)src/index.ts (2)
XmlDSigValidator(8-8)Algorithms(9-9)src/xmldsig-validator.ts (1)
XmlDSigValidator(89-212)
test/xmldsig-signer.spec.ts (1)
src/xmldsig-signer.ts (3)
XmlDSigSigner(213-374)SigningReference(146-146)ReferenceAttributes(41-48)
src/xmldsig-validator.ts (2)
src/index.ts (2)
XmlDSigValidator(8-8)SignedXml(6-6)src/signed-xml.ts (1)
SignedXml(30-1523)
src/xmldsig-signer.ts (3)
src/types.ts (4)
ComputeSignatureOptionsLocation(83-86)SignatureAlgorithmType(19-19)CanonicalizationAlgorithmType(13-13)ComputeSignatureOptions(100-105)src/utils.ts (1)
buildIdXPathWithNamespaces(407-427)src/signed-xml.ts (1)
getKeyInfoContent(203-232)
🔇 Additional comments (2)
src/xmldsig-signer.ts (1)
284-289: Good guard: reject URI inside attributesThe early reject prevents ambiguous configuration and keeps URI source single-sourced.
example/xmldsig-validator-example.js (1)
3-9: Import pattern is acceptable for development examples.The try-catch fallback from local build to published package is a reasonable approach for development and testing, while maintaining end-user perspective through the fallback.
ca57699 to
414ec7c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/signed-xml.ts (1)
507-517: Harden against XPath injection in validateElementAgainstReferencesUnlike
validateReference(Lines 551–554), this path does not reject URIs containing quotes before embedding into XPath. Add the same guard here.Apply:
- const uri = ref.uri?.[0] === "#" ? ref.uri.substring(1) : ref.uri; + const uri = ref.uri?.[0] === "#" ? ref.uri.substring(1) : ref.uri; + if (uri?.includes("'")) { + throw new Error("Cannot validate a uri with quotes inside it"); + }
♻️ Duplicate comments (8)
src/signed-xml.ts (1)
511-523: Bug: namespace-uri predicate used when attribute has no namespaceWhen
attr.namespaceURIis falsy, XPath becomesnamespace-uri(.)='undefined'. Mirror the conditional logic used invalidateReference.Apply:
- ref.xpath = `//*[@*[local-name(.)='${attr.localName}' and namespace-uri(.)='${attr.namespaceURI}']='${uri}']`; + if (attr.namespaceURI) { + ref.xpath = `//*[@*[local-name(.)='${attr.localName}' and namespace-uri(.)='${attr.namespaceURI}']='${uri}']`; + } else { + ref.xpath = `//*[@*[local-name(.)='${attr.localName}']='${uri}']`; + }test/xmldsig-validator.spec.ts (2)
360-387: Test title claims XmlSigner but uses SignedXmlRename the title or use the wrapper to avoid confusion.
Suggestion:
-it("should validate signatures created by XmlSigner", function () { +it("should validate signatures created by SignedXml (XmlDSigSigner compatibility)", function () {
279-290: “maxTransforms” test doesn’t exercise or assert the limitIt signs with a single transform and no enforcement exists; the test will always pass.
Update the test to exceed the cap and expect failure, and add enforcement in the validator (see comment in src/xmldsig-validator.ts).
Example change here:
- const signedXml = createSignedXml(xml); + // Create a signature with two transforms + const sig = new SignedXml({ + privateKey, + canonicalizationAlgorithm: canonicalization.EXCLUSIVE_C14N, + signatureAlgorithm: signature.RSA_SHA1, + }); + sig.addReference({ + xpath: "//*[local-name(.)='test']", + digestAlgorithm: hash.SHA1, + transforms: [ + Algorithms.transform.ENVELOPED_SIGNATURE, + Algorithms.canonicalization.EXCLUSIVE_C14N, + ], + }); + sig.computeSignature(xml); + const signedXml = sig.getSignedXml(); @@ - expect(result.valid).to.be.true; + expect(result.valid).to.be.false; + expect(result.error).to.match(/transform/i);src/xmldsig-validator.ts (3)
53-55: Allow PEM string/Buffer for publicCertTests and examples pass PEM strings; typing only
KeyLikeis too narrow.-export type PublicCertXmlValidatorOptions = CommonValidatorOptions & { - publicCert: KeyLike; -}; +export type PublicCertXmlValidatorOptions = CommonValidatorOptions & { + publicCert: KeyLike | string | Buffer; +};
138-146: Enforce maxTransforms before cryptographic verificationDefensive check: reject if any Reference has more transforms than allowed.
- // Perform cryptographic validation + // Enforce maxTransforms limit (defense-in-depth) + if (this.options.maxTransforms != null) { + const over = signedXml + .getReferences?.() + ?.find((r: any) => Array.isArray(r.transforms) && r.transforms.length > this.options.maxTransforms!); + if (over) { + return this.handleError( + `Reference contains ${over.transforms.length} transforms (limit=${this.options.maxTransforms}).`, + ); + } + } + // Perform cryptographic validation const isValid = signedXml.checkSignature(xml);
159-163: Fix class name in error messageUse the correct class name to avoid confusion.
- throw new Error( - "XmlValidator requires either a publicCert or getCertFromKeyInfo function in options.", - ); + throw new Error( + "XmlDSigValidator requires either a publicCert or getCertFromKeyInfo function in options.", + );example/xmldsig-validator-example.js (1)
209-213: DOMParser.parseFromString missing mimeTypePass the content type to avoid runtime issues.
- const doc = new DOMParser().parseFromString(signedTwice); + const doc = new DOMParser().parseFromString(signedTwice, "application/xml");src/xmldsig-signer.ts (1)
256-276: Widen privateKey type to accept common formats.The
privateKeyparameter currently only acceptscrypto.KeyLike, but should also acceptstring | Bufferto match common usage patterns with PEM-encoded keys.
🧹 Nitpick comments (7)
src/types.ts (2)
13-19: Keep algorithm type safety while allowing extensibilitySwitching to plain
stringremoves useful checking. Prefer an open union: known literals OR branded string.Example pattern:
-export type CanonicalizationAlgorithmType = string; +export type KnownCanonicalizationAlgorithm = + | "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" + | "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" + | "http://www.w3.org/2001/10/xml-exc-c14n#" + | "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; +export type CanonicalizationAlgorithmType = KnownCanonicalizationAlgorithm | (string & {});Apply similarly to CanonicalizationOrTransformAlgorithmType, HashAlgorithmType, SignatureAlgorithmType. This keeps autocomplete and catches typos while still permitting custom algorithms.
51-53: Doc the new QName options in JSDoc for discoverabilityAdd brief JSDoc to
idAttributeQNamesandnamespaceMapdescribing precedence overidAttributes/idModeand expected QName format (e.g.,["wsu:Id", "Id"]).src/algorithms.ts (1)
1-44: Algorithm registry looks good; consider exporting group types for DXThe constants/URIs are correct. Optionally export per-group types to help consumers type options precisely.
Example:
export const Algorithms = { canonicalization: CANONICALIZATION_ALGORITHMS, transform: TRANSFORM_ALGORITHMS, hash: HASH_ALGORITHMS, signature: SIGNATURE_ALGORITHMS, }; + +export type CanonicalizationAlgorithm = + typeof CANONICALIZATION_ALGORITHMS[keyof typeof CANONICALIZATION_ALGORITHMS]; +export type TransformAlgorithm = + typeof TRANSFORM_ALGORITHMS[keyof typeof TRANSFORM_ALGORITHMS]; +export type HashAlgorithm = + typeof HASH_ALGORITHMS[keyof typeof HASH_ALGORITHMS]; +export type SignatureAlgorithm = + typeof SIGNATURE_ALGORITHMS[keyof typeof SIGNATURE_ALGORITHMS];src/signed-xml.ts (1)
1375-1392: Avoid redundant xmlns re-declaration when setting QName IDsBefore calling
setAttributeNSforxmlns:${prefix}, check if the prefix is already bound on the element or ancestors to prevent duplicate namespace attributes.Example:
- if (namespace) { - node.setAttributeNS("http://www.w3.org/2000/xmlns/", `xmlns:${prefix}`, namespace); - node.setAttributeNS(namespace, qName, id); + if (namespace) { + const current = node.lookupNamespaceURI(prefix); + if (current !== namespace) { + node.setAttributeNS("http://www.w3.org/2000/xmlns/", `xmlns:${prefix}`, namespace); + } + node.setAttributeNS(namespace, qName, id);test/xmldsig-signer.spec.ts (1)
540-571: Prefer Algorithms constants over raw URIs in testsUsing
Algorithmsavoids string drift and improves readability.Example:
-const algorithms = [ - "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", - "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", -]; +import { Algorithms } from "../src"; +const algorithms = [ + Algorithms.signature.RSA_SHA1, + Algorithms.signature.RSA_SHA256, + Algorithms.signature.RSA_SHA512, +];src/xmldsig-validator.ts (2)
183-186: Don’t assign QName values into idAttributesWhen
idAttributeQNamesis set, SignedXml already uses it; assigning colonized QNames intoidAttributescan be misleading. Only setidAttributesif all names are non‑prefixed.- if (this.options.idAttributeQNames) { - signedXml.idAttributes = this.options.idAttributeQNames; - } + if (this.options.idAttributeQNames?.every((n) => !n.includes(":"))) { + signedXml.idAttributes = this.options.idAttributeQNames; + }
12-47: Optional: add WS‑Security convenience flagMany users expect a simple switch. Offer
enableWSSecurityMode?: booleanto passidMode: "wssecurity"through to SignedXml automatically (or document usingidAttributeQNames: ["wsu:Id"]withnamespaceMap).Based on learnings.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
.gitignore(1 hunks).nvmrc(1 hunks)example/xmldsig-signer-example.js(1 hunks)example/xmldsig-validator-example.js(1 hunks)src/algorithms.ts(1 hunks)src/index.ts(1 hunks)src/signed-xml.ts(7 hunks)src/types.ts(2 hunks)src/utils.ts(2 hunks)src/xmldsig-signer.ts(1 hunks)src/xmldsig-validator.ts(1 hunks)test/xmldsig-signer.spec.ts(1 hunks)test/xmldsig-validator.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- src/index.ts
- .gitignore
- src/utils.ts
- example/xmldsig-signer-example.js
- .nvmrc
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/xmldsig-signer.tssrc/signed-xml.tssrc/xmldsig-validator.tstest/xmldsig-validator.spec.ts
🧬 Code graph analysis (6)
src/algorithms.ts (1)
src/index.ts (1)
Algorithms(9-9)
src/xmldsig-signer.ts (3)
src/types.ts (4)
ComputeSignatureOptionsLocation(83-86)SignatureAlgorithmType(19-19)CanonicalizationAlgorithmType(13-13)ComputeSignatureOptions(100-105)src/utils.ts (1)
buildIdXPathWithNamespaces(407-427)src/signed-xml.ts (1)
getKeyInfoContent(203-232)
src/xmldsig-validator.ts (2)
src/index.ts (2)
XmlDSigValidator(8-8)SignedXml(6-6)src/signed-xml.ts (1)
SignedXml(30-1494)
example/xmldsig-validator-example.js (3)
example/xmldsig-signer-example.js (8)
result(76-76)result(119-119)result(156-156)result(221-221)result(307-307)result(337-337)result1(367-367)result2(379-379)src/index.ts (2)
XmlDSigValidator(8-8)Algorithms(9-9)src/xmldsig-validator.ts (1)
XmlDSigValidator(89-212)
test/xmldsig-signer.spec.ts (1)
src/xmldsig-signer.ts (3)
XmlDSigSigner(213-374)SigningReference(146-146)ReferenceAttributes(41-48)
test/xmldsig-validator.spec.ts (2)
src/xmldsig-signer.ts (1)
createSignedXml(342-373)src/xmldsig-validator.ts (2)
createSignedXml(157-189)XmlDSigValidator(89-212)
🔇 Additional comments (7)
src/xmldsig-signer.ts (7)
1-11: LGTM: Clean imports and sensible defaults.The imports are appropriate and the default ID attribute QNames cover the most common XML ID conventions.
13-207: LGTM: Well-documented type definitions.The type definitions are comprehensive and well-documented with XMLDSig spec references. The discriminated union for
SigningReference(xpath vs uri) provides good type safety.
222-246: LGTM: Solid constructor validation and normalization.The required field validation and default ID attribute handling are well-implemented. Preparing
computeOptionsonce for reuse is a good performance optimization.
284-288: LGTM: Good validation to prevent misuse.Validating that URI is not in attributes prevents confusion about where URI should be specified in the reference configuration.
291-302: LGTM: XPath reference handling is correct.The conversion from
SigningReferenceto SignedXml format correctly handles XPath-based references with appropriate defaults.
303-340: LGTM: Comprehensive URI reference handling with good security.The URI-based reference handling correctly supports:
- Enveloped signatures (empty URI)
- Fragment identifiers with namespace-aware ID lookup
- Security: External URIs are appropriately rejected since signing can only operate on document content
342-373: LGTM: Proper SignedXml instantiation with filtered attributes.The
createSignedXmlmethod correctly:
- Handles KeyInfo configuration with content generation
- Filters undefined values from attributes (preventing empty attributes)
- Passes through all necessary options including namespace-aware ID handling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (7)
test/xmldsig-validator.spec.ts (1)
679-708: Rename test title or switch to XmlDSigSignerThe test body uses SignedXml, but the title says XmlSigner. Rename for accuracy or update to exercise the new XmlDSigSigner.
- it("should validate signatures created by XmlSigner", function () { + it("should validate signatures created by SignedXml (XmlDSigSigner compatibility)", function () {src/xmldsig-validator.ts (4)
199-204: Defense-in-depth: enforce maxTransforms after loading the signatureSignedXml enforces during load, but validating here guards against future regressions and alternate code paths.
- // Perform cryptographic validation + // Enforce maxTransforms before cryptographic validation (defense-in-depth) + if (this.options.security.maxTransforms != null) { + const over = (this.signedXml as any) + .getReferences?.() + ?.find((r: any) => Array.isArray(r.transforms) && r.transforms.length > this.options.security.maxTransforms!); + if (over) { + return this.handleError( + `Reference contains ${over.transforms.length} transforms (limit=${this.options.security.maxTransforms}).`, + ); + } + } + // Perform cryptographic validation const isValid = this.signedXml.checkSignature(xml);
214-233: Optional: add WS‑Security mode flag to simplify configurationAdd enableWSSecurityMode?: boolean and pass idMode: "wssecurity" to SignedXml when true, so users don’t have to wire idAttributeQNames/namespaceMap manually.
@@ -interface XmlDSigValidatorOptions { +interface XmlDSigValidatorOptions { + /** Treat only wsu:Id as ID per WS‑Security profile */ + enableWSSecurityMode?: boolean; @@ - const signedXmlOptions = { + const signedXmlOptions = { publicCert: undefined as KeyLike | undefined, getCertFromKeyInfo: undefined as KeySelectorFunction | undefined, idAttributeQNames: this.options.idAttributeQNames, namespaceMap: this.options.namespaceMap, maxTransforms: this.options.security.maxTransforms, implicitTransforms: this.options.implicitTransforms, + idMode: this.options.enableWSSecurityMode ? "wssecurity" : undefined, };
123-125: Error message should use the correct class nameUse XmlDSigValidator for clarity.
- throw new Error("XmlValidator requires a keySelector in options."); + throw new Error("XmlDSigValidator requires a keySelector in options.");
220-222: Error message should use the correct class nameSame naming fix here.
- throw new Error( - "XmlValidator requires either a publicCert or getCertFromKeyInfo function in options.", - ); + throw new Error( + "XmlDSigValidator requires either a publicCert or getCertFromKeyInfo function in options.", + );src/signed-xml.ts (1)
514-525: Fix QName XPath when attribute has no namespaceAvoid generating namespace-uri(.)='undefined' for non-namespaced attrs. Mirror the conditional used in validateReference.
- if (uri === elemId) { - ref.xpath = `//*[@*[local-name(.)='${attr.localName}' and namespace-uri(.)='${attr.namespaceURI}']='${uri}']`; - break; // found the correct element, no need to check further - } + if (uri === elemId) { + if (attr.namespaceURI) { + ref.xpath = `//*[@*[local-name(.)='${attr.localName}' and namespace-uri(.)='${attr.namespaceURI}']='${uri}']`; + } else { + ref.xpath = `//*[@*[local-name(.)='${attr.localName}']='${uri}']`; + } + break; // found the correct element, no need to check further + }example/xmldsig-validator-example.js (1)
231-234: Missing required mimeType in DOMParser.parseFromStringPass "application/xml" (or "text/xml") as the second argument to avoid runtime errors.
- const doc = new DOMParser().parseFromString(signedTwice); + const doc = new DOMParser().parseFromString(signedTwice, "application/xml");
🧹 Nitpick comments (7)
test/xmldsig-validator.spec.ts (1)
433-447: Add a failure-path test for maxTransforms to actually exercise the limitCurrently only the success case with 1 transform is covered. Add a test that signs with ≥2 transforms and asserts validation fails when maxTransforms: 1.
@@ it("should validate with maxTransforms limit", function () { @@ expect(result.valid).to.be.true; }); + + it("should reject when Reference has more transforms than maxTransforms", function () { + const xml = "<root><test>content</test></root>"; + const sig = new SignedXml({ + privateKey, + canonicalizationAlgorithm: canonicalization.EXCLUSIVE_C14N, + signatureAlgorithm: signature.RSA_SHA1, + }); + sig.addReference({ + xpath: "//*[local-name(.)='test']", + digestAlgorithm: hash.SHA1, + transforms: [ + Algorithms.transform.ENVELOPED_SIGNATURE, + canonicalization.EXCLUSIVE_C14N, + ], + }); + sig.computeSignature(xml); + const signedXml = sig.getSignedXml(); + + const validator = new XmlDSigValidator({ + keySelector: { publicCert }, + security: { maxTransforms: 1 }, + throwOnError: false, + }); + const result = validator.validate(signedXml); + expect(result.valid).to.be.false; + expect(result.error).to.match(/exceeds the maximum/i); + });src/types.ts (3)
126-129: Type accuracy: digestValue should be a string (base64)Unknown weakens safety and downstream messaging. Prefer string.
- // Optional. The digest value of the referenced data. - digestValue?: unknown; + // Optional. The digest value of the referenced data (base64). + digestValue?: string;
13-20: Optional: restore stricter algorithm typings via literal unionsUsing plain strings degrades DX. Consider exporting algorithm literal unions (or deriving from Algorithms registry) to catch typos at compile time.
113-149: Future: split Reference into SigningReference and ValidationReferenceDifferent fields are meaningful per phase (e.g., xpath/id/type vs uri/digestValue/validationError). Splitting clarifies contracts and avoids accidental misuse. Based on learnings.
src/xmldsig-validator.ts (2)
23-44: Naming consistency: XmlDSig vs XmlDsigConsider renaming XmlDsigValidatorSecurityOptions to XmlDSigValidatorSecurityOptions for consistency with the class name.
138-149: Minor: simplify truststore guardA direct nullish coalescing/array check improves readability.
- if ("truststore" in this.options.security && this.options.security.truststore !== undefined) { + if (Array.isArray(this.options.security.truststore)) {example/xmldsig-validator-example.js (1)
1-332: Add mimeType parameter to all parseFromString() callsYour scan confirms 50+ instances of
parseFromString()calls missing themimeTypeparameter across test files, source files, and examples. The call at line 233 inexample/xmldsig-validator-example.jsis one of many that should specify"text/xml"as the second argument for consistency and proper document parsing:const doc = new DOMParser().parseFromString(signedTwice, "text/xml");This should be applied uniformly across:
test/**/*.spec.ts(~30 instances)src/signed-xml.ts(~4 instances)example/xmldsig-validator-example.js(line 233)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (6)
test/static/chain_client.crt.pemis excluded by!**/*.pemtest/static/chain_client.key.pemis excluded by!**/*.pemtest/static/chain_root.crt.pemis excluded by!**/*.pemtest/static/chain_root.key.pemis excluded by!**/*.pemtest/static/expired_certificate.crt.pemis excluded by!**/*.pemtest/static/expired_certificate.key.pemis excluded by!**/*.pem
📒 Files selected for processing (6)
example/xmldsig-validator-example.js(1 hunks)src/signed-xml.ts(10 hunks)src/types.ts(2 hunks)src/xmldsig-validator.ts(1 hunks)test/static/chain_root.crt.srl(1 hunks)test/xmldsig-validator.spec.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- test/static/chain_root.crt.srl
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/signed-xml.tssrc/xmldsig-validator.tstest/xmldsig-validator.spec.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/signed-xml.ts
🧬 Code graph analysis (4)
example/xmldsig-validator-example.js (3)
example/xmldsig-signer-example.js (9)
fs(1-1)result(76-76)result(119-119)result(156-156)result(221-221)result(307-307)result(337-337)result1(367-367)result2(379-379)src/xmldsig-validator.ts (1)
XmlDSigValidator(112-305)src/index.ts (2)
XmlDSigValidator(8-8)Algorithms(9-9)
src/signed-xml.ts (2)
example/xmldsig-validator-example.js (2)
xpath(232-232)doc(233-233)example/example.js (1)
doc(17-17)
src/xmldsig-validator.ts (3)
src/index.ts (2)
XmlDSigValidator(8-8)SignedXml(6-6)src/signed-xml.ts (2)
SignedXml(30-1505)getCertFromKeyInfo(244-253)src/types.ts (1)
KeySelectorFunction(45-45)
test/xmldsig-validator.spec.ts (2)
src/xmldsig-validator.ts (2)
createSignedXml(214-282)XmlDSigValidator(112-305)src/signed-xml.ts (1)
SignedXml(30-1505)
🔇 Additional comments (1)
src/signed-xml.ts (1)
1198-1201: Retaining ref.uri mutation during signing is OK for nowSetting ref.uri = id aligns with existing behavior and the maintainer’s request to defer changes.
7c419b5 to
8fd99f2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/xmldsig-validator.ts (1)
207-216: Incorrect class name in error message.The error message references "XmlValidator" but the class is named "XmlDSigValidator". Update for consistency.
Apply this diff:
throw new Error( - "XmlValidator requires either a publicCert or getCertFromKeyInfo function in options.", + "XmlDSigValidator requires either a publicCert or getCertFromKeyInfo function in options.", );src/signed-xml.ts (1)
807-813: maxTransforms check needs null-safe comparison.The truthy check
if (this.maxTransforms)will skip enforcement whenmaxTransformsis set to0(a valid limit). A past review comment indicated this was addressed, but the code still uses the truthy check.Apply this diff to fix:
- if (this.maxTransforms) { - if (transforms.length > this.maxTransforms) { + if (this.maxTransforms != null) { + if (transforms.length > this.maxTransforms) { throw new Error( `Number of transforms (${transforms.length}) exceeds the maximum allowed (${this.maxTransforms})`, ); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (6)
test/static/chain_client.crt.pemis excluded by!**/*.pemtest/static/chain_client.key.pemis excluded by!**/*.pemtest/static/chain_root.crt.pemis excluded by!**/*.pemtest/static/chain_root.key.pemis excluded by!**/*.pemtest/static/expired_certificate.crt.pemis excluded by!**/*.pemtest/static/expired_certificate.key.pemis excluded by!**/*.pem
📒 Files selected for processing (12)
.gitignore(1 hunks).nvmrc(1 hunks)src/algorithms.ts(1 hunks)src/index.ts(1 hunks)src/signed-xml.ts(11 hunks)src/types.ts(2 hunks)src/utils.ts(3 hunks)src/xmldsig-signer.ts(1 hunks)src/xmldsig-validator.ts(1 hunks)test/static/chain_root.crt.srl(1 hunks)test/xmldsig-signer.spec.ts(1 hunks)test/xmldsig-validator.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/algorithms.ts
- .nvmrc
- test/static/chain_root.crt.srl
- src/index.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/signed-xml.tssrc/xmldsig-signer.tssrc/xmldsig-validator.tstest/xmldsig-validator.spec.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/signed-xml.ts
🧬 Code graph analysis (6)
src/utils.ts (1)
src/types.ts (1)
IdAttributeType(13-13)
src/signed-xml.ts (1)
src/types.ts (1)
IdAttributeType(13-13)
src/xmldsig-signer.ts (3)
src/types.ts (6)
ObjectAttributes(36-45)IdAttributeType(13-13)ComputeSignatureOptionsLocation(87-90)SignatureAlgorithmType(21-21)CanonicalizationAlgorithmType(15-15)ComputeSignatureOptions(104-109)src/signed-xml.ts (2)
SignedXml(31-1476)getKeyInfoContent(202-231)src/utils.ts (1)
buildIdXPathWithNamespaces(407-432)
test/xmldsig-signer.spec.ts (2)
src/xmldsig-signer.ts (3)
XmlDSigSigner(207-363)SigningReference(147-147)ReferenceAttributes(42-49)src/signed-xml.ts (1)
SignedXml(31-1476)
src/xmldsig-validator.ts (2)
src/types.ts (2)
IdAttributeType(13-13)KeySelectorFunction(47-47)src/signed-xml.ts (2)
SignedXml(31-1476)getCertFromKeyInfo(240-249)
test/xmldsig-validator.spec.ts (3)
src/algorithms.ts (1)
Algorithms(39-44)src/xmldsig-validator.ts (2)
createSignedXml(207-274)XmlDSigValidator(105-297)src/signed-xml.ts (1)
SignedXml(31-1476)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/signed-xml.ts (3)
171-176: Regression: default key extraction disabled.Constructor overwrites getCertFromKeyInfo with noop when no override is supplied, breaking KeyInfo-based validation. Restore the static default.
- this.getKeyInfoContent = getKeyInfoContent ?? this.getKeyInfoContent; - this.getCertFromKeyInfo = getCertFromKeyInfo ?? SignedXml.noop; + this.getKeyInfoContent = getKeyInfoContent ?? this.getKeyInfoContent; + // Preserve the class default (SignedXml.getCertFromKeyInfo) unless explicitly overridden. + this.getCertFromKeyInfo = getCertFromKeyInfo ?? this.getCertFromKeyInfo;
547-576: False positive “multiple elements with same ID” when the same element matches multiple idAttributes.The code sums matches across different idAttributes and throws when total > 1, even if all matches point to the same element (e.g., both Id and ID present on the same node). Track distinct element identity instead.
- let num_elements_for_id = 0; + let matchedElem: Element | null = null; for (const idAttr of this.idAttributes) { let tmp_elemXpath: string; if (typeof idAttr === "string") { tmp_elemXpath = `//*[@*[local-name(.)='${idAttr}']='${uri}']`; } else { if (idAttr.namespaceUri) { tmp_elemXpath = `//*[@*[local-name(.)='${idAttr.localName}' and namespace-uri(.)='${idAttr.namespaceUri}']='${uri}']`; } else { tmp_elemXpath = `//*[@*[local-name(.)='${idAttr.localName}']='${uri}']`; } } - const tmp_elem = xpath.select(tmp_elemXpath, doc); - if (utils.isArrayHasLength(tmp_elem)) { - num_elements_for_id += tmp_elem.length; - if (num_elements_for_id > 1) { - throw new Error( - "Cannot validate a document which contains multiple elements with the " + - "same value for the ID / Id / Id attributes, in order to prevent " + - "signature wrapping attack.", - ); - } - elem = tmp_elem[0]; - ref.xpath = tmp_elemXpath; - } + const tmp_elem = xpath.select(tmp_elemXpath, doc); + if (utils.isArrayHasLength(tmp_elem)) { + const candidate = tmp_elem[0] as Element; + if (matchedElem && candidate !== matchedElem) { + throw new Error( + "Cannot validate a document which contains multiple distinct elements with the same ID value (prevents signature wrapping).", + ); + } + matchedElem = candidate; + elem = candidate; + ref.xpath = tmp_elemXpath; + } }
566-571: Typo/clarity in error message.Use consistent casing for attribute names.
- "same value for the ID / Id / Id attributes, in order to prevent " + + "same value for the ID / Id / id attributes, in order to prevent " +
♻️ Duplicate comments (1)
test/xmldsig-validator.spec.ts (1)
683-711: Exercise XmlDSigSigner once it’s ready.Title references XmlDSigSigner but the body simulates with SignedXml. Fine for now; please switch to XmlDSigSigner in this test when available to prevent drift.
🧹 Nitpick comments (2)
test/xmldsig-validator.spec.ts (1)
23-41: Add SHA‑256 coverage alongside SHA‑1.Current helpers/signatures use SHA‑1. Please add at least one test path that signs with SHA‑256 (hash.SHA256 + signature.RSA_SHA256) to avoid normalizing legacy algorithms and to exercise stronger defaults. Keep existing SHA‑1 tests for backward compatibility.
Also applies to: 44-74
src/xmldsig-validator.ts (1)
218-274: Optional: WS‑Security convenience flag.You already accept idAttributes; consider adding an enableWSSecurityMode boolean that passes idMode: "wssecurity" to SignedXml to ease WS‑Security usage without requiring callers to craft QName arrays. Low-risk additive API.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/signed-xml.ts(11 hunks)src/xmldsig-validator.ts(1 hunks)test/xmldsig-validator.spec.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/xmldsig-validator.spec.tssrc/xmldsig-validator.tssrc/signed-xml.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/signed-xml.ts
🧬 Code graph analysis (3)
test/xmldsig-validator.spec.ts (3)
src/algorithms.ts (1)
Algorithms(39-44)src/xmldsig-validator.ts (2)
createSignedXml(207-274)XmlDSigValidator(105-297)src/signed-xml.ts (1)
SignedXml(31-1476)
src/xmldsig-validator.ts (2)
src/types.ts (2)
IdAttributeType(13-13)KeySelectorFunction(47-47)src/signed-xml.ts (2)
SignedXml(31-1476)getCertFromKeyInfo(240-249)
src/signed-xml.ts (1)
src/types.ts (1)
IdAttributeType(13-13)
🔇 Additional comments (1)
src/xmldsig-validator.ts (1)
106-145: Remove this review comment; the proposed change contradicts the documented API contract and claims are unsubstantiated.The constructor docstring explicitly states "The instance can be reused for multiple validations" (line 111). The underlying
SignedXml.loadSignature()clears state viathis.references = [](line 696 in signed-xml.ts), preventing cross-talk. JavaScript is single-threaded—no concurrency model exists to "break." The review's claimed "design goal of new internal SignedXml per call" is not documented in the codebase and contradicts the existing API contract.While there is a minor architectural inconsistency (XmlDSigValidator uses shared state; XmlDSigSigner uses per-call), this is a separate optional refactoring concern, not a bug or violation. Current tests pass; no state bleed occurs.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/c14n-canonicalization.ts (1)
87-94: Blocker: incorrect type for defaultNsForPrefix (causes TS error at usages)renderNs declares defaultNsForPrefix as string but uses it as a map (e.g., defaultNsForPrefix[node.prefix]). Fix the signature to Record<string, string>.
Apply:
- renderNs( - node: Element, - prefixesInScope: string[], - defaultNs: string, - defaultNsForPrefix: string, - ancestorNamespaces: NamespacePrefix[], - ): RenderedNamespace { + renderNs( + node: Element, + prefixesInScope: string[], + defaultNs: string, + defaultNsForPrefix: Record<string, string>, + ancestorNamespaces: NamespacePrefix[], + ): RenderedNamespace {
🧹 Nitpick comments (11)
src/constants.ts (2)
14-17: Clarify/decouple transform vs canonicalization constantsTRANSFORM_ALGORITHMS spreads canonicalization URIs and adds ENVELOPED_SIGNATURE. This is pragmatic, but it blurs categories. Consider documenting this explicitly here, or (preferable next) separating transform-only names and mapping canonicalization via a dedicated lookup to reduce confusion in consumers.
39-44: Harden runtime immutability for security‑critical constantsUse Object.freeze on both the top-level and nested objects to prevent accidental mutation at runtime (as const is compile-time only).
Apply pattern like:
-export const Algorithms = { - canonicalization: CANONICALIZATION_ALGORITHMS, - transform: TRANSFORM_ALGORITHMS, - hash: HASH_ALGORITHMS, - signature: SIGNATURE_ALGORITHMS, -}; +export const Algorithms = Object.freeze({ + canonicalization: Object.freeze(CANONICALIZATION_ALGORITHMS), + transform: Object.freeze(TRANSFORM_ALGORITHMS), + hash: Object.freeze(HASH_ALGORITHMS), + signature: Object.freeze(SIGNATURE_ALGORITHMS), +} as const); -export const Namespaces = NAMESPACES; +export const Namespaces = Object.freeze(NAMESPACES);Also applies to: 46-56
src/types.ts (4)
10-10: Avoid runtime import for type queries to reduce couplingUsing a value import of Algorithms in a types module introduces an unnecessary runtime dependency. Switch to typeof import to keep this in the type space only.
Apply:
-import { Algorithms } from "./constants"; +type AlgorithmsConst = typeof import("./constants").Algorithms;Then replace occurrences of
typeof AlgorithmswithAlgorithmsConst, e.g.:-export type SignatureAlgorithmName = - | (typeof Algorithms.signature)[keyof typeof Algorithms.signature] +export type SignatureAlgorithmName = + | AlgorithmsConst["signature"][keyof AlgorithmsConst["signature"]] | string;Repeat similarly for hash/transform/canonicalization name types.
23-26: Narrow GetKeyInfoContentArgs.publicCert typegetKeyInfoContent only handles string/Buffer; allowing crypto.KeyLike suggests KeyObject support which is not implemented. Narrow to match behavior.
-export interface GetKeyInfoContentArgs { - publicCert?: crypto.KeyLike; +export interface GetKeyInfoContentArgs { + publicCert?: string | Buffer;
129-147: Define precedence and deprecate duplicate optionsBoth idAttribute and idAttributes exist; unclear precedence if both are set. Mark idAttribute as deprecated and specify that idAttributes takes precedence to avoid ambiguity.
Add JSDoc:
export interface SignedXmlOptions { idMode?: "wssecurity"; - idAttribute?: IdAttributeType; + /** + * @deprecated Use `idAttributes` instead. If both are provided, `idAttributes` takes precedence. + */ + idAttribute?: IdAttributeType; idAttributes?: IdAttributeType[];
194-196: Tighten digestValue typingDigest values are base64 strings in practice. Using unknown weakens consumer type safety for validation flows.
- digestValue?: unknown; + digestValue?: string;test/xmldsig-validator.spec.ts (3)
377-385: Exercise expiration check path explicitlyWith keySelector.publicCert, checkCertExpiration logic in getCertFromKeyInfo wrapper won’t run. Use getCertFromKeyInfo for the non‑expired case too to cover that branch.
-const validator = new XmlDSigValidator({ - keySelector: { publicCert }, - security: { checkCertExpiration: true }, -}); +const validator = new XmlDSigValidator({ + keySelector: { getCertFromKeyInfo: () => publicCert }, + security: { checkCertExpiration: true }, +});
291-292: Avoid brittle “fail” substring assertionsMatch a specific, stable error phrase to reduce test flakiness (e.g., “Id attribute namespace mismatch”).
-expectInvalidResult(validator.validate(signedXml), "fail"); +expectInvalidResult(validator.validate(signedXml), "Id attribute namespace mismatch");Apply to both assertions.
Also applies to: 314-315
25-43: Optional: include KeyInfo when using getCertFromKeyInfoTo better mimic real‑world flows, consider embedding X509Data via getKeyInfoContent in createSignedXml for tests that rely on keyInfo parsing; then have getCertFromKeyInfo read from the provided node.
Also applies to: 175-185
src/enveloped-signature.ts (1)
58-60: Return the namespaced constant, not a string literalUse Algorithms.transform.ENVELOPED_SIGNATURE for consistency with other algorithms.
- getAlgorithmName(): TransformAlgorithmName { - return "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; - } + getAlgorithmName(): TransformAlgorithmName { + return Algorithms.transform.ENVELOPED_SIGNATURE; + }Add import at top:
import { Algorithms } from "./constants";src/signed-xml.ts (1)
1417-1436: Consider defensive check for empty prefix.The code correctly handles
IdAttributeTypevariants. However, iffirstIdAttrhas type{ prefix: string; localName: string; namespaceUri: string }with an empty string prefix (e.g.,prefix: ""), the checkfirstIdAttr.prefixwould be falsy and fall through to theelsebranch, usingsetAttributeinstead ofsetAttributeNS. This would not properly handle the namespace.While the type system doesn't prevent empty strings and this is likely an edge case that won't occur in practice, a defensive check could prevent subtle bugs.
Consider strengthening the check:
- if ("prefix" in firstIdAttr && firstIdAttr.prefix) { + if ("prefix" in firstIdAttr && firstIdAttr.prefix && firstIdAttr.prefix.length > 0) {Or use a more explicit length check:
- if ("prefix" in firstIdAttr && firstIdAttr.prefix) { + if ("prefix" in firstIdAttr && typeof firstIdAttr.prefix === "string" && firstIdAttr.prefix.length > 0) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/c14n-canonicalization.ts(4 hunks)src/constants.ts(1 hunks)src/enveloped-signature.ts(2 hunks)src/exclusive-canonicalization.ts(5 hunks)src/index.ts(1 hunks)src/signed-xml.ts(26 hunks)src/types.ts(3 hunks)src/xmldsig-validator.ts(1 hunks)test/xmldsig-validator.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/index.ts
- src/xmldsig-validator.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/xmldsig-validator.spec.tssrc/signed-xml.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/signed-xml.tssrc/types.ts
🧬 Code graph analysis (7)
src/constants.ts (1)
src/index.ts (1)
Algorithms(8-8)
src/exclusive-canonicalization.ts (2)
src/types.ts (3)
CanonicalizationAlgorithm(114-120)TransformAlgorithmOptions(50-56)CanonicalizationAlgorithmName(110-112)src/constants.ts (1)
Algorithms(39-44)
src/enveloped-signature.ts (1)
src/types.ts (3)
TransformAlgorithm(103-107)TransformAlgorithmOptions(50-56)TransformAlgorithmName(99-101)
src/c14n-canonicalization.ts (2)
src/types.ts (3)
CanonicalizationAlgorithm(114-120)TransformAlgorithmOptions(50-56)CanonicalizationAlgorithmName(110-112)src/constants.ts (1)
Algorithms(39-44)
test/xmldsig-validator.spec.ts (7)
src/constants.ts (1)
Algorithms(39-44)src/xmldsig-validator.ts (3)
createSignedXml(265-331)XmlDSigValidationResult(122-137)XmlDSigValidator(142-353)src/signed-xml.ts (1)
SignedXml(33-1526)src/signature-algorithms.ts (1)
RsaSha1(4-28)src/hash-algorithms.ts (1)
Sha1(4-15)src/enveloped-signature.ts (1)
EnvelopedSignature(10-61)src/exclusive-canonicalization.ts (1)
ExclusiveCanonicalization(22-327)
src/signed-xml.ts (2)
src/types.ts (10)
IdAttributeType(14-17)SignatureAlgorithmName(58-60)CanonicalizationAlgorithmName(110-112)TransformAlgorithmName(99-101)CanonicalizationAlgorithmMap(121-124)HashAlgorithmMap(97-97)SignatureAlgorithmMap(88-88)TransformAlgorithmMap(108-108)HashAlgorithmName(90-90)TransformAlgorithmOptions(50-56)src/constants.ts (2)
Namespaces(56-56)Algorithms(39-44)
src/types.ts (1)
src/constants.ts (1)
Algorithms(39-44)
🔇 Additional comments (14)
src/types.ts (1)
181-217: Good: kept unified Reference shape (split later)Retains the existing Reference fields while adopting algorithm name types. This aligns with the maintainer’s request to split Signing vs Validation refs in a follow-up, not here.
Based on learnings
src/exclusive-canonicalization.ts (1)
324-337: LGTM: returns via centralized Algorithms constantsConsistent with the new API surface; no behavior change.
src/signed-xml.ts (12)
1-31: LGTM! Type system imports align with algorithm map refactor.The new type imports (algorithm names, maps, and IdAttributeType) support the broader refactoring to typed algorithm maps, consistent with changes in src/types.ts and src/constants.ts.
34-46: LGTM! IdAttributeType[] enables flexible ID attribute specifications.The field type change from
string[]toIdAttributeType[]allows both simple string-based IDs and QName-based IDs with namespace support, with clear documentation and examples.
56-73: LGTM! Algorithm fields properly typed.Algorithm fields now use the typed name types (SignatureAlgorithmName, CanonicalizationAlgorithmName, TransformAlgorithmName), and
maxTransformscorrectly usesnumber | nullto distinguish between "no limit" (null) and "zero allowed" (0).
105-120: LGTM! Algorithm maps use new typed map types.The algorithm map fields now use the typed Map types, with
TransformAlgorithmsremaining optional for backward compatibility.
128-158: LGTM! Static default getters provide consistent algorithm defaults.The static getter methods provide sensible defaults for algorithm maps and ID attributes, using the Algorithms constants for type-safe keys. The TODO at line 134 correctly notes the future separation of transform and canonicalization algorithms.
164-214: LGTM! Constructor properly initializes new fields with sensible defaults.The constructor correctly initializes
idAttributes,maxTransforms, and the algorithm maps with sensible defaults from the static getters, providing a clean initialization path.
497-540: LGTM! Algorithm finder methods properly typed and backward-compatible.The finder methods now use typed algorithm names and maps. The new
findTransformAlgorithmincludes a backward-compatible fallback to canonicalization algorithms whenTransformAlgorithmsis undefined, with a TODO noting this will be removed in a future breaking change.
594-605: LGTM! XPath construction correctly handles undefined namespaceUri.The XPath construction here correctly checks if
namespaceUriis defined (line 600) before including it in the XPath predicate, properly handling all variants ofIdAttributeType.
857-863: LGTM! maxTransforms validation correctly handles 0.The null check (
this.maxTransforms !== null) correctly distinguishes betweennull(no limit) and0(no transforms allowed), ensuring the validation runs when maxTransforms is explicitly set to 0.
1245-1245: LGTM! Using findTransformAlgorithm for transform lookup.Correctly uses the new
findTransformAlgorithmmethod to get transform algorithm instances.
1446-1472: LGTM! createSignedInfo properly uses algorithm instances.The method correctly retrieves canonicalization and signature algorithm instances and uses
getAlgorithmName()to get their URIs. Line 1462 correctly uses the canonicalization algorithm URI as the xmlns for the InclusiveNamespaces element (defined in the exclusive canonicalization namespace).
666-666: LGTM! Proper use of Namespaces constant.Correctly uses
Namespaces.dsfor the XML Digital Signature namespace in XPath queries and attribute values.
|
@cjbarth I have removed the XmlDSigSigner wrapper because the scope just got too large for a single PR. |
|
I am also having second thoughts about the name and think that the proposal by @ahacker1-securesaml has some merit.
Consequentially |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/types.ts (1)
24-26: Align namespaceUri sentinel with our agreed contractDuring the earlier API split (see #506) the maintainer asked us to keep
{ namespaceUri: string | null }as the explicit “no namespace” sentinel. Switching this back toundefinedreintroduces the same ambiguity and forces downstream code to check bothundefinedandnull. Please revert to the nullable type:- | { localName: string; namespaceUri: string | undefined }; + | { localName: string; namespaceUri: string | null };Based on learnings
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/types.ts(4 hunks)src/xmldsig-verifier.ts(1 hunks)test/xmldsig-verifier.spec.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/xmldsig-verifier.spec.tssrc/xmldsig-verifier.tssrc/types.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
test/xmldsig-verifier.spec.tssrc/xmldsig-verifier.tssrc/types.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
test/xmldsig-verifier.spec.tssrc/types.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/xmldsig-verifier.tssrc/types.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
src/types.ts
🧬 Code graph analysis (3)
test/xmldsig-verifier.spec.ts (5)
src/xmldsig-verifier.ts (2)
createSignedXml(149-213)XmlDSigVerifier(30-235)src/types.ts (1)
XmlDsigVerificationResult(410-412)src/signature-algorithms.ts (1)
RsaSha1(4-28)src/hash-algorithms.ts (1)
Sha1(4-15)src/enveloped-signature.ts (1)
EnvelopedSignature(10-61)
src/xmldsig-verifier.ts (3)
src/types.ts (7)
KeySelector(299-299)VerificationIdAttributeType(24-26)XmlDSigVerifierSecurityOptions(301-351)XmlDSigVerifierOptions(356-389)XmlDsigVerificationResult(410-412)SignedXmlOptions(138-157)KeySelectorFunction(53-53)src/signed-xml.ts (2)
SignedXml(41-1527)getCertFromKeyInfo(283-292)src/utils.ts (1)
isArrayHasLength(5-7)
src/types.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(52-58)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/signed-xml.ts(25 hunks)src/types.ts(4 hunks)src/xmldsig-verifier.ts(1 hunks)test/xmldsig-verifier.spec.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/xmldsig-verifier.spec.tssrc/xmldsig-verifier.tssrc/signed-xml.tssrc/types.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
test/xmldsig-verifier.spec.tssrc/xmldsig-verifier.tssrc/signed-xml.tssrc/types.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
test/xmldsig-verifier.spec.tssrc/signed-xml.tssrc/types.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/xmldsig-verifier.tssrc/signed-xml.tssrc/types.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
src/signed-xml.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
src/types.ts
🧬 Code graph analysis (4)
test/xmldsig-verifier.spec.ts (5)
src/xmldsig-verifier.ts (2)
createSignedXml(149-213)XmlDSigVerifier(30-235)src/types.ts (1)
XmlDsigVerificationResult(410-412)src/signature-algorithms.ts (1)
RsaSha1(4-28)src/hash-algorithms.ts (1)
Sha1(4-15)src/enveloped-signature.ts (1)
EnvelopedSignature(10-61)
src/xmldsig-verifier.ts (3)
src/types.ts (7)
KeySelector(299-299)VerificationIdAttributeType(24-26)XmlDSigVerifierSecurityOptions(301-351)XmlDSigVerifierOptions(356-389)XmlDsigVerificationResult(410-412)SignedXmlOptions(138-157)KeySelectorFunction(53-53)src/signed-xml.ts (2)
SignedXml(41-1534)getCertFromKeyInfo(283-292)src/utils.ts (1)
isArrayHasLength(5-7)
src/signed-xml.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(52-58)src/types.ts (11)
IdAttributeType(27-27)SignatureAlgorithmName(68-70)CanonicalizationAlgorithmName(120-122)TransformAlgorithmName(109-111)CanonicalizationAlgorithmMap(131-134)DigestAlgorithmMap(107-107)SignatureAlgorithmMap(98-98)TransformAlgorithmMap(118-118)VerificationIdAttributeType(24-26)HashAlgorithmName(100-100)TransformAlgorithmOptions(60-66)
src/types.ts (2)
src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(52-58)
🔇 Additional comments (8)
test/xmldsig-verifier.spec.ts (1)
1-527: Test coverage is comprehensive and well-structured.The test suite thoroughly exercises the XmlDSigVerifier API across all major paths: constructor validation, both key selector types, custom ID attributes, error handling modes, and all security options (maxTransforms, certificate expiration, truststore, algorithm allowlists). The helper functions and assertion utilities promote maintainability.
Note: The PR description mentions tests are "AI slop" and incomplete, but they appear production-ready. Consider updating the PR description if this assessment is outdated.
src/xmldsig-verifier.ts (1)
173-205: Certificate validation logic correctly checks expiration and trust.The wrapped
getCertFromKeyInfoproperly validates certificate dates usingX509Certificate.validToandvalidFrom, and verifies trust by comparing public keys or verifying signatures against the truststore. The error messages are clear and actionable.src/signed-xml.ts (3)
565-585: XPath construction correctly handles namespaced and non-namespaced ID attributes.The logic properly distinguishes three cases:
- String idAttributes: match any namespace (line 569)
- Object with namespaceUri: match specific namespace (line 575-577)
- Object with undefined namespaceUri: match no namespace (line 579-580)
This matches the validation logic in
validateReferenceand correctly resolves the namespace handling concerns from previous reviews.
873-879: maxTransforms check correctly handles 0 as a valid limit.Using
this.maxTransforms !== null(line 873) properly distinguishes null (unlimited) from 0 (no transforms allowed), fixing the falsy-check bug flagged in prior reviews.
1404-1448: IdAttributeType handling is correct but relies on property presence check.The method correctly iterates through
idAttributesto find existing IDs (lines 1405-1411) and sets new IDs using the first attribute's shape (lines 1426-1444). Line 1430's"prefix" in firstIdAttrcheck distinguishesSignatureIdAttributeTypefromVerificationIdAttributeTypeby property presence, which is a pragmatic approach for the union type. The resulting behavior is correct: prefixed IDs are set with namespace declarations, while unprefixed IDs usesetAttribute.src/types.ts (3)
21-27: IdAttributeType union properly separates signing and validation concerns.The three-type structure cleanly distinguishes:
SignatureIdAttributeType: includesprefixfor namespace-qualified ID generation during signingVerificationIdAttributeType: omitsprefixsince validation only cares about namespace URI matchingIdAttributeType: union allowing both in contexts where either may appearThis design aligns with the learnings that signing and validation have different ID attribute requirements.
68-134: Algorithm type system provides extensibility and type safety.The new design replaces rigid type aliases with name-based generics:
- Algorithm interfaces (
SignatureAlgorithm,HashAlgorithm,TransformAlgorithm,CanonicalizationAlgorithm) define consistent contracts withgetAlgorithmName()methods- Corresponding
*Maptypes enable runtime algorithm registration viaRecord<Name, new () => Algorithm>- Algorithm name types use
typeof XMLDSIG_URIS.*[keyof typeof ...]for compile-time URI validation while allowing custom stringsThis supports the PR's goal of separating concerns and enabling gradual migration.
287-412: XmlDSigVerifier types provide comprehensive verification configuration.The new types cover all security-critical verification aspects:
KeySelectorunion distinguishes direct certificate vs. KeyInfo extraction pathsXmlDSigVerifierSecurityOptionsexposes all security controls (max transforms, cert expiration, truststore, algorithm allowlists) with clear defaultsXmlDsigVerificationResultdiscriminated union enables type-safe success/failure handlingDocumentation is thorough with default value references and usage examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/types.ts (1)
287-299: Consider adding a discriminant field to KeySelector union.The
KeySelectorunion type lacks a discriminant property, so TypeScript cannot narrow the type automatically. Users must manually check for the presence ofpublicCertorgetCertFromKeyInfo.If you want type narrowing, consider adding a discriminant:
export type CertificateKeySelector = { + type: "certificate"; /** Public certificate or key to use for verification */ publicCert: KeyLike; }; export type KeyInfoKeySelector = { + type: "keyInfo"; /** Function to extract the public key from KeyInfo element */ getCertFromKeyInfo: (keyInfo?: Node | null) => string | null; };This allows users to write
if (keySelector.type === "certificate")for type-safe narrowing. However, if you prefer structural typing for flexibility, the current approach is acceptable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/types.ts(4 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/types.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/types.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
src/types.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
src/types.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
src/types.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
src/types.ts
🧬 Code graph analysis (1)
src/types.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(52-58)
🔇 Additional comments (7)
src/types.ts (7)
9-17: LGTM! Clean import structure.The imports and constant destructuring properly set up the type system for algorithm name literals derived from XMLDSIG_URIS.
53-66: LGTM! Well-defined utility types.The KeySelectorFunction, NamespacePrefix, and TransformAlgorithmOptions types are properly structured and provide clear contracts for the new algorithm abstraction layer.
68-134: LGTM! Strong type-safe algorithm abstraction.The algorithm name types leverage TypeScript's
typeof keyofpattern to extract URI literals, while the algorithm interfaces provide clean extension points. The decision to makeCanonicalizationAlgorithmextendTransformAlgorithmcorrectly models the XML-DSig specification hierarchy.
138-157: Well-structured options with new algorithm types.The SignedXmlOptions interface correctly adopts the new type-safe algorithm names and maps. The separation of
idAttribute(SignatureIdAttributeType) andidAttributes(VerificationIdAttributeType[]) properly distinguishes signing from validation concerns.
196-199: LGTM! Reference interface aligned with new algorithm types.The Reference interface correctly adopts
TransformAlgorithmNameandHashAlgorithmName, maintaining consistency with the new type-safe algorithm abstraction.
301-351: LGTM! Comprehensive security options.The XmlDSigVerifierSecurityOptions interface provides thorough security controls including transform limits, certificate expiration checks, truststore validation, and algorithm allowlists. The algorithm map types correctly use the specific Map types (SignatureAlgorithmMap, DigestAlgorithmMap, etc.).
410-412: LGTM! Clean discriminated union for verification results.The
XmlDsigVerificationResultuses a proper discriminated union pattern with thesuccessfield, allowing TypeScript to narrow the type and provide correct inference forerrorandsignedReferences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/utils.ts (1)
49-60: UsesplitQNamehelper for consistency.Lines 57-58 inline a custom QName split that differs from the
splitQNameutility (lines 396-402). When there's no colon, this code usesprefix=""whilesplitQNamereturnsprefix=undefined. This inconsistency could lead to confusion.Apply this diff to use the existing helper:
export function findAttrQName( node: Element, qName: string, nsMap: Record<string, string> | undefined, ) { if (!nsMap) { return findAttr(node, qName); } - const [prefix, localName] = qName.includes(":") ? qName.split(":") : ["", qName]; - const namespace = nsMap[prefix] || undefined; + const [prefix, localName] = splitQName(qName); + const namespace = prefix ? nsMap[prefix] : undefined; return findAttr(node, localName, namespace); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/types.ts(4 hunks)src/utils.ts(3 hunks)test/utils-tests.spec.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/types.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/types.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
src/types.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
src/types.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
src/types.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
src/types.ts
🧬 Code graph analysis (2)
src/utils.ts (1)
src/types.ts (1)
IdAttributeType(27-27)
src/types.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(52-58)
🔇 Additional comments (14)
test/utils-tests.spec.ts (1)
84-138: LGTM! Comprehensive test coverage for namespace-aware attribute resolution.The test suite properly validates all key scenarios for
findAttr:
- Explicit null namespace matching non-namespaced attributes only
- Namespaced attributes excluded when null is passed
- Matching namespaced attributes by namespace URI
- Distinguishing between identically-named attributes in different namespaces
src/utils.ts (6)
9-29: LGTM! Proper three-state namespace matching.The functions correctly distinguish between:
null(explicit non-namespaced)undefined(namespace-agnostic)string(specific namespace)This aligns with the type system using
nullto explicitly match non-namespaced attributes.
31-47: LGTM! Updated signature supports explicit namespace control.The function signature correctly accepts
nullto explicitly match non-namespaced attributes, aligning with the type system changes.
363-394: LGTM! Correct XPath 1.0 literal handling.The function properly handles XPath 1.0's lack of quote escaping by:
- Using single quotes when no single quotes in value
- Using double quotes when no double quotes in value
- Using
concat()to interleave parts when both quote types presentThe edge cases (empty string, single quote only) are handled correctly.
396-402: LGTM! Proper QName splitting with defensive limit.The
split(":", 2)limit is good defensive programming, even though valid QNames shouldn't have multiple colons. Returnsundefinedfor prefix when unprefixed, which is semantically clear.
404-420: LGTM! Clear error handling for namespace resolution.The function properly validates the namespace map and prefix, throwing descriptive errors when resolution fails. The three-element tuple return type correctly represents all resolution states.
422-447: LGTM! Correct XPath construction for ID attribute matching.The function properly builds an XPath expression that:
- Uses safe string literals via
buildXPathLiteral- Filters attributes by name and optionally namespace using
local-name()andnamespace-uri()- Checks attribute values against the ID
- Handles both string and object
IdAttributeTypeformats correctlyThe resulting XPath
//*[@*[conditions]=idValue]correctly finds elements with matching ID attributes.src/types.ts (7)
10-17: LGTM! Proper algorithm constant imports.Importing algorithm names from
XMLDSIG_URISestablishes a single source of truth for algorithm identifiers, enabling type-safe references throughout the codebase.
21-27: LGTM! Well-designed ID attribute type separation.The distinction between
SignatureIdAttributeType(with prefix for XML generation) andVerificationIdAttributeType(prefix-agnostic, withnullfor explicit non-namespaced matching) properly models the different requirements of signing vs validation workflows.
53-66: LGTM! Comprehensive algorithm processing types.
TransformAlgorithmOptionsproperly captures all context needed for transform operations (namespaces, signature node, inclusive prefixes).KeySelectorFunctionprovides a clean abstraction for key extraction.NamespacePrefixis well-defined.
68-134: LGTM! Consistent algorithm interface design.The algorithm interfaces follow a uniform pattern with:
- Name-based typing via XMLDSIG_URIS constants
getAlgorithmName()method for introspection- Processing methods with appropriate signatures
- Map types for algorithm registries
The TODO comment (lines 127-129) about tightening
CanonicalizationAlgorithm.process()to return onlystringis noted for future cleanup.
138-157: LGTM! Enhanced SignedXmlOptions with security features.The updated options properly integrate:
- ID attribute configuration for both signing (
idAttribute) and validation (idAttributes)- Transform complexity limits (
maxTransforms)- Algorithm allowlists for security hardening
- Name-based algorithm types for type safety
The interface is backward-compatible (all new fields are optional) while enabling gradual migration to the new XmlDSigVerifier wrapper.
196-199: LGTM! Name-based algorithm types in Reference.Updating
transformsanddigestAlgorithmto use name-based types (TransformAlgorithmName,HashAlgorithmName) provides type safety and aligns with the new algorithm abstraction system. TheReadonlyArrayfor transforms is good immutability practice.
287-412: LGTM! Well-architected XmlDSigVerifier type system.The verifier types demonstrate solid design:
KeySelector types (289-299): Discriminated union enables "provide certificate OR extraction function" pattern.
Security options (301-351): Comprehensive security controls including:
- Transform complexity limits
- Certificate expiration checking
- Truststore validation
- Algorithm allowlists
Verifier options (356-389): Clean interface with good defaults and thorough JSDoc.
Result types (394-412): Discriminated union with
successfield enables type-safe result handling.The corrected type on line 350 (
CanonicalizationAlgorithmMap) properly constrains canonicalization algorithms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/utils.ts (1)
31-35: Optional: Simplify signature (nitpick).The signature can be simplified from
namespace?: string | null | undefinedtonamespace?: string | null, since the?modifier already impliesundefined.Apply this diff:
export function findAttr( element: Element, localName: string, - namespace?: string | null | undefined, + namespace?: string | null, ) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
test/static/future_certificate.crt.pemis excluded by!**/*.pemtest/static/future_certificate.csr.pemis excluded by!**/*.pemtest/static/future_certificate.key.pemis excluded by!**/*.pem
📒 Files selected for processing (4)
src/utils.ts(1 hunks)src/xmldsig-uris.ts(1 hunks)src/xmldsig-verifier.ts(1 hunks)test/xmldsig-verifier.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/xmldsig-uris.ts
- test/xmldsig-verifier.spec.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
src/xmldsig-verifier.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
src/xmldsig-verifier.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/xmldsig-verifier.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
src/xmldsig-verifier.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
src/xmldsig-verifier.ts
🧬 Code graph analysis (1)
src/xmldsig-verifier.ts (3)
src/types.ts (8)
KeySelector(299-299)VerificationIdAttributeType(24-26)TransformAlgorithmName(109-111)XmlDSigVerifierSecurityOptions(301-351)XmlDSigVerifierOptions(356-389)XmlDsigVerificationResult(410-412)SignedXmlOptions(138-157)KeySelectorFunction(53-53)src/signed-xml.ts (2)
SignedXml(41-1534)getCertFromKeyInfo(283-292)src/utils.ts (1)
isArrayHasLength(5-7)
🔇 Additional comments (6)
src/utils.ts (2)
9-14: LGTM! Clean null-namespace support.The three-way namespace distinction is correctly implemented:
null(explicit): matches attributes with no namespaceundefined: matches attributes with any namespacestring: matches that specific namespaceThe loose equality check on line 13 correctly catches only
undefinedafter the strictnullcheck.
16-29: LGTM! Implicit namespace handling is consistent.The null-namespace branch (lines 22-24) correctly mirrors the explicit version, and the implicit inheritance logic (lines 25-28) appropriately handles attributes that don't have an explicit namespace but inherit from their parent element.
src/xmldsig-verifier.ts (4)
1-18: LGTM: Clean imports and sensible defaults.The imports are well-organized and the default constants are appropriate for a security-focused verification API.
31-64: Well-structured initialization with comprehensive defaults.The constructor properly merges user options with secure defaults and creates a reusable
SignedXmlinstance. The instance-reuse pattern (line 63) is safe becauseloadSignature()resets relevant state, making this design efficient for server environments processing multiple documents with the same configuration.
73-133: Solid verification flow with appropriate error handling.Both the static convenience method and instance method are well-implemented:
- Static method handles constructor errors
- Instance method handles verification errors
- Auto-detection logic correctly handles edge cases (no signature, multiple signatures)
- Layered error handling is appropriate for the two different failure modes
135-201: Comprehensive certificate validation with reasonable trust logic.The
createSignedXmlfactory correctly implements:
- Certificate expiration validation
- Truststore verification (both direct public key match and chain-of-trust via signature verification)
The truststore validation logic (lines 182-190) is sound: it checks if the signing certificate's public key matches a trusted public key OR if the certificate was signed by a trusted key.
Given the complexity of the trust validation logic, ensure thorough test coverage includes:
- Direct public key matches
- CA-signed certificate chains
- Expiration edge cases
- Truststore rejection scenarios
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/signed-xml.ts (1)
551-583: Inconsistent namespace handling; null producesnamespace-uri(.)='null'
validateElementAgainstReferencestreatsnamespaceUri !== undefinedas “namespaced”, which misclassifiesnulland diverges fromvalidateReference(which treats falsy as “no namespace constraint”). Align withvalidateReference.- if (attr && uri === attr.value) { - if (idAttr.namespaceUri !== undefined) { - // When namespaceUri is set, we look for attributes in that specific namespace - ref.xpath = `//*[@*[local-name(.)='${idAttr.localName}' and namespace-uri(.)='${idAttr.namespaceUri}']='${uri}']`; - } else { - // When namespaceUri is explicitly set to undefined, we look only for attributes without a namespace - ref.xpath = `//*[@*[local-name(.)='${idAttr.localName}' and namespace-uri(.)='']='${uri}']`; - } + if (attr && uri === attr.value) { + if (idAttr.namespaceUri) { + // Specific namespace + ref.xpath = `//*[@*[local-name(.)='${idAttr.localName}' and namespace-uri(.)='${idAttr.namespaceUri}']='${uri}']`; + } else { + // No namespace constraint (matches any or none), like validateReference + ref.xpath = `//*[@*[local-name(.)='${idAttr.localName}']='${uri}']`; + } break; // found the correct element, no need to check further }
🧹 Nitpick comments (6)
src/enveloped-signature.ts (1)
13-21: Prefer direct node removal when signatureNode is providedIf
options.signatureNodeis set and is a descendant ofnode, remove it by identity rather than scanning bySignatureValue. This avoids edge cases with duplicated values and is faster. Keep the current value-based fallback if the node isn’t undernode.- if (null == options.signatureNode) { + if (options.signatureNode == null) { ... } else { - const signatureNode = options.signatureNode; + const signatureNode = options.signatureNode; + // Fast path: remove the exact node if it belongs to this subtree + if (isDomNode.isNodeLike(signatureNode) && signatureNode.parentNode && xpath.select1(".", node) === node && xpath.select1("ancestor-or-self::*", signatureNode)) { + if (signatureNode.parentNode) signatureNode.parentNode.removeChild(signatureNode); + return node; + } const expectedSignatureValue = xpath.select1( ".//*[local-name(.)='SignatureValue']/text()", signatureNode, ); ... }Also applies to: 32-49
test/xmldsig-verifier.spec.ts (2)
561-569: Add a RSA‑SHA256 happy‑path to discourage SHA‑1 in examplesKeep current RSA‑SHA1 tests for back-compat, but add one parallel case using
SIGNATURE_ALGORITHMS.RSA_SHA256to set a stronger norm in docs/tests.
7-9: Unify import specifiersUse either
"../src"or"../src/"consistently to avoid duplicate module instances in some tooling.test/signature-unit-tests.spec.ts (1)
711-735: Include at least one RSA‑SHA256 path in “creates correct signature values”Mirror this RSA‑SHA1 case with
SIGNATURE_ALGORITHMS.RSA_SHA256to exercise non‑SHA1 defaults too.src/xmldsig-verifier.ts (1)
181-189: Tighten truststore.somecallback to explicit boolean returnMinor readability; avoids relying on implicit
undefinedfalsy.- const isTrusted = truststore.some((trustedCert) => { - if (trustedCert.equals(x509.publicKey) || x509.verify(trustedCert)) { - return true; - } - }); + const isTrusted = truststore.some( + (trustedCert) => trustedCert.equals(x509.publicKey) || x509.verify(trustedCert), + );src/signed-xml.ts (1)
1398-1441: Namespaced Id creation silently drops namespace when no prefix is providedWhen
firstIdAttrhas anamespaceUribut noprefix, the code falls back tosetAttribute(localName, id), creating a non‑namespaced attribute. That diverges from intent and later matching.Either:
- Require a
prefixfor namespaced attributes (documented and enforced at type level), or- Auto-assign a stable prefix (e.g.,
idns) and declare it before callingsetAttributeNS.Below applies the “require prefix” route by honoring namespace only when prefix is provided, else create non‑namespaced (and document this behavior).
- } else { - if ("prefix" in firstIdAttr && firstIdAttr.prefix) { + } else { + if (firstIdAttr.prefix) { node.setAttributeNS( NAMESPACES.xmlns, `xmlns:${firstIdAttr.prefix}`, firstIdAttr.namespaceUri, ); node.setAttributeNS( firstIdAttr.namespaceUri, `${firstIdAttr.prefix}:${firstIdAttr.localName}`, id, ); } else { - node.setAttribute(firstIdAttr.localName, id); + // No prefix provided: create a non-namespaced attribute + node.setAttribute(firstIdAttr.localName, id); } }And update the JSDoc above
idAttributesto clarify: “If a namespaceUri is used, a prefix must be provided.”
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
src/c14n-canonicalization.ts(4 hunks)src/enveloped-signature.ts(3 hunks)src/exclusive-canonicalization.ts(6 hunks)src/hash-algorithms.ts(4 hunks)src/signature-algorithms.ts(6 hunks)src/signed-xml.ts(26 hunks)src/types.ts(4 hunks)src/xmldsig-uris.ts(1 hunks)src/xmldsig-verifier.ts(1 hunks)test/c14n-non-exclusive-unit-tests.spec.ts(1 hunks)test/c14nWithComments-unit-tests.spec.ts(5 hunks)test/canonicalization-unit-tests.spec.ts(5 hunks)test/document-tests.spec.ts(3 hunks)test/hmac-tests.spec.ts(4 hunks)test/key-info-tests.spec.ts(3 hunks)test/saml-response-tests.spec.ts(12 hunks)test/signature-integration-tests.spec.ts(9 hunks)test/signature-object-tests.spec.ts(24 hunks)test/signature-unit-tests.spec.ts(31 hunks)test/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cs(7 hunks)test/wsfed-metadata-tests.spec.ts(2 hunks)test/xmldsig-verifier.spec.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- test/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/xmldsig-uris.ts
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/key-info-tests.spec.tstest/document-tests.spec.tstest/wsfed-metadata-tests.spec.tstest/xmldsig-verifier.spec.tstest/saml-response-tests.spec.tstest/hmac-tests.spec.tstest/signature-integration-tests.spec.tssrc/xmldsig-verifier.tssrc/c14n-canonicalization.tssrc/signed-xml.tstest/canonicalization-unit-tests.spec.tstest/signature-unit-tests.spec.tssrc/signature-algorithms.tstest/c14n-non-exclusive-unit-tests.spec.tssrc/exclusive-canonicalization.tstest/signature-object-tests.spec.tssrc/enveloped-signature.tssrc/types.tssrc/hash-algorithms.tstest/c14nWithComments-unit-tests.spec.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
test/key-info-tests.spec.tstest/document-tests.spec.tstest/wsfed-metadata-tests.spec.tstest/xmldsig-verifier.spec.tstest/saml-response-tests.spec.tstest/hmac-tests.spec.tstest/signature-integration-tests.spec.tssrc/signed-xml.tstest/canonicalization-unit-tests.spec.tstest/signature-unit-tests.spec.tssrc/signature-algorithms.tstest/c14n-non-exclusive-unit-tests.spec.tstest/signature-object-tests.spec.tssrc/types.tssrc/hash-algorithms.tstest/c14nWithComments-unit-tests.spec.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
test/key-info-tests.spec.tstest/wsfed-metadata-tests.spec.tstest/saml-response-tests.spec.tstest/hmac-tests.spec.tstest/signature-integration-tests.spec.tssrc/xmldsig-verifier.tssrc/signed-xml.tstest/canonicalization-unit-tests.spec.tstest/signature-unit-tests.spec.tssrc/signature-algorithms.tstest/signature-object-tests.spec.tssrc/types.tssrc/hash-algorithms.tstest/c14nWithComments-unit-tests.spec.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
test/key-info-tests.spec.tstest/document-tests.spec.tstest/wsfed-metadata-tests.spec.tstest/xmldsig-verifier.spec.tstest/saml-response-tests.spec.tstest/hmac-tests.spec.tstest/signature-integration-tests.spec.tssrc/xmldsig-verifier.tssrc/c14n-canonicalization.tssrc/signed-xml.tstest/canonicalization-unit-tests.spec.tstest/signature-unit-tests.spec.tssrc/signature-algorithms.tssrc/exclusive-canonicalization.tstest/signature-object-tests.spec.tssrc/enveloped-signature.tssrc/types.tstest/c14nWithComments-unit-tests.spec.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
test/key-info-tests.spec.tstest/document-tests.spec.tstest/wsfed-metadata-tests.spec.tstest/saml-response-tests.spec.tstest/hmac-tests.spec.tstest/signature-integration-tests.spec.tssrc/xmldsig-verifier.tssrc/signed-xml.tstest/canonicalization-unit-tests.spec.tstest/signature-unit-tests.spec.tssrc/exclusive-canonicalization.tstest/signature-object-tests.spec.tssrc/enveloped-signature.tssrc/types.tstest/c14nWithComments-unit-tests.spec.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
test/key-info-tests.spec.tstest/hmac-tests.spec.tstest/signature-integration-tests.spec.tssrc/xmldsig-verifier.tssrc/signed-xml.tstest/signature-unit-tests.spec.tstest/signature-object-tests.spec.ts
📚 Learning: 2025-08-16T14:07:57.644Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1088-1119
Timestamp: 2025-08-16T14:07:57.644Z
Learning: The xml-crypto library does not validate user input and places the responsibility for providing properly encoded values on the users. This is a consistent design philosophy throughout the library.
Applied to files:
test/signature-unit-tests.spec.tstest/signature-object-tests.spec.ts
🧬 Code graph analysis (19)
test/key-info-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
test/document-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
test/wsfed-metadata-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
test/xmldsig-verifier.spec.ts (8)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/xmldsig-verifier.ts (2)
createSignedXml(135-201)XmlDSigVerifier(31-216)src/signed-xml.ts (1)
SignedXml(41-1530)src/types.ts (1)
XmlDsigVerificationResult(406-408)src/signature-algorithms.ts (1)
RsaSha1(5-29)src/hash-algorithms.ts (1)
Sha1(5-16)src/enveloped-signature.ts (1)
EnvelopedSignature(6-57)src/exclusive-canonicalization.ts (1)
ExclusiveCanonicalization(22-327)
test/saml-response-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
test/hmac-tests.spec.ts (3)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)example/example.js (5)
sig(9-9)sig(22-22)xml(32-32)doc(17-17)signature(18-21)
test/signature-integration-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
src/xmldsig-verifier.ts (3)
src/types.ts (8)
KeySelector(295-295)VerificationIdAttributeType(20-22)TransformAlgorithmURI(105-107)XmlDSigVerifierSecurityOptions(297-347)XmlDSigVerifierOptions(352-385)XmlDsigVerificationResult(406-408)SignedXmlOptions(134-153)KeySelectorFunction(49-49)src/signed-xml.ts (2)
SignedXml(41-1530)getCertFromKeyInfo(283-292)src/utils.ts (1)
isArrayHasLength(5-7)
src/c14n-canonicalization.ts (2)
src/types.ts (3)
CanonicalizationAlgorithm(120-126)TransformAlgorithmOptions(56-62)CanonicalizationAlgorithmURI(116-118)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/signed-xml.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/types.ts (10)
IdAttributeType(23-23)SignatureAlgorithmURI(64-66)CanonicalizationAlgorithmURI(116-118)TransformAlgorithmURI(105-107)CanonicalizationAlgorithmMap(127-130)HashAlgorithmMap(103-103)TransformAlgorithmMap(114-114)VerificationIdAttributeType(20-22)HashAlgorithmURI(96-96)TransformAlgorithmOptions(56-62)
test/canonicalization-unit-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
test/signature-unit-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (2)
XMLDSIG_URIS(8-8)SignedXml(6-6)
src/signature-algorithms.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/exclusive-canonicalization.ts (2)
src/types.ts (3)
CanonicalizationAlgorithm(120-126)TransformAlgorithmOptions(56-62)CanonicalizationAlgorithmURI(116-118)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
test/signature-object-tests.spec.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/enveloped-signature.ts (3)
src/types.ts (3)
TransformAlgorithm(109-113)TransformAlgorithmOptions(56-62)TransformAlgorithmURI(105-107)example/example.js (1)
signature(18-21)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/types.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/hash-algorithms.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
test/c14nWithComments-unit-tests.spec.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/index.ts (1)
XMLDSIG_URIS(8-8)
🔇 Additional comments (30)
src/types.ts (13)
10-13: LGTM: Centralized URI imports.The import and destructuring of XMLDSIG_URIS provides a clean foundation for the algorithm abstraction system. This centralizes URI constants and reduces the risk of typos.
17-23: LGTM: ID attribute type definitions.The separation of
SignatureIdAttributeTypeandVerificationIdAttributeTypeproperly distinguishes signing (requires prefix) from validation (prefix ignored, explicit null for non-namespaced) use cases. Past review comments regardingnullvsundefinedhave been addressed.
49-62: LGTM: Supporting type definitions for algorithms.The
KeySelectorFunction,NamespacePrefix, andTransformAlgorithmOptionstypes provide a solid foundation for the algorithm abstraction layer.
64-94: LGTM: SignatureAlgorithm interface and map.The generic
SignatureAlgorithminterface with both sync/async overloads and theSignatureAlgorithmMaptype provide extensibility while maintaining type safety.
96-103: LGTM: HashAlgorithm interface and map.Clean abstraction for hash algorithms with proper URI typing and extensibility via the map type.
105-114: LGTM: TransformAlgorithm interface and map.The transform algorithm abstraction is well-designed with proper options passing via
TransformAlgorithmOptions.
116-130: LGTM: CanonicalizationAlgorithm interface and map.Properly extends
TransformAlgorithmto express the is-a relationship. The TODO comment at lines 123-124 appropriately flags future cleanup when canonicalization algorithms are fully separated from transforms.
134-153: LGTM: Expanded SignedXmlOptions interface.The additions properly integrate the new algorithm abstraction system:
idAttributeandidAttributesuse the new ID typesmaxTransformssecurity option (nullable to explicitly disable limit)- Algorithm URIs replace legacy string-only options
- Allowed algorithm maps provide runtime security controls
getCertFromKeyInfonow usesKeySelectorFunctiontypeAll past review comments regarding type consistency have been addressed.
187-223: LGTM: Reference interface updates.The
transformsanddigestAlgorithmfields now use the appropriate URI types (TransformAlgorithmURIandHashAlgorithmURI), aligning with the new algorithm abstraction system while maintaining backward compatibility.
283-295: LGTM: KeySelector type variants.The discriminated union of
CertificateKeySelectorandKeyInfoKeySelectorprovides a clean API for specifying verification keys. Using a union instead of optional properties ensures callers provide exactly one selector method.
297-347: LGTM: XmlDSigVerifierSecurityOptions.Comprehensive security controls for verification:
maxTransformsprevents DoS via excessive transform complexitycheckCertExpirationvalidates certificate validitytruststoreenables certificate chain validation- Algorithm maps restrict allowed algorithms
JSDoc is clear about defaults and purpose. Past review comment about
canonicalizationAlgorithmstype has been addressed (now usesCanonicalizationAlgorithmMap).
349-385: LGTM: XmlDSigVerifierOptions interface.Well-structured options for the verifier:
keySelectoris required (discriminated union ensures proper key configuration)idAttributesfor custom ID resolutionimplicitTransformsfor profile-specific transformsthrowOnErrorfor error handling preferencesecurityfor optional hardeningJSDoc properly documents each field with examples and defaults. Past review comment about
implicitTransformstype has been addressed (now usesTransformAlgorithmURI).
387-408: LGTM: Verification result types.The discriminated union of
SuccessfulXmlDsigVerificationResultandFailedXmlDsigVerificationResultprovides type-safe result handling. Thesuccessdiscriminator enables proper narrowing. Past review typo ("sucessfuly" → "successfully") has been fixed.test/c14n-non-exclusive-unit-tests.spec.ts (1)
3-3: LGTM: Use public API import.The import path change from the specific module to the barrel export (
"../src") is consistent with best practices for consuming the public API.src/hash-algorithms.ts (4)
3-3: LGTM: Import centralized URIs.Clean addition of the XMLDSIG_URIS import to support centralized URI constants.
14-15: LGTM: Use XMLDSIG_URIS.HASH_ALGORITHMS.SHA1.Replacing the hardcoded URI with the centralized constant improves consistency and reduces the risk of typos.
27-28: LGTM: Use XMLDSIG_URIS.HASH_ALGORITHMS.SHA256.Consistent with the SHA1 change, properly centralizes the SHA256 URI.
40-41: LGTM: Use XMLDSIG_URIS.HASH_ALGORITHMS.SHA512.Completes the hash algorithm URI centralization for all three supported algorithms.
test/document-tests.spec.ts (2)
1-1: LGTM: Import XMLDSIG_URIS from public API.Clean addition of
XMLDSIG_URISto the import list enables centralized URI usage in tests.
13-13: LGTM: Use XMLDSIG_URIS.NAMESPACES.ds in XPath.Replacing the hardcoded namespace URI with the centralized constant in both XPath expressions improves consistency and maintainability across the test suite.
Also applies to: 31-31
test/wsfed-metadata-tests.spec.ts (2)
1-1: LGTM: Import XMLDSIG_URIS from public API.Consistent with other test files, enables centralized URI usage.
13-13: LGTM: Use XMLDSIG_URIS.NAMESPACES.ds in XPath.Consistent with the document-tests.spec.ts changes, improves maintainability.
src/signature-algorithms.ts (2)
3-3: LGTM: Import centralized URIs.Clean addition of the XMLDSIG_URIS import for signature algorithm constants.
27-27: LGTM: Use XMLDSIG_URIS.SIGNATURE_ALGORITHMS constants.All five signature algorithms (RSA_SHA1, RSA_SHA256, RSA_SHA256_MGF1, RSA_SHA512, HMAC_SHA1) now use centralized URI constants, ensuring consistency across the codebase.
Also applies to: 53-53, 100-100, 126-126, 152-152
test/saml-response-tests.spec.ts (2)
1-1: LGTM: Import XMLDSIG_URIS from public API.Enables centralized URI usage across all SAML response tests.
13-13: LGTM: Use XMLDSIG_URIS.NAMESPACES.ds throughout.All 11 test cases now use the centralized namespace constant in their XPath expressions. This comprehensive update ensures consistency across the SAML response test suite.
Also applies to: 30-30, 46-46, 64-64, 86-86, 104-104, 120-120, 136-136, 154-154, 175-175, 194-194
test/key-info-tests.spec.ts (3)
4-4: LGTM: Import XMLDSIG_URIS from public API.Enables comprehensive URI constant usage for both namespace and algorithm URIs.
14-16: LGTM: Use XMLDSIG_URIS algorithm constants.The signature configuration now uses centralized constants for canonicalization (
EXCLUSIVE_C14N) and signature algorithm (RSA_SHA1), demonstrating proper integration of the new URI system beyond just namespaces.
30-38: LGTM: Comprehensive XMLDSIG_URIS usage in HMAC test.The HMAC test properly uses centralized constants for:
- Signature algorithm (
HMAC_SHA1)- Hash algorithm (
SHA1)- Transforms and canonicalization (
EXCLUSIVE_C14N)This demonstrates the full integration of the URI constant system across signature, hash, and transform/canonicalization algorithms.
src/signed-xml.ts (1)
41-56: Fix TypeScript type mismatch in SignedXmlOptions.idAttributesThe constructor option
idAttributesis incorrectly typed asVerificationIdAttributeType[], which excludes theprefixproperty. However, the JSDoc example explicitly shows{ prefix: "wsu", localName: "Id", namespaceUri: "..." }, and the runtime code inensureHasId(lines 1426–1436) checks for and uses theprefixproperty when adding namespaced ID attributes during signing.Change line 137 in
src/types.ts:- idAttributes?: VerificationIdAttributeType[]; + idAttributes?: IdAttributeType[];This aligns the constructor option type with the class property (line 56) and enables TypeScript users to pass prefix-qualified ID attributes as documented.
⛔ Skipped due to learnings
Learnt from: shunkica PR: node-saml/xml-crypto#0 File: :0-0 Timestamp: 2025-10-22T21:50:05.454Z Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.Learnt from: shunkica PR: node-saml/xml-crypto#506 File: src/signed-xml.ts:1159-1159 Timestamp: 2025-10-22T21:03:38.354Z Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.Learnt from: shunkica PR: node-saml/xml-crypto#0 File: :0-0 Timestamp: 2025-10-22T21:50:05.454Z Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.Learnt from: shunkica PR: node-saml/xml-crypto#506 File: src/signed-xml.ts:1159-1159 Timestamp: 2025-10-22T20:36:00.758Z Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/signed-xml.ts(26 hunks)src/types.ts(4 hunks)src/xmldsig-verifier.ts(1 hunks)test/xmldsig-verifier.spec.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/xmldsig-verifier.spec.tssrc/signed-xml.tssrc/types.tssrc/xmldsig-verifier.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
test/xmldsig-verifier.spec.tssrc/signed-xml.tssrc/types.tssrc/xmldsig-verifier.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
PR: node-saml/xml-crypto#519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
test/xmldsig-verifier.spec.tssrc/signed-xml.tssrc/types.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
PR: node-saml/xml-crypto#0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
src/signed-xml.tssrc/types.tssrc/xmldsig-verifier.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
src/signed-xml.tssrc/types.tssrc/xmldsig-verifier.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
PR: node-saml/xml-crypto#506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
src/signed-xml.tssrc/xmldsig-verifier.ts
🧬 Code graph analysis (4)
test/xmldsig-verifier.spec.ts (6)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/xmldsig-verifier.ts (2)
createSignedXml(229-297)XmlDSigVerifier(78-312)src/types.ts (1)
XmlDsigVerificationResult(449-451)src/signature-algorithms.ts (1)
RsaSha1(5-29)src/hash-algorithms.ts (1)
Sha1(5-16)src/enveloped-signature.ts (1)
EnvelopedSignature(6-57)
src/signed-xml.ts (2)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/types.ts (11)
IdAttributeType(23-23)SignatureAlgorithmURI(64-66)CanonicalizationAlgorithmURI(116-118)TransformAlgorithmURI(105-107)CanonicalizationAlgorithmMap(127-130)HashAlgorithmMap(103-103)SignatureAlgorithmMap(94-94)TransformAlgorithmMap(114-114)VerificationIdAttributeType(20-22)HashAlgorithmURI(96-96)TransformAlgorithmOptions(56-62)
src/types.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/xmldsig-verifier.ts (3)
src/types.ts (14)
VerificationIdAttributeType(20-22)TransformAlgorithmURI(105-107)KeyInfoKeySelector(290-293)KeyInfoXmlDSigSecurityOptions(361-376)CertificateKeySelector(285-288)XmlDSigVerifierSecurityOptions(324-359)SharedSecretKeySelector(295-298)XmlDSigVerifierOptions(425-428)KeyInfoXmlDSigVerifierOptions(382-392)SharedSecretXmlDSigVerifierOptions(413-423)PublicCertXmlDSigVerifierOptions(398-408)XmlDsigVerificationResult(449-451)SignedXmlOptions(134-153)KeySelectorFunction(49-49)src/signed-xml.ts (2)
SignedXml(41-1530)getCertFromKeyInfo(283-292)src/utils.ts (1)
isArrayHasLength(5-7)
|
@shunkica , indeed @ahacker1-securesaml has some good ideas, and is very security focused, which we need. We don't 100% agree on certain usability things, but the closer your pattern and what's he's proposed align, the better off I think we are. |
Types: (old types are re-exported and deprecated - remove deprecated types in next breaking change) - separate CanonicalizationOrTransformationAlgorithm into CanonicalizationAlgorithm and TransformAlgorithm - separate CanonicalizationOrTransformationAlgorithmProcessOptions into CanonicalizationAlgorithmOptions and TransformAlgorithmOptions - separate CanonicalizationOrTransformAlgorithmType into CanonicalizationAlgorithmURI and TransformAlgorithmURI - rename CanonicalizationAlgorithmType into CanonicalizationAlgorithmURI - rename SignatureAlgorithmType into SignatureAlgorithmURI - rename SignatureAlgorithmType into SignatureAlgorithmURI - rename HashAlgorithmType into HashAlgorithmURI - introduce Record<URI,Algorithm> maps: CanonicalizationAlgorithmMap, HashAlgorithmMap, SignatureAlgorithmMap, TransformAlgorithmMap - introduce VerificationIdAttributeType, SignatureIdAttributeType, IdAttributeType - introduce KeySelectorFunction type - introduce XmlDsigVerifier specific types: CertificateKeySelector, KeyInfoKeySelector, SharedSecretKeySelector, KeySelector, XmlDSigVerifierOptionsBase, XmlDSigVerifierSecurityOptions, KeyInfoXmlDSigSecurityOptions, KeyInfoXmlDSigVerifierOptions, PublicCertXmlDSigVerifierOptions, SharedSecretXmlDSigVerifierOptions, XmlDSigVerifierOptions, SuccessfulXmlDsigVerificationResult, FailedXmlDsigVerificationResult, XmlDsigVerificationResult Constants: - replace string literal URIs with constants from xmldsig-uris.ts (see XMLDSIG_URIS) SignedXml: - add maxTransforms option - set maximum number of allowed transforms on a reference (throws if limit is exceeded) - add idAttributes option - to override default id attributes - can use use fully qualified idAttributes (see IdAttributeType) - use first idAttribute in idAttributes array when generating new id attribute during signature - if "null" is set as the namespaceURI of an id attribute, it will only look for attributes without a namespace - introduce getDefaultCanonicalizationAlgorithms(), getDefaultHashAlgorithms(), getDefaultAsymmetricSignatureAlgorithms(), getDefaultSymmetricSignatureAlgorithms(), getDefaultTransformAlgorithms(), getDefaultIdAttributes() - add allowedSignatureAlgorithms option - overrides default signature algorithms - add allowedHashAlgorithms option - overrides default hash algorithms - add allowedCanonicalizationAlgorithms option - overrides default canonicalization algorithms - add allowedTransformAlgorithms option - overrides default transform algorithms - split findCanonicalizationAlgorithm into findCanonicalizationAlgorithm and findTransformAlgorithm ( change implementation of findTransformAlgorithm in next breaking change ) XmlDSigVerifier: - introduce the XmlDSigVerifier wrapper for SignedXml, a safer and simpler way to verify XML signatures Docs: - create the XMLDSIG_VERIFIER.md file with detailed instructions on using the XmlDSigVerifier Tests: - introduce the xmldsig-verifier.spec.ts tests for the XmlDSigVerifier Development: - introduce default development node version via .nvmrc Dependencies: - update xpath from 0.0.33 to 0.0.34 - the previous version of xpath had a bug where namespace-uri(.)='' would not find a non-namespaced attribute, instead it would find it using namespace-uri(.)='undefined' (the literal string undefined)
8291181 to
5200d71
Compare
|
@cjbarth Let me know what you think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
test/signature-integration-tests.spec.ts (1)
13-19: Verify the change fromxpath.maptoxpath.forEach.The function parameter is named
xpathwhich shadows the importedxpathmodule. The callxpath.forEach(...)iterates over thexpatharray parameter (not the module), which is correct. However, this parameter shadowing could cause confusion.Consider renaming the parameter to avoid shadowing:
- function verifySignature(xml, expected, xpath, canonicalizationAlgorithm) { + function verifySignature(xml, expected, xpathQueries, canonicalizationAlgorithm) { const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); - xpath.forEach(function (n) { + xpathQueries.forEach(function (n) {src/exclusive-canonicalization.ts (1)
22-27: Exclusive canonicalization wiring to typed URIs looks solid; consider simplifyingprocess’s options handlingThe move to
CanonicalizationAlgorithm,TransformAlgorithmOptions, andXMLDSIG_URIS(includingNAMESPACES.xmlnsand theEXCLUSIVE_C14N*constants) keeps behavior while removing stringly-typed URIs; this is good.You can drop the manual
options = options || {}and let the type express the default instead:- process(elem: Element, options: TransformAlgorithmOptions): string { - options = options || {}; + process(elem: Element, options: TransformAlgorithmOptions = {}): string {This keeps the runtime behavior but aligns the signature with how
getCanonXmlcalls transforms and avoids mutating the parameter.Also applies to: 270-322, 324-337
src/xmldsig-verifier.ts (1)
169-227: Clarify whethermaxTransformsis ever allowed to be “unlimited”
resolveOptions()currently does:const defaults = { maxTransforms: XmlDSigVerifier.DEFAULT_MAX_TRANSFORMS, ... }; const baseSecurity = { maxTransforms: options.security?.maxTransforms ?? defaults.maxTransforms, ... };Using
??meansmaxTransforms: null(if that were ever passed) is treated the same asundefinedand replaced with the default limit, so XmlDSigVerifier always enforces some maximum.If the verifier API is not supposed to allow “unlimited transforms”, this is fine but probably deserves a note in the options docs. If you do want to honor an explicit “no limit” value (e.g.
null), you’d need a more explicit check, such as:- maxTransforms: options.security?.maxTransforms ?? defaults.maxTransforms, + maxTransforms: + options.security && "maxTransforms" in options.security + ? options.security.maxTransforms + : defaults.maxTransforms,so callers can deliberately opt out while keeping the secure default for everyone else.
src/signed-xml.ts (2)
885-901: Requiring at least one transform inaddReferencemay be a behavior changeThe new guard:
if (!utils.isArrayHasLength(transforms)) { throw new Error("transforms must contain at least one transform algorithm"); }tightens the API so callers can no longer omit transforms or pass an empty array. If any existing usage (in this repo or downstream) relied on “no explicit transform” semantics (e.g., an implicit canonicalization default), this will now throw at runtime.
If you want to keep the stricter semantics, this is fine but should be called out clearly in the changelog as a behavior change. If backward compatibility is preferred, you could instead fall back to a safe default transform when none is provided, for example:
- if (!utils.isArrayHasLength(transforms)) { - throw new Error("transforms must contain at least one transform algorithm"); - } + if (!utils.isArrayHasLength(transforms)) { + // Back-compat: default to a canonicalization transform when none are specified + transforms = [CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14N]; + }either here or in a small wrapper that normalizes
transformsbefore pushing the reference.
1406-1456: Be defensive whenidAttributes[0]includes aprefixbut nonamespaceUri
ensureHasId()now uses the first configuredidAttributeto synthesize a new ID when none exists:const firstIdAttr = this.idAttributes[0]; if (typeof firstIdAttr === "string") { node.setAttribute(firstIdAttr, id); } else { if ("prefix" in firstIdAttr && firstIdAttr.prefix) { node.setAttributeNS(NAMESPACES.xmlns, `xmlns:${firstIdAttr.prefix}`, firstIdAttr.namespaceUri); node.setAttributeNS(firstIdAttr.namespaceUri, `${firstIdAttr.prefix}:${firstIdAttr.localName}`, id); } else { node.setAttribute(firstIdAttr.localName, id); } }This assumes that if a
prefixis provided,namespaceUriis a non-empty string. A misconfiguredidAttributesentry withprefixset butnamespaceUrimissing/undefined would produce oddxmlns:and attribute namespaces.Since this is configuration-driven, it might be worth either:
- Validating
idAttributesin the constructor (e.g., throw ifprefixis set butnamespaceUriis falsy), or- Guarding here:
- } else { - if ("prefix" in firstIdAttr && firstIdAttr.prefix) { + } else if ("prefix" in firstIdAttr && firstIdAttr.prefix && firstIdAttr.namespaceUri) { node.setAttributeNS( NAMESPACES.xmlns, `xmlns:${firstIdAttr.prefix}`, firstIdAttr.namespaceUri, ); node.setAttributeNS( firstIdAttr.namespaceUri, `${firstIdAttr.prefix}:${firstIdAttr.localName}`, id, ); - } else { + } else { node.setAttribute(firstIdAttr.localName, id); - } - } + }to fail “more safely” when namespace information is incomplete.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (10)
package-lock.jsonis excluded by!**/package-lock.jsontest/static/chain_client.crt.pemis excluded by!**/*.pemtest/static/chain_client.key.pemis excluded by!**/*.pemtest/static/chain_root.crt.pemis excluded by!**/*.pemtest/static/chain_root.key.pemis excluded by!**/*.pemtest/static/expired_certificate.crt.pemis excluded by!**/*.pemtest/static/expired_certificate.key.pemis excluded by!**/*.pemtest/static/future_certificate.crt.pemis excluded by!**/*.pemtest/static/future_certificate.csr.pemis excluded by!**/*.pemtest/static/future_certificate.key.pemis excluded by!**/*.pem
📒 Files selected for processing (30)
.nvmrc(1 hunks)README.md(1 hunks)XMLDSIG_VERIFIER.md(1 hunks)package.json(1 hunks)src/c14n-canonicalization.ts(4 hunks)src/enveloped-signature.ts(3 hunks)src/exclusive-canonicalization.ts(6 hunks)src/hash-algorithms.ts(4 hunks)src/index.ts(1 hunks)src/signature-algorithms.ts(6 hunks)src/signed-xml.ts(26 hunks)src/types.ts(4 hunks)src/utils.ts(1 hunks)src/xmldsig-uris.ts(1 hunks)src/xmldsig-verifier.ts(1 hunks)test/c14n-non-exclusive-unit-tests.spec.ts(1 hunks)test/c14nWithComments-unit-tests.spec.ts(5 hunks)test/canonicalization-unit-tests.spec.ts(5 hunks)test/document-tests.spec.ts(3 hunks)test/hmac-tests.spec.ts(4 hunks)test/key-info-tests.spec.ts(3 hunks)test/saml-response-tests.spec.ts(12 hunks)test/signature-integration-tests.spec.ts(9 hunks)test/signature-object-tests.spec.ts(24 hunks)test/signature-unit-tests.spec.ts(31 hunks)test/static/chain_root.crt.srl(1 hunks)test/utils-tests.spec.ts(1 hunks)test/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cs(7 hunks)test/wsfed-metadata-tests.spec.ts(2 hunks)test/xmldsig-verifier.spec.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- XMLDSIG_VERIFIER.md
- test/static/chain_root.crt.srl
🚧 Files skipped from review as they are similar to previous changes (12)
- src/index.ts
- src/xmldsig-uris.ts
- test/utils-tests.spec.ts
- src/hash-algorithms.ts
- src/signature-algorithms.ts
- test/canonicalization-unit-tests.spec.ts
- test/c14n-non-exclusive-unit-tests.spec.ts
- test/signature-object-tests.spec.ts
- package.json
- test/hmac-tests.spec.ts
- .nvmrc
- test/key-info-tests.spec.ts
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: In src/signed-xml.ts Line 1099, createReferences mutates ref.uri = id during signing. Maintain this behavior for now; remove/refactor in a separate PR as previously requested by the maintainer.
Applied to files:
test/wsfed-metadata-tests.spec.tstest/signature-unit-tests.spec.tstest/saml-response-tests.spec.tstest/xmldsig-verifier.spec.tstest/signature-integration-tests.spec.tstest/document-tests.spec.tsREADME.mdsrc/xmldsig-verifier.tssrc/exclusive-canonicalization.tssrc/c14n-canonicalization.tssrc/signed-xml.tstest/c14nWithComments-unit-tests.spec.tssrc/enveloped-signature.tssrc/types.ts
📚 Learning: 2025-10-22T21:03:38.354Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T21:03:38.354Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) requested an issue to separate the overloaded Reference interface into distinct SigningReference and ValidationReference types. Initial hypothesis: signing-only (xpath, isEmptyUri, id, type), validation-only (uri, digestValue, validationError, signedReference), shared (transforms, digestAlgorithm, inclusiveNamespacesPrefixList). This should be proposed and designed in a follow-up, not altered in the current PR.
Applied to files:
test/wsfed-metadata-tests.spec.tstest/signature-unit-tests.spec.tstest/saml-response-tests.spec.tstest/signature-integration-tests.spec.tstest/document-tests.spec.tsREADME.mdsrc/xmldsig-verifier.tssrc/exclusive-canonicalization.tssrc/c14n-canonicalization.tssrc/signed-xml.tstest/c14nWithComments-unit-tests.spec.tssrc/enveloped-signature.tssrc/types.ts
📚 Learning: 2025-10-25T14:41:13.728Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 519
File: example/new-api-example.js:1-9
Timestamp: 2025-10-25T14:41:13.728Z
Learning: Example files in the node-saml/xml-crypto repository should use `require("xml-crypto")` (the package name) rather than relative paths to build artifacts, since they demonstrate usage from an end-user's perspective.
Applied to files:
test/wsfed-metadata-tests.spec.tstest/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cstest/signature-unit-tests.spec.tstest/saml-response-tests.spec.tstest/signature-integration-tests.spec.tstest/document-tests.spec.tsREADME.mdsrc/signed-xml.tstest/c14nWithComments-unit-tests.spec.tssrc/types.ts
📚 Learning: 2025-10-22T21:50:05.454Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 0
File: :0-0
Timestamp: 2025-10-22T21:50:05.454Z
Learning: The current Reference fields are defined in src/types.ts Lines 109–168: xpath?, transforms, digestAlgorithm, uri, digestValue?, inclusiveNamespacesPrefixList, isEmptyUri, ancestorNamespaces?, validationError?, getValidatedNode(), signedReference?.
Applied to files:
test/wsfed-metadata-tests.spec.tstest/signature-unit-tests.spec.tstest/saml-response-tests.spec.tstest/signature-integration-tests.spec.tstest/document-tests.spec.tssrc/xmldsig-verifier.tssrc/exclusive-canonicalization.tssrc/signed-xml.tstest/c14nWithComments-unit-tests.spec.tssrc/enveloped-signature.tssrc/types.ts
📚 Learning: 2025-10-22T20:36:00.758Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 506
File: src/signed-xml.ts:1159-1159
Timestamp: 2025-10-22T20:36:00.758Z
Learning: In node-saml/xml-crypto PR #506, the maintainer (shunkica) prefers to address the ref.uri mutation inside addAllReferences in a separate PR; removing the in-loop assignment is the desired fix but may be treated as a breaking change. Future guidance: avoid behavioral changes to ref.uri in the current PR.
Applied to files:
test/wsfed-metadata-tests.spec.tstest/signature-unit-tests.spec.tstest/saml-response-tests.spec.tstest/signature-integration-tests.spec.tssrc/xmldsig-verifier.tssrc/signed-xml.tstest/c14nWithComments-unit-tests.spec.tssrc/types.ts
📚 Learning: 2025-08-17T10:50:18.024Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 506
File: src/signed-xml.ts:1447-1451
Timestamp: 2025-08-17T10:50:18.024Z
Learning: In the xml-crypto codebase, ref.digestValue is only used during signature validation when loading references from existing signatures, not during signature creation in methods like createReferences or processSignatureReferences.
Applied to files:
test/signature-unit-tests.spec.tstest/signature-integration-tests.spec.tsREADME.mdsrc/xmldsig-verifier.tssrc/signed-xml.ts
📚 Learning: 2025-08-16T14:07:57.644Z
Learnt from: shunkica
Repo: node-saml/xml-crypto PR: 506
File: src/signed-xml.ts:1088-1119
Timestamp: 2025-08-16T14:07:57.644Z
Learning: The xml-crypto library does not validate user input and places the responsibility for providing properly encoded values on the users. This is a consistent design philosophy throughout the library.
Applied to files:
test/signature-unit-tests.spec.tsREADME.md
🧬 Code graph analysis (11)
test/wsfed-metadata-tests.spec.ts (2)
src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
test/signature-unit-tests.spec.ts (2)
src/index.ts (2)
XMLDSIG_URIS(8-8)SignedXml(6-6)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
test/saml-response-tests.spec.ts (1)
src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
test/xmldsig-verifier.spec.ts (4)
src/index.ts (4)
XMLDSIG_URIS(8-8)SignedXml(6-6)XmlDSigVerifier(7-7)ExclusiveCanonicalization(3-3)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)src/xmldsig-verifier.ts (2)
createSignedXml(229-297)XmlDSigVerifier(78-312)src/signed-xml.ts (1)
SignedXml(41-1542)
test/signature-integration-tests.spec.ts (2)
src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
test/document-tests.spec.ts (2)
src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/xmldsig-verifier.ts (2)
src/types.ts (11)
VerificationIdAttributeType(22-24)KeyInfoKeySelector(315-318)KeyInfoXmlDSigSecurityOptions(387-402)CertificateKeySelector(310-313)XmlDSigVerifierSecurityOptions(350-385)SharedSecretKeySelector(320-323)XmlDSigVerifierOptions(451-454)SharedSecretXmlDSigVerifierOptions(439-449)PublicCertXmlDSigVerifierOptions(424-434)XmlDsigVerificationResult(475-477)KeySelectorFunction(51-51)src/utils.ts (1)
isArrayHasLength(5-7)
src/c14n-canonicalization.ts (2)
src/types.ts (3)
CanonicalizationAlgorithm(120-126)TransformAlgorithmOptions(58-64)CanonicalizationAlgorithmURI(116-118)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
test/c14nWithComments-unit-tests.spec.ts (2)
src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/enveloped-signature.ts (3)
src/types.ts (3)
TransformAlgorithm(111-115)TransformAlgorithmOptions(58-64)CanonicalizationOrTransformAlgorithmType(146-148)src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
src/types.ts (2)
src/index.ts (1)
XMLDSIG_URIS(8-8)src/xmldsig-uris.ts (1)
XMLDSIG_URIS(54-60)
🔇 Additional comments (47)
test/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cs (1)
1-278: LGTM - Formatting changes only.All modifications are whitespace/blank-line adjustments with no functional impact on the test utility.
src/types.ts (7)
10-13: LGTM - Clean import structure for centralized URIs.Destructuring the algorithm constants from
XMLDSIG_URISprovides clear access to the URI constants throughout the type definitions.
17-25: Well-designed discriminated union for ID attribute types.The separation of
SignatureIdAttributeTypeandVerificationIdAttributeTypecorrectly distinguishes signing requirements (prefix mandatory when using object form with namespace) from verification (prefix not needed). The use ofnullfor explicit non-namespaced matching aligns with the intended design.
66-96: LGTM - SignatureAlgorithm interface is well-structured.The interface provides clear contracts for signing and verification with proper overloads supporting both sync and async (callback-based) usage patterns.
116-142: Good design with CanonicalizationAlgorithm extending TransformAlgorithm.The inheritance relationship correctly models that canonicalization is a specialized transform. The deprecated aliases provide a smooth migration path while the TODO at lines 123-125 documents the planned future refinement.
350-385: Comprehensive security options for signature verification.The
XmlDSigVerifierSecurityOptionsinterface provides fine-grained control over allowed algorithms and transforms, enabling defense-in-depth against algorithm substitution attacks. The JSDoc references toSignedXmldefaults are helpful for understanding default behavior.
387-402: Good separation of KeyInfo-specific security options.The
KeyInfoXmlDSigSecurityOptionsappropriately adds certificate expiration checking and truststore validation only for theKeyInfoKeySelectorpath where these features are meaningful.
459-477: Well-designed result types using discriminated unions.The
SuccessfulXmlDsigVerificationResultandFailedXmlDsigVerificationResulttypes provide type-safe result handling with mutually exclusive properties (signedReferencesvserror), enabling clean conditional logic in consuming code.test/saml-response-tests.spec.ts (2)
1-1: Good refactoring to use centralized XMLDSIG_URIS constants.Importing
XMLDSIG_URISfrom the public API surface ensures test code stays synchronized with the library's URI definitions.
12-14: Consistent use of XMLDSIG_URIS.NAMESPACES.ds across XPath expressions.Replacing hardcoded namespace URIs with the centralized constant improves maintainability and reduces the risk of typos or inconsistencies.
test/document-tests.spec.ts (2)
1-1: LGTM - Consistent with other test file refactoring.The import and XPath expression updates align with the centralized URI constants pattern applied across the test suite.
12-14: Consistent namespace URI constant usage.The XPath expressions now properly reference
XMLDSIG_URIS.NAMESPACES.ds, maintaining consistency with the SAML response tests and other updated test files.README.md (1)
212-236: Verify that the referencedXMLDSIG_VERIFIER.mdfile exists and contains detailed usage instructions.The example clearly demonstrates the simplified verification workflow with
keySelectorand result handling. However, the README referencesXMLDSIG_VERIFIER.mdfor detailed usage instructions—confirm this file exists in the repository and provides adequate documentation for users following the link.test/wsfed-metadata-tests.spec.ts (1)
1-1: LGTM! Consistent use of centralized URI constants.The import of
XMLDSIG_URISand its usage in the XPath query to reference the XMLDSig namespace improves maintainability and aligns with the PR's goal of centralizing URI definitions.Also applies to: 12-15
src/utils.ts (3)
9-14: LGTM! Explicit null handling for namespace-less attribute matching.The distinction between
null(explicitly no namespace) andundefined(any namespace) enables precise control over ID attribute matching, which is important for the newidAttributessecurity option withnamespaceUri: null.
16-29: LGTM! Consistent null handling in implicit attribute matching.The same null-handling pattern is correctly applied here, ensuring that when
namespaceis explicitlynull, only attributes without a namespace URI will match.
31-35: LGTM! Updated signature with proper type for namespace parameter.The
string | null | undefinedtype accurately represents the three semantic states: explicit namespace, explicitly no namespace, and "any namespace".test/c14nWithComments-unit-tests.spec.ts (2)
6-6: LGTM! Import of centralized URI constants.
359-361: LGTM! Consistent use of XMLDSIG_URIS for transform and canonicalization algorithms.Replacing hard-coded URIs with
XMLDSIG_URIS.TRANSFORM_ALGORITHMS.ENVELOPED_SIGNATUREandXMLDSIG_URIS.CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14Nimproves maintainability.Also applies to: 376-376
test/signature-integration-tests.spec.ts (2)
3-3: LGTM! Import and destructuring of XMLDSIG_URIS constants.Also applies to: 9-11
16-17: LGTM! Consistent replacement of hard-coded URIs with XMLDSIG_URIS constants.All algorithm and namespace references now use the centralized constants, improving maintainability across the test suite.
Also applies to: 22-22, 37-37, 57-57, 104-104, 123-123, 145-145, 167-167, 186-191
test/xmldsig-verifier.spec.ts (9)
1-16: LGTM! Well-organized imports and constant extraction.The imports are properly structured, bringing in necessary components from the library and extracting algorithm constants for cleaner test code.
35-53: LGTM! Clean helper function for creating signed XML.The
createSignedXmlhelper reduces duplication across tests while providing flexibility through options.
110-124: LGTM! Well-designed assertion helpers.The
expectValidResultandexpectInvalidResulthelpers provide consistent result validation with clear error message matching.
126-190: LGTM! Comprehensive constructor tests.The tests cover the key constructor scenarios including valid configurations, invalid configurations, and error cases for missing or invalid
getCertFromKeyInfo.
391-558: LGTM! Thorough idAttributes namespace handling tests.This test suite comprehensively covers the new namespace-aware ID attribute matching, including:
- String-based idAttributes (matches any namespace)
namespaceUri: undefined(matches any namespace)namespaceUri: null(matches only non-namespaced attributes)- Explicit namespace matching and mismatches
This aligns well with the
findAttrchanges insrc/utils.ts.
623-661: LGTM! Certificate expiration validation tests.The tests properly cover valid certificates, expired certificates with check disabled, expired certificates with check enabled, and future certificates (not yet valid).
663-728: LGTM! Truststore validation tests.The tests cover exact certificate matching, chain verification, X509Certificate objects in truststore, untrusted certificates, and interaction with
checkCertExpiration.
839-940: LGTM! Multiple signature handling tests.The tests properly verify behavior with no signatures, a specific signature node provided, multiple signatures without specification (should fail), and verifying specific signatures from a multi-signature document.
942-998: LGTM! Static verifySignature method tests.The tests cover both success and error scenarios with
throwOnErrortrue and false, ensuring the static method behaves consistently with instance methods.test/signature-unit-tests.spec.ts (4)
3-3: LGTM! Consistent import and usage of XMLDSIG_URIS constants.The signature algorithms array and all subsequent usages now reference the centralized constants, improving maintainability.
Also applies to: 9-17
29-30: LGTM! Comprehensive replacement of hard-coded algorithm URIs.All
digestAlgorithm,transforms,canonicalizationAlgorithm, andsignatureAlgorithmassignments now use the appropriateXMLDSIG_URISconstants.Also applies to: 33-34, 42-42, 98-99, 103-104, 108-109, 112-113, 146-147, 150-151, 154-155, 172-173, 175-176, 207-208, 211-212, 234-234, 244-245, 247-248, 268-269, 272-273, 300-301, 304-305, 331-332, 335-336, 363-364, 367-368
858-858: LGTM! Remaining algorithm and namespace references updated.All signature loading, validation, and reference creation tests now use the centralized constants.
Also applies to: 866-867, 870-871, 915-917, 937-938, 1062-1063, 1070-1071, 1129-1130, 1138-1139, 1155-1156, 1162-1163, 1192-1193, 1199-1200, 1220-1221, 1224-1225, 1259-1260, 1263-1264, 1290-1291, 1322-1323, 1361-1362, 1367-1368, 1395-1396, 1401-1402, 1414-1415, 1420-1421
787-788: LGTM! Mock signature algorithm returns the correct URI constant.The
getAlgorithmNamemethod in the async callback test now returnsSIGNATURE_ALGORITHMS.RSA_SHA1instead of a hard-coded string.src/enveloped-signature.ts (3)
3-8: LGTM! Updated imports and interface implementation.The class now correctly implements
TransformAlgorithmwith the appropriate type imports, aligning with the new interface hierarchy.Also applies to: 10-10
17-17: LGTM! Method signature and namespace constant usage.The
processmethod correctly usesTransformAlgorithmOptions, and XPath queries now referenceXMLDSIG_URIS.NAMESPACES.dsfor consistency.Also applies to: 20-20, 37-37
58-62: LGTM! Algorithm name returns centralized constant with deprecation note.The TODO comment appropriately flags the return type change for the next breaking version, while the eslint-disable handles the current deprecation warning.
src/c14n-canonicalization.ts (4)
1-10: LGTM! Updated imports and interface implementation.The class now correctly implements
CanonicalizationAlgorithmwith the appropriate type imports, andXMLDSIG_URISis imported for URI constants.Also applies to: 12-12
257-260: LGTM! Updated process method signature.The method now uses
TransformAlgorithmOptionsand the JSDoc is updated accordingly.
281-283: LGTM! getAlgorithmName returns typed URI constant.The return type
CanonicalizationAlgorithmURIand the use ofXMLDSIG_URIS.CANONICALIZATION_ALGORITHMS.C14Nprovide type safety and consistency.
295-297: LGTM! Subclass correctly overrides getAlgorithmName.The
C14nCanonicalizationWithCommentsclass properly returns theC14N_WITH_COMMENTSURI constant.src/xmldsig-verifier.ts (3)
75-117: Verifier API shape and SignedXml reuse look consistent with intended behaviorThe split between the static
verifySignature()and the instance method, plus funneling all failures throughhandleError, reads clean and predictable. Reusing a singleSignedXmlperXmlDSigVerifierinstance is also fine here:loadSignature()andcheckSignature()fully reset the verification state between calls, so a configured verifier can safely be reused for many documents with the same policy. Based on learnings, this matches the intended performance characteristics without leaking state between verifications.Also applies to: 126-167
229-297: KeyInfo truststore and expiration enforcement are correctly centralized ingetCertFromKeyInfoThe KeyInfo path in
createSignedXml()cleanly normalizes the truststore to public keys, enforcescheckCertExpiration, and requires the signing cert to either match or verify against one of the truststore entries before returning it to SignedXml. Placing this logic inside the injectedgetCertFromKeyInfokeeps SignedXml unchanged while letting XmlDSigVerifier enforce stronger policies.No issues spotted here; behavior matches the intent of
KeyInfoXmlDSigSecurityOptions(truststore + expiration) and integrates cleanly with SignedXml’scheckSignature()flow.
299-311: Centralized error mapping is minimal and predictable
handleError()’s behavior of either rethrowing the original value or returning{ success: false, error: string }gives a simple contract to both the static and instance methods. The typo fix in the fallback message (“occurred”) is also applied correctly.No further changes needed here.
src/signed-xml.ts (3)
32-39: Algorithm registry and lookup refactor looks correct and keeps customization intactThe switch to
XMLDSIG_URIS-backed maps (CanonicalizationAlgorithms,HashAlgorithms,SignatureAlgorithms,TransformAlgorithms) plus thegetDefault*helpers and thefind*Algorithm()methods gives a much clearer, URI-driven surface while still allowing callers to override allowed algorithms via the constructor options.
enableHMAC()cleanly swapsSignatureAlgorithmsto the symmetric-only map and disables KeyInfo content, which is consistent with the security comment about avoiding key confusion. ThefindSignatureAlgorithm/findHashAlgorithm/findCanonicalizationAlgorithmimplementations correctly enforce the allow-lists and produce useful error messages when an unsupported URI shows up.This all looks good and aligns with the new XmlDSigVerifier defaults.
Also applies to: 112-135, 142-175, 223-229, 511-541, 544-556
41-56:idAttributesand namespace handling now properly distinguish string, undefined, and null casesThe introduction of
idAttributes: IdAttributeType[]withgetDefaultIdAttributes()and the updated logic in bothvalidateElementAgainstReferences()andvalidateReference()correctly supports:
- Simple string attributes (match by local-name in any namespace),
- Object form with
namespaceUrias a string (match only that namespace),namespaceUri === null(explicitly require no namespace, i.e.,namespace-uri(.)=''),namespaceUri === undefined(match regardless of namespace).This mirrors the semantics in both validation paths and addresses the previous inconsistencies around
undefined/nullthat could yieldnamespace-uri(.)='undefined'/'null'. Based on learnings, this is the desired behavior and should fix the earlier edge cases without altering the signing side.Also applies to: 174-205, 568-607, 620-634
1734-1793: SignedInfo generation correctly uses canonicalization/signature algorithm URIs
createSignedInfo()now pulls the canonicalization and signature algorithms from the configured maps and writes their URIs viagetAlgorithmName(), and theInclusiveNamespaceselement’sxmlnsis set to the canonicalization algorithm URI. This is consistent with the rest of the URI-driven refactor and the updated Exclusive/Non-exclusive canonicalization implementations.No issues spotted; this should interoperate cleanly with the new XmlDSigVerifier and the centralized XMLDSIG_URIS constants.
As per the discussion #517 I set out to do some work on this problematic.
My conclusion was that it would be best to not just separate the reference types, but separate the signer from the validator entirely, while providing a more streamlined interface.
I am submitting this initial draft for your review and opinions.
The goal was to not introduce any breaking change, instead produce these wrappers so users can slowly start migrating toward them. The next step would be to deprecate
SignedXml, migrate the specific signature/validation functions in their respective class, with the final breaking change being to removeSignedXmlentirely.The tests are currently just AI slop. I welcome anyone that wants to contribute.
Once the XmlDSigSigner and XmlDSigValidator are configured, they can be reused to sign/validate multiple xml files, since each time sign/compute is called a new SignedXml instance is created internally.
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.