Skip to content

Conversation

@shunkica
Copy link
Contributor

@shunkica shunkica commented Oct 25, 2025

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 remove SignedXml entirely.

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

    • Added XmlDSigVerifier for secure, simplified XML signature verification with configurable security policies
    • Introduced centralized algorithm URIs for consistent configuration
    • Support for multiple key selection strategies (certificates, KeyInfo with truststore validation, shared secrets)
    • Enhanced ID attribute handling with namespace awareness
    • Added transform limits and certificate expiration checks for improved security
  • Bug Fixes

    • Improved namespace handling in attribute lookups
  • Documentation

    • New XmlDSigVerifier guide with practical examples
    • Updated verification documentation with modern best practices

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Oct 25, 2025

Walkthrough

This PR modernizes the XML Digital Signature library by introducing a URI-driven type system, centralizing XMLDSig constants, adding a new XmlDSigVerifier class for simplified verification, supporting flexible key selection strategies, implementing algorithm registry maps, and enhancing ID attribute handling with namespace awareness.

Changes

Cohort / File(s) Summary
Constants & URIs
src/xmldsig-uris.ts, src/index.ts
New module that aggregates and exports all XMLDSig algorithm URIs (canonicalization, transforms, hash, signatures) and namespaces as XMLDSIG_URIS constant; added to public API exports.
Type System Overhaul
src/types.ts
Introduced URI-based algorithm type hierarchy with interfaces (SignatureAlgorithm, HashAlgorithm, TransformAlgorithm, CanonicalizationAlgorithm), algorithm maps, key selector mechanism (KeySelectorFunction, CertificateKeySelector, KeyInfoKeySelector, SharedSecretKeySelector), and new verifier option types; extended SignedXmlOptions with algorithm URIs, ID attribute handling, and transform limits.
New Verifier Implementation
src/xmldsig-verifier.ts, test/xmldsig-verifier.spec.ts
Introduced XmlDSigVerifier class with static and instance verification entry points, configurable security options (transform limits, certificate expiration checks, truststore validation), flexible key selection strategies, and structured error handling; comprehensive test suite covering all verification scenarios.
Core Algorithm Updates
src/c14n-canonicalization.ts, src/exclusive-canonicalization.ts, src/hash-algorithms.ts, src/signature-algorithms.ts, src/enveloped-signature.ts
Updated all algorithm classes to implement new interfaces (CanonicalizationAlgorithm, HashAlgorithm, TransformAlgorithm), accept TransformAlgorithmOptions, return algorithm URIs from getAlgorithmName(), and reference centralized XMLDSIG_URIS constants instead of hard-coded strings.
SignedXml Refactoring
src/signed-xml.ts
Replaced flat algorithm types with URI-based variants, integrated algorithm registry maps with defaults, refactored ID attribute handling to support namespace-aware format, added maxTransforms enforcement, updated all algorithm lookups and processing to use map-based architecture.
Utility & Namespace Handling
src/utils.ts
Enhanced findAttr() to accept null namespaces and treat null as absence of namespace, adding explicit null-branch handling in attribute comparison logic.
Test Suite Updates
test/c14n-*.spec.ts, test/canonicalization-unit-tests.spec.ts, test/document-tests.spec.ts, test/hmac-tests.spec.ts, test/key-info-tests.spec.ts, test/saml-response-tests.spec.ts, test/signature-*.spec.ts, test/wsfed-metadata-tests.spec.ts, test/utils-tests.spec.ts
Updated imports to use public XMLDSIG_URIS export; replaced all hard-coded XMLDSig URIs with centralized constants throughout test assertions and configurations; added new tests for null namespace handling and XmlDSigVerifier functionality.
Documentation & Configuration
.nvmrc, package.json, README.md, XMLDSIG_VERIFIER.md
Added Node.js version pinning (v22.16.0), bumped xpath dependency, documented new XmlDSigVerifier API with usage examples and security guidance, expanded implicit transforms explanation.
Test Certificates
test/static/chain_root.crt.srl, test/validators/...
Added certificate serial file and minor formatting adjustments to validation test utilities.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • src/types.ts: Comprehensive type hierarchy overhaul with numerous new interfaces, aliases, and deprecated patterns; requires verification that backward-compatibility aliases are correct and deprecation paths are clear.
  • src/signed-xml.ts: Large-scale refactoring integrating algorithm maps, ID attribute changes, and transform limits throughout signing/verification paths; logic density requires careful tracing of algorithm lookups and ID resolution.
  • src/xmldsig-verifier.ts: New verifier class with complex option resolution pipeline, key selection discrimination, certificate validation, and error handling; verify all security checks (expiration, truststore, transform limits) are correctly enforced.
  • Type-to-URI migration across algorithm classes (c14n-canonicalization.ts, exclusive-canonicalization.ts, hash-algorithms.ts, signature-algorithms.ts, enveloped-signature.ts): Verify consistent use of centralized XMLDSIG_URIS constants and correct interface implementations.
  • Test coverage: Extensive test updates across 10+ test files; spot-check that all constant substitutions are correct and test logic aligns with refactored code paths.

Possibly related PRs

Suggested labels

enhancement, documentation

Suggested reviewers

  • cjbarth

Poem

🐰 A rabbit's ode to cleaner code:
URIs bundled, types refined—no more hardcoded roads,
Verifier verified, security assured,
Constants centralized, all fears cured,
Maps and selectors dance in harmony,
From flat strings to typed serenity! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: introducing a new XmlDSigVerifier wrapper class for SignedXml to provide a streamlined verification interface.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Oct 25, 2025

Codecov Report

❌ Patch coverage is 86.30952% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.61%. Comparing base (627d83f) to head (41faa18).
⚠️ Report is 13 commits behind head on master.

Files with missing lines Patch % Lines
src/signed-xml.ts 71.01% 14 Missing and 6 partials ⚠️
src/c14n-canonicalization.ts 50.00% 2 Missing ⚠️
src/exclusive-canonicalization.ts 75.00% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a 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 = string erodes DX and safety. Recommend unions from algorithms.ts values 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 const is 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 === null even on validation errors. If this is the desired API, document it; otherwise, return errors via err.

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.ts using tsd to assert enableWSSecurityMode forbids idAttributes.


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.

createSignedXml and createMultiSignedXml duplicate 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 @experimental or 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&quot;s&apos;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

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4813d and d6a1a17.

📒 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:

  • maxTransforms is actively used in the implementation (lines 12, 77, 116 of src/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.

Copy link

@coderabbitai coderabbitai bot left a 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 pemToDer utility 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

📥 Commits

Reviewing files that changed from the base of the PR and between d6a1a17 and 05089f3.

📒 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.ts
  • example/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 loadSignature to 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 never type to enforce mutual exclusivity between enableWSSecurityMode and idAttributes is 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 buildIdXPath for safe escaping)
  • External URIs (correctly rejected for signing)

The use of buildIdXPath ensures 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

Copy link

@coderabbitai coderabbitai bot left a 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 examples

Examples 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 immutability

Consider 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 checks

Both 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 message

The 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 Id

Before 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 to string reduces safety; derive unions from Algorithms while allowing extension

Preserve 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: add maxTransforms?

XmlDSigValidator passes maxTransforms into SignedXml options; 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 misleading

You 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 implicitTransforms

If 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 inputs

Returning 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 incorrect

Type 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 helper

Wrap 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 assets

These 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

📥 Commits

Reviewing files that changed from the base of the PR and between 92c81fd and ee0beef.

📒 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.ts
  • example/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 good

Re-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 = id is preserved while computing references. This aligns with prior guidance to defer behavioral changes to a separate PR. Looks good.

Based on learnings

Copy link

@coderabbitai coderabbitai bot left a 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 limit

This 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 SignedXml

Rename for accuracy or switch to XmlDSigSigner to 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 examples

Expose an enableWSSecurityMode?: boolean option and pass idMode: "wssecurity" to SignedXml when true; skip setting idAttributes in that case. This matches the WS-Security profile used in examples.

Also applies to: 165-172


159-163: Fix class name in error message

Use 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 IDs

Setting idAttributes with 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 consistency

Replace hardcoded URIs with Algorithms.signature/*, Algorithms.canonicalization/*, and Algorithms.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 clarity

Swap canonicalization.EXCLUSIVE_C14N for transform.EXCLUSIVE_C14N when populating Reference.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 compatibility

Older 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 if certPath ever 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 factory

Ensure 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

📥 Commits

Reviewing files that changed from the base of the PR and between ee0beef and ca57699.

📒 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.ts
  • test/xmldsig-signer.spec.ts
  • src/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 attributes

The 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.

@shunkica shunkica changed the title Add XmlSigner and XmlValidator wrappers around SignedXml Add XmlDSigSigner and XmlDSigValidator wrappers around SignedXml Oct 25, 2025
@shunkica shunkica force-pushed the signer-validator-separation branch from ca57699 to 414ec7c Compare October 27, 2025 18:47
Copy link

@coderabbitai coderabbitai bot left a 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 validateElementAgainstReferences

Unlike 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 namespace

When attr.namespaceURI is falsy, XPath becomes namespace-uri(.)='undefined'. Mirror the conditional logic used in validateReference.

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 SignedXml

Rename 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 limit

It 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 publicCert

Tests and examples pass PEM strings; typing only KeyLike is too narrow.

-export type PublicCertXmlValidatorOptions = CommonValidatorOptions & {
-  publicCert: KeyLike;
-};
+export type PublicCertXmlValidatorOptions = CommonValidatorOptions & {
+  publicCert: KeyLike | string | Buffer;
+};

138-146: Enforce maxTransforms before cryptographic verification

Defensive 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 message

Use 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 mimeType

Pass 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 privateKey parameter currently only accepts crypto.KeyLike, but should also accept string | Buffer to match common usage patterns with PEM-encoded keys.

🧹 Nitpick comments (7)
src/types.ts (2)

13-19: Keep algorithm type safety while allowing extensibility

Switching to plain string removes 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 discoverability

Add brief JSDoc to idAttributeQNames and namespaceMap describing precedence over idAttributes/idMode and expected QName format (e.g., ["wsu:Id", "Id"]).

src/algorithms.ts (1)

1-44: Algorithm registry looks good; consider exporting group types for DX

The 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 IDs

Before calling setAttributeNS for xmlns:${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 tests

Using Algorithms avoids 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 idAttributes

When idAttributeQNames is set, SignedXml already uses it; assigning colonized QNames into idAttributes can be misleading. Only set idAttributes if 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 flag

Many users expect a simple switch. Offer enableWSSecurityMode?: boolean to pass idMode: "wssecurity" through to SignedXml automatically (or document using idAttributeQNames: ["wsu:Id"] with namespaceMap).

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca57699 and 414ec7c.

📒 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.ts
  • src/signed-xml.ts
  • src/xmldsig-validator.ts
  • test/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 computeOptions once 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 SigningReference to 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 createSignedXml method 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

Copy link

@coderabbitai coderabbitai bot left a 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 XmlDSigSigner

The 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 signature

SignedXml 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 configuration

Add 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 name

Use 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 name

Same 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 namespace

Avoid 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.parseFromString

Pass "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 limit

Currently 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 unions

Using 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 ValidationReference

Different 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 XmlDsig

Consider renaming XmlDsigValidatorSecurityOptions to XmlDSigValidatorSecurityOptions for consistency with the class name.


138-149: Minor: simplify truststore guard

A 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() calls

Your scan confirms 50+ instances of parseFromString() calls missing the mimeType parameter across test files, source files, and examples. The call at line 233 in example/xmldsig-validator-example.js is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 414ec7c and c3e31a0.

⛔ Files ignored due to path filters (6)
  • test/static/chain_client.crt.pem is excluded by !**/*.pem
  • test/static/chain_client.key.pem is excluded by !**/*.pem
  • test/static/chain_root.crt.pem is excluded by !**/*.pem
  • test/static/chain_root.key.pem is excluded by !**/*.pem
  • test/static/expired_certificate.crt.pem is excluded by !**/*.pem
  • test/static/expired_certificate.key.pem is 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.ts
  • src/xmldsig-validator.ts
  • test/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 now

Setting ref.uri = id aligns with existing behavior and the maintainer’s request to defer changes.

@shunkica shunkica force-pushed the signer-validator-separation branch 2 times, most recently from 7c419b5 to 8fd99f2 Compare October 28, 2025 16:44
Copy link

@coderabbitai coderabbitai bot left a 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 when maxTransforms is set to 0 (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

📥 Commits

Reviewing files that changed from the base of the PR and between c3e31a0 and 8fd99f2.

⛔ Files ignored due to path filters (6)
  • test/static/chain_client.crt.pem is excluded by !**/*.pem
  • test/static/chain_client.key.pem is excluded by !**/*.pem
  • test/static/chain_root.crt.pem is excluded by !**/*.pem
  • test/static/chain_root.key.pem is excluded by !**/*.pem
  • test/static/expired_certificate.crt.pem is excluded by !**/*.pem
  • test/static/expired_certificate.key.pem is 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.ts
  • src/xmldsig-signer.ts
  • src/xmldsig-validator.ts
  • test/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)

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8fd99f2 and a6cd55c.

📒 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.ts
  • src/xmldsig-validator.ts
  • src/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 via this.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.

@shunkica shunkica changed the title Add XmlDSigSigner and XmlDSigValidator wrappers around SignedXml Add XmlDSigValidator wrapper around SignedXml Oct 28, 2025
Copy link

@coderabbitai coderabbitai bot left a 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 constants

TRANSFORM_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 constants

Use 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 coupling

Using 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 Algorithms with AlgorithmsConst, 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 type

getKeyInfoContent 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 options

Both 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 typing

Digest 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 explicitly

With 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 assertions

Match 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 getCertFromKeyInfo

To 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 literal

Use 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 IdAttributeType variants. However, if firstIdAttr has type { prefix: string; localName: string; namespaceUri: string } with an empty string prefix (e.g., prefix: ""), the check firstIdAttr.prefix would be falsy and fall through to the else branch, using setAttribute instead of setAttributeNS. 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

📥 Commits

Reviewing files that changed from the base of the PR and between a6cd55c and ec7592e.

📒 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.ts
  • src/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
  • src/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 constants

Consistent 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[] to IdAttributeType[] 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 maxTransforms correctly uses number | null to 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 TransformAlgorithms remaining 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 findTransformAlgorithm includes a backward-compatible fallback to canonicalization algorithms when TransformAlgorithms is 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 namespaceUri is defined (line 600) before including it in the XPath predicate, properly handling all variants of IdAttributeType.


857-863: LGTM! maxTransforms validation correctly handles 0.

The null check (this.maxTransforms !== null) correctly distinguishes between null (no limit) and 0 (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 findTransformAlgorithm method 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.ds for the XML Digital Signature namespace in XPath queries and attribute values.

@shunkica
Copy link
Contributor Author

@cjbarth I have removed the XmlDSigSigner wrapper because the scope just got too large for a single PR.
The new XmlDSigValidator has a lot of new security options.
Let me know your opinions.

@shunkica
Copy link
Contributor Author

I am also having second thoughts about the name and think that the proposal by @ahacker1-securesaml has some merit.

XmlDSigVerifier might make more sense in this context. Validation would imply we are checking it against some schema.

Consequentially validate() should be verify() or verifySignature()

Copy link

@coderabbitai coderabbitai bot left a 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 contract

During the earlier API split (see #506) the maintainer asked us to keep { namespaceUri: string | null } as the explicit “no namespace” sentinel. Switching this back to undefined reintroduces the same ambiguity and forces downstream code to check both undefined and null. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 255663b and e2f72ee.

📒 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.ts
  • src/xmldsig-verifier.ts
  • 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:

  • test/xmldsig-verifier.spec.ts
  • src/xmldsig-verifier.ts
  • 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:

  • test/xmldsig-verifier.spec.ts
  • 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: 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
  • 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 (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)

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 255663b and f7f887a.

📒 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.ts
  • src/xmldsig-verifier.ts
  • src/signed-xml.ts
  • 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:

  • test/xmldsig-verifier.spec.ts
  • src/xmldsig-verifier.ts
  • src/signed-xml.ts
  • 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:

  • test/xmldsig-verifier.spec.ts
  • src/signed-xml.ts
  • 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: 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
  • src/signed-xml.ts
  • 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/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 getCertFromKeyInfo properly validates certificate dates using X509Certificate.validTo and validFrom, 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 validateReference and 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 idAttributes to find existing IDs (lines 1405-1411) and sets new IDs using the first attribute's shape (lines 1426-1444). Line 1430's "prefix" in firstIdAttr check distinguishes SignatureIdAttributeType from VerificationIdAttributeType by 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 use setAttribute.

src/types.ts (3)

21-27: IdAttributeType union properly separates signing and validation concerns.

The three-type structure cleanly distinguishes:

  • SignatureIdAttributeType: includes prefix for namespace-qualified ID generation during signing
  • VerificationIdAttributeType: omits prefix since validation only cares about namespace URI matching
  • IdAttributeType: union allowing both in contexts where either may appear

This 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 with getAlgorithmName() methods
  • Corresponding *Map types enable runtime algorithm registration via Record<Name, new () => Algorithm>
  • Algorithm name types use typeof XMLDSIG_URIS.*[keyof typeof ...] for compile-time URI validation while allowing custom strings

This 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:

  • KeySelector union distinguishes direct certificate vs. KeyInfo extraction paths
  • XmlDSigVerifierSecurityOptions exposes all security controls (max transforms, cert expiration, truststore, algorithm allowlists) with clear defaults
  • XmlDsigVerificationResult discriminated union enables type-safe success/failure handling

Documentation is thorough with default value references and usage examples.

Copy link

@coderabbitai coderabbitai bot left a 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 KeySelector union type lacks a discriminant property, so TypeScript cannot narrow the type automatically. Users must manually check for the presence of publicCert or getCertFromKeyInfo.

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

📥 Commits

Reviewing files that changed from the base of the PR and between f7f887a and 76fccff.

📒 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 keyof pattern to extract URI literals, while the algorithm interfaces provide clean extension points. The decision to make CanonicalizationAlgorithm extend TransformAlgorithm correctly 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) and idAttributes (VerificationIdAttributeType[]) properly distinguishes signing from validation concerns.


196-199: LGTM! Reference interface aligned with new algorithm types.

The Reference interface correctly adopts TransformAlgorithmName and HashAlgorithmName, 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 XmlDsigVerificationResult uses a proper discriminated union pattern with the success field, allowing TypeScript to narrow the type and provide correct inference for error and signedReferences.

Copy link

@coderabbitai coderabbitai bot left a 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: Use splitQName helper for consistency.

Lines 57-58 inline a custom QName split that differs from the splitQName utility (lines 396-402). When there's no colon, this code uses prefix="" while splitQName returns prefix=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

📥 Commits

Reviewing files that changed from the base of the PR and between 76fccff and cb31b0b.

📒 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 null to explicitly match non-namespaced attributes.


31-47: LGTM! Updated signature supports explicit namespace control.

The function signature correctly accepts null to 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 present

The 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. Returns undefined for 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() and namespace-uri()
  • Checks attribute values against the ID
  • Handles both string and object IdAttributeType formats correctly

The 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_URIS establishes 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) and VerificationIdAttributeType (prefix-agnostic, with null for explicit non-namespaced matching) properly models the different requirements of signing vs validation workflows.


53-66: LGTM! Comprehensive algorithm processing types.

TransformAlgorithmOptions properly captures all context needed for transform operations (namespaces, signature node, inclusive prefixes). KeySelectorFunction provides a clean abstraction for key extraction. NamespacePrefix is 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 only string is 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 transforms and digestAlgorithm to use name-based types (TransformAlgorithmName, HashAlgorithmName) provides type safety and aligns with the new algorithm abstraction system. The ReadonlyArray for 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 success field enables type-safe result handling.

The corrected type on line 350 (CanonicalizationAlgorithmMap) properly constrains canonicalization algorithms.

Copy link

@coderabbitai coderabbitai bot left a 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 | undefined to namespace?: string | null, since the ? modifier already implies undefined.

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

📥 Commits

Reviewing files that changed from the base of the PR and between cb31b0b and 41faa18.

⛔ Files ignored due to path filters (3)
  • test/static/future_certificate.crt.pem is excluded by !**/*.pem
  • test/static/future_certificate.csr.pem is excluded by !**/*.pem
  • test/static/future_certificate.key.pem is 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 namespace
  • undefined: matches attributes with any namespace
  • string: matches that specific namespace

The loose equality check on line 13 correctly catches only undefined after the strict null check.


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 SignedXml instance. The instance-reuse pattern (line 63) is safe because loadSignature() 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 createSignedXml factory 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

Copy link

@coderabbitai coderabbitai bot left a 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 produces namespace-uri(.)='null'

validateElementAgainstReferences treats namespaceUri !== undefined as “namespaced”, which misclassifies null and diverges from validateReference (which treats falsy as “no namespace constraint”). Align with validateReference.

-          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 provided

If options.signatureNode is set and is a descendant of node, remove it by identity rather than scanning by SignatureValue. This avoids edge cases with duplicated values and is faster. Keep the current value-based fallback if the node isn’t under node.

-  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 examples

Keep current RSA‑SHA1 tests for back-compat, but add one parallel case using SIGNATURE_ALGORITHMS.RSA_SHA256 to set a stronger norm in docs/tests.


7-9: Unify import specifiers

Use 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_SHA256 to exercise non‑SHA1 defaults too.

src/xmldsig-verifier.ts (1)

181-189: Tighten truststore .some callback to explicit boolean return

Minor readability; avoids relying on implicit undefined falsy.

- 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 provided

When firstIdAttr has a namespaceUri but no prefix, the code falls back to setAttribute(localName, id), creating a non‑namespaced attribute. That diverges from intent and later matching.

Either:

  • Require a prefix for namespaced attributes (documented and enforced at type level), or
  • Auto-assign a stable prefix (e.g., idns) and declare it before calling setAttributeNS.

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 idAttributes to clarify: “If a namespaceUri is used, a prefix must be provided.”

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41faa18 and e9a1db7.

📒 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.ts
  • test/document-tests.spec.ts
  • test/wsfed-metadata-tests.spec.ts
  • test/xmldsig-verifier.spec.ts
  • test/saml-response-tests.spec.ts
  • test/hmac-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/c14n-canonicalization.ts
  • src/signed-xml.ts
  • test/canonicalization-unit-tests.spec.ts
  • test/signature-unit-tests.spec.ts
  • src/signature-algorithms.ts
  • test/c14n-non-exclusive-unit-tests.spec.ts
  • src/exclusive-canonicalization.ts
  • test/signature-object-tests.spec.ts
  • src/enveloped-signature.ts
  • src/types.ts
  • src/hash-algorithms.ts
  • test/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.ts
  • test/document-tests.spec.ts
  • test/wsfed-metadata-tests.spec.ts
  • test/xmldsig-verifier.spec.ts
  • test/saml-response-tests.spec.ts
  • test/hmac-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/signed-xml.ts
  • test/canonicalization-unit-tests.spec.ts
  • test/signature-unit-tests.spec.ts
  • src/signature-algorithms.ts
  • test/c14n-non-exclusive-unit-tests.spec.ts
  • test/signature-object-tests.spec.ts
  • src/types.ts
  • src/hash-algorithms.ts
  • test/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.ts
  • test/wsfed-metadata-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/hmac-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/signed-xml.ts
  • test/canonicalization-unit-tests.spec.ts
  • test/signature-unit-tests.spec.ts
  • src/signature-algorithms.ts
  • test/signature-object-tests.spec.ts
  • src/types.ts
  • src/hash-algorithms.ts
  • test/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.ts
  • test/document-tests.spec.ts
  • test/wsfed-metadata-tests.spec.ts
  • test/xmldsig-verifier.spec.ts
  • test/saml-response-tests.spec.ts
  • test/hmac-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/c14n-canonicalization.ts
  • src/signed-xml.ts
  • test/canonicalization-unit-tests.spec.ts
  • test/signature-unit-tests.spec.ts
  • src/signature-algorithms.ts
  • src/exclusive-canonicalization.ts
  • test/signature-object-tests.spec.ts
  • src/enveloped-signature.ts
  • src/types.ts
  • test/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.ts
  • test/document-tests.spec.ts
  • test/wsfed-metadata-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/hmac-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/signed-xml.ts
  • test/canonicalization-unit-tests.spec.ts
  • test/signature-unit-tests.spec.ts
  • src/exclusive-canonicalization.ts
  • test/signature-object-tests.spec.ts
  • src/enveloped-signature.ts
  • src/types.ts
  • test/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.ts
  • test/hmac-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/signed-xml.ts
  • test/signature-unit-tests.spec.ts
  • test/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.ts
  • test/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 SignatureIdAttributeType and VerificationIdAttributeType properly distinguishes signing (requires prefix) from validation (prefix ignored, explicit null for non-namespaced) use cases. Past review comments regarding null vs undefined have been addressed.


49-62: LGTM: Supporting type definitions for algorithms.

The KeySelectorFunction, NamespacePrefix, and TransformAlgorithmOptions types provide a solid foundation for the algorithm abstraction layer.


64-94: LGTM: SignatureAlgorithm interface and map.

The generic SignatureAlgorithm interface with both sync/async overloads and the SignatureAlgorithmMap type 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 TransformAlgorithm to 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:

  • idAttribute and idAttributes use the new ID types
  • maxTransforms security option (nullable to explicitly disable limit)
  • Algorithm URIs replace legacy string-only options
  • Allowed algorithm maps provide runtime security controls
  • getCertFromKeyInfo now uses KeySelectorFunction type

All past review comments regarding type consistency have been addressed.


187-223: LGTM: Reference interface updates.

The transforms and digestAlgorithm fields now use the appropriate URI types (TransformAlgorithmURI and HashAlgorithmURI), aligning with the new algorithm abstraction system while maintaining backward compatibility.


283-295: LGTM: KeySelector type variants.

The discriminated union of CertificateKeySelector and KeyInfoKeySelector provides 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:

  • maxTransforms prevents DoS via excessive transform complexity
  • checkCertExpiration validates certificate validity
  • truststore enables certificate chain validation
  • Algorithm maps restrict allowed algorithms

JSDoc is clear about defaults and purpose. Past review comment about canonicalizationAlgorithms type has been addressed (now uses CanonicalizationAlgorithmMap).


349-385: LGTM: XmlDSigVerifierOptions interface.

Well-structured options for the verifier:

  • keySelector is required (discriminated union ensures proper key configuration)
  • idAttributes for custom ID resolution
  • implicitTransforms for profile-specific transforms
  • throwOnError for error handling preference
  • security for optional hardening

JSDoc properly documents each field with examples and defaults. Past review comment about implicitTransforms type has been addressed (now uses TransformAlgorithmURI).


387-408: LGTM: Verification result types.

The discriminated union of SuccessfulXmlDsigVerificationResult and FailedXmlDsigVerificationResult provides type-safe result handling. The success discriminator 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_URIS to 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.idAttributes

The constructor option idAttributes is incorrectly typed as VerificationIdAttributeType[], which excludes the prefix property. However, the JSDoc example explicitly shows { prefix: "wsu", localName: "Id", namespaceUri: "..." }, and the runtime code in ensureHasId (lines 1426–1436) checks for and uses the prefix property 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.

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between e9a1db7 and 89b0395.

📒 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.ts
  • src/signed-xml.ts
  • src/types.ts
  • 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:

  • test/xmldsig-verifier.spec.ts
  • src/signed-xml.ts
  • src/types.ts
  • src/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.ts
  • src/signed-xml.ts
  • 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: 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
  • src/types.ts
  • 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/signed-xml.ts
  • src/types.ts
  • 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/signed-xml.ts
  • src/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)

@cjbarth
Copy link
Contributor

cjbarth commented Nov 15, 2025

@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.

@shunkica shunkica changed the title Add XmlDSigValidator wrapper around SignedXml Add XmlDSigVerifier wrapper for SignedXml Dec 2, 2025
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)
@shunkica shunkica force-pushed the signer-validator-separation branch from 8291181 to 5200d71 Compare December 2, 2025 22:25
@shunkica
Copy link
Contributor Author

shunkica commented Dec 2, 2025

@cjbarth Let me know what you think.
I re-exported the "old" types to avoid making any breaking changes, but I suppose even updating xpath to 0.3.4 could be considered a breaking change since it handles some queries differently than 0.3.3.
Should I just scrap my attempt to avoid breaking changes and target 7.0?

Copy link

@coderabbitai coderabbitai bot left a 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 from xpath.map to xpath.forEach.

The function parameter is named xpath which shadows the imported xpath module. The call xpath.forEach(...) iterates over the xpath array 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 simplifying process’s options handling

The move to CanonicalizationAlgorithm, TransformAlgorithmOptions, and XMLDSIG_URIS (including NAMESPACES.xmlns and the EXCLUSIVE_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 getCanonXml calls transforms and avoids mutating the parameter.

Also applies to: 270-322, 324-337

src/xmldsig-verifier.ts (1)

169-227: Clarify whether maxTransforms is ever allowed to be “unlimited”

resolveOptions() currently does:

const defaults = { maxTransforms: XmlDSigVerifier.DEFAULT_MAX_TRANSFORMS, ... };

const baseSecurity = {
  maxTransforms: options.security?.maxTransforms ?? defaults.maxTransforms,
  ...
};

Using ?? means maxTransforms: null (if that were ever passed) is treated the same as undefined and 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 in addReference may be a behavior change

The 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 transforms before pushing the reference.


1406-1456: Be defensive when idAttributes[0] includes a prefix but no namespaceUri

ensureHasId() now uses the first configured idAttribute to 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 prefix is provided, namespaceUri is a non-empty string. A misconfigured idAttributes entry with prefix set but namespaceUri missing/undefined would produce odd xmlns: and attribute namespaces.

Since this is configuration-driven, it might be worth either:

  • Validating idAttributes in the constructor (e.g., throw if prefix is set but namespaceUri is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8291181 and 5200d71.

⛔ Files ignored due to path filters (10)
  • package-lock.json is excluded by !**/package-lock.json
  • test/static/chain_client.crt.pem is excluded by !**/*.pem
  • test/static/chain_client.key.pem is excluded by !**/*.pem
  • test/static/chain_root.crt.pem is excluded by !**/*.pem
  • test/static/chain_root.key.pem is excluded by !**/*.pem
  • test/static/expired_certificate.crt.pem is excluded by !**/*.pem
  • test/static/expired_certificate.key.pem is excluded by !**/*.pem
  • test/static/future_certificate.crt.pem is excluded by !**/*.pem
  • test/static/future_certificate.csr.pem is excluded by !**/*.pem
  • test/static/future_certificate.key.pem is 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.ts
  • test/signature-unit-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/xmldsig-verifier.spec.ts
  • test/signature-integration-tests.spec.ts
  • test/document-tests.spec.ts
  • README.md
  • src/xmldsig-verifier.ts
  • src/exclusive-canonicalization.ts
  • src/c14n-canonicalization.ts
  • src/signed-xml.ts
  • test/c14nWithComments-unit-tests.spec.ts
  • src/enveloped-signature.ts
  • src/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.ts
  • test/signature-unit-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • test/document-tests.spec.ts
  • README.md
  • src/xmldsig-verifier.ts
  • src/exclusive-canonicalization.ts
  • src/c14n-canonicalization.ts
  • src/signed-xml.ts
  • test/c14nWithComments-unit-tests.spec.ts
  • src/enveloped-signature.ts
  • src/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.ts
  • test/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cs
  • test/signature-unit-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • test/document-tests.spec.ts
  • README.md
  • src/signed-xml.ts
  • test/c14nWithComments-unit-tests.spec.ts
  • src/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.ts
  • test/signature-unit-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • test/document-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/exclusive-canonicalization.ts
  • src/signed-xml.ts
  • test/c14nWithComments-unit-tests.spec.ts
  • src/enveloped-signature.ts
  • src/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.ts
  • test/signature-unit-tests.spec.ts
  • test/saml-response-tests.spec.ts
  • test/signature-integration-tests.spec.ts
  • src/xmldsig-verifier.ts
  • src/signed-xml.ts
  • test/c14nWithComments-unit-tests.spec.ts
  • src/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.ts
  • test/signature-integration-tests.spec.ts
  • README.md
  • src/xmldsig-verifier.ts
  • src/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.ts
  • README.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_URIS provides clear access to the URI constants throughout the type definitions.


17-25: Well-designed discriminated union for ID attribute types.

The separation of SignatureIdAttributeType and VerificationIdAttributeType correctly distinguishes signing requirements (prefix mandatory when using object form with namespace) from verification (prefix not needed). The use of null for 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 XmlDSigVerifierSecurityOptions interface provides fine-grained control over allowed algorithms and transforms, enabling defense-in-depth against algorithm substitution attacks. The JSDoc references to SignedXml defaults are helpful for understanding default behavior.


387-402: Good separation of KeyInfo-specific security options.

The KeyInfoXmlDSigSecurityOptions appropriately adds certificate expiration checking and truststore validation only for the KeyInfoKeySelector path where these features are meaningful.


459-477: Well-designed result types using discriminated unions.

The SuccessfulXmlDsigVerificationResult and FailedXmlDsigVerificationResult types provide type-safe result handling with mutually exclusive properties (signedReferences vs error), 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_URIS from 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 referenced XMLDSIG_VERIFIER.md file exists and contains detailed usage instructions.

The example clearly demonstrates the simplified verification workflow with keySelector and result handling. However, the README references XMLDSIG_VERIFIER.md for 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_URIS and 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) and undefined (any namespace) enables precise control over ID attribute matching, which is important for the new idAttributes security option with namespaceUri: null.


16-29: LGTM! Consistent null handling in implicit attribute matching.

The same null-handling pattern is correctly applied here, ensuring that when namespace is explicitly null, only attributes without a namespace URI will match.


31-35: LGTM! Updated signature with proper type for namespace parameter.

The string | null | undefined type 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_SIGNATURE and XMLDSIG_URIS.CANONICALIZATION_ALGORITHMS.EXCLUSIVE_C14N improves 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 createSignedXml helper reduces duplication across tests while providing flexibility through options.


110-124: LGTM! Well-designed assertion helpers.

The expectValidResult and expectInvalidResult helpers 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 findAttr changes in src/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 throwOnError true 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, and signatureAlgorithm assignments now use the appropriate XMLDSIG_URIS constants.

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 getAlgorithmName method in the async callback test now returns SIGNATURE_ALGORITHMS.RSA_SHA1 instead of a hard-coded string.

src/enveloped-signature.ts (3)

3-8: LGTM! Updated imports and interface implementation.

The class now correctly implements TransformAlgorithm with 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 process method correctly uses TransformAlgorithmOptions, and XPath queries now reference XMLDSIG_URIS.NAMESPACES.ds for 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 CanonicalizationAlgorithm with the appropriate type imports, and XMLDSIG_URIS is imported for URI constants.

Also applies to: 12-12


257-260: LGTM! Updated process method signature.

The method now uses TransformAlgorithmOptions and the JSDoc is updated accordingly.


281-283: LGTM! getAlgorithmName returns typed URI constant.

The return type CanonicalizationAlgorithmURI and the use of XMLDSIG_URIS.CANONICALIZATION_ALGORITHMS.C14N provide type safety and consistency.


295-297: LGTM! Subclass correctly overrides getAlgorithmName.

The C14nCanonicalizationWithComments class properly returns the C14N_WITH_COMMENTS URI constant.

src/xmldsig-verifier.ts (3)

75-117: Verifier API shape and SignedXml reuse look consistent with intended behavior

The split between the static verifySignature() and the instance method, plus funneling all failures through handleError, reads clean and predictable. Reusing a single SignedXml per XmlDSigVerifier instance is also fine here: loadSignature() and checkSignature() 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 in getCertFromKeyInfo

The KeyInfo path in createSignedXml() cleanly normalizes the truststore to public keys, enforces checkCertExpiration, 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 injected getCertFromKeyInfo keeps 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’s checkSignature() 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 intact

The switch to XMLDSIG_URIS-backed maps (CanonicalizationAlgorithms, HashAlgorithms, SignatureAlgorithms, TransformAlgorithms) plus the getDefault* helpers and the find*Algorithm() methods gives a much clearer, URI-driven surface while still allowing callers to override allowed algorithms via the constructor options.

enableHMAC() cleanly swaps SignatureAlgorithms to the symmetric-only map and disables KeyInfo content, which is consistent with the security comment about avoiding key confusion. The findSignatureAlgorithm / findHashAlgorithm / findCanonicalizationAlgorithm implementations 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: idAttributes and namespace handling now properly distinguish string, undefined, and null cases

The introduction of idAttributes: IdAttributeType[] with getDefaultIdAttributes() and the updated logic in both validateElementAgainstReferences() and validateReference() correctly supports:

  • Simple string attributes (match by local-name in any namespace),
  • Object form with namespaceUri as 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/null that could yield namespace-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 via getAlgorithmName(), and the InclusiveNamespaces element’s xmlns is 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants