Skip to content

feat(xmldsig): add signature template builders#74

Open
polaz wants to merge 8 commits into
mainfrom
feat/#73-signature-builders
Open

feat(xmldsig): add signature template builders#74
polaz wants to merge 8 commits into
mainfrom
feat/#73-signature-builders

Conversation

@polaz

@polaz polaz commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

  • add deterministic SignatureBuilder and ReferenceBuilder APIs for XMLDSig templates
  • preserve reference/transform order and serialize namespace-correct XML with quick-xml
  • reject missing references, invalid namespace prefixes and IDs, and verify-only SHA-1 signing algorithms
  • harden XML NCName validation against markup injection while accepting valid Unicode names
  • update compatible Rust dependencies (quick-xml 0.38, time 0.3.53, num-bigint 0.4.7 resolution)

Testing

  • cargo fmt --all -- --check
  • cargo check --all-features
  • cargo clippy --all-features --all-targets -- -D warnings
  • cargo nextest run --all-features (511 passed)
  • cargo test --doc --all-features (3 passed)

Closes #73

polaz added 2 commits July 3, 2026 14:38
- Add quick-xml 0.38 for deterministic XML output
- Raise the time dev-dependency floor to 0.3.53
- Resolve num-bigint 0.4.7 through the updated dependency graph
- Add deterministic SignatureBuilder and ReferenceBuilder APIs
- Validate signing algorithms, namespace prefixes, and required references
- Cover XML structure, escaping, transform order, and error paths

Closes #73
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for generating XMLDSig signature templates, including configurable references, transforms, namespace prefixes, optional KeyInfo, and deterministic output.
  • Bug Fixes

    • Improved validation for signature and reference identifiers, namespace prefixes, and required references before template generation.
    • Added support for safer algorithm handling and clearer error reporting when a template can’t be built.
  • Documentation

    • Updated the README to reflect the current XML and cryptography dependencies used by the project.

Walkthrough

This PR adds a new xmldsig::builder module for constructing deterministic XMLDSig <Signature> templates, wires the types into the public API, updates the crate dependency stack, and adds integration tests covering serialization and validation.

Changes

XMLDSig signature builder

Layer / File(s) Summary
Dependency updates
Cargo.toml, README.md
Adds quick-xml as a runtime dependency, updates time to 0.3.53 for dev builds, and revises the README implementation stack description.
Constants, error type and ReferenceBuilder
src/xmldsig/builder.rs
Adds module constants, SignatureBuilderError, and ReferenceBuilder with optional reference metadata plus ordered transform support.
SignatureBuilder core and validation
src/xmldsig/builder.rs
Implements SignatureBuilder state, validation, and full template assembly for namespaces, SignedInfo, SignatureValue, optional KeyInfo, and references.
Reference and transform serialization
src/xmldsig/builder.rs
Serializes Reference and Transform elements and adds XML writing helpers plus NCName and namespace-prefix validation helpers.
Public API exports
src/xmldsig/mod.rs
Exposes the new builder module and re-exports ReferenceBuilder, SignatureBuilder, and SignatureBuilderError.
Integration tests for template correctness
tests/signature_builder.rs
Adds tests for ordering, namespace handling, transform serialization, invalid inputs, Unicode prefixes, and Id validation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding XMLDSig signature template builders.
Description check ✅ Passed The description matches the implemented builder APIs, validation, tests, and dependency updates.
Linked Issues check ✅ Passed The PR delivers the requested builders, ordering/validation rules, tests, and compatible dependency updates for issue #73.
Out of Scope Changes check ✅ Passed The README and dependency updates align with the XMLDSig builder work and no unrelated changes are evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#73-signature-builders

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.

Describe the current RustCrypto stack and quick-xml writer instead of the removed ring dependency.
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds XMLDSig template builders for signatures and references. The main changes are:

  • New SignatureBuilder and ReferenceBuilder APIs.
  • Deterministic XML serialization with quick-xml.
  • Validation for references, signing algorithms, namespace prefixes, and IDs.
  • Tests for ordering, XPath transforms, Unicode prefixes, and invalid IDs.
  • README and dependency updates for the new writer path.

Confidence Score: 5/5

This looks safe to merge.

No blocking issues found in the changed code.

No files need attention.

T-Rex T-Rex Logs

What T-Rex did

  • During the initial base build, the compile failed due to unresolved imports for ReferenceBuilder and SignatureBuilder, exiting with code 101.
  • After updating to head, the codebase builds and runs, emitting the full prefixed/default XML, preserving order and namespace-correct structure, and completing all runtime assertions with exit code 0.
  • Before running the trex_signature_builder_validation tests on the base branch, cargo test failed at compile time with unresolved imports and missing public APIs like SignatureBuilder and SignatureBuilderError, indicating the public builder APIs were absent.
  • After applying changes on head, the same test command now exits 0 and the output shows the exact rejected variants/messages and the generated parsed Unicode-prefix XML.
  • Previously the base Cargo.toml specified time = "0.3", lacked quick-xml, and the README relied on roxmltree, ring, and x509-parser; cargo tree -i quick-xml failed due to a missing package.
  • Now the head Cargo.toml lists quick-xml = "0.38" and time = "0.3.53", with metadata downloaded and locked as quick-xml v0.38.4 and time v0.3.53.
  • Additionally, cargo tree -i quick-xml now shows quick-xml v0.38.4 depending on xml-sec, and cargo check --all-features completes successfully with time v0.3.53 present.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/xmldsig/builder.rs Adds the XMLDSig template builder implementation and validation logic.
src/xmldsig/mod.rs Exports the new builder module and public builder types.
tests/signature_builder.rs Adds tests for generated template structure, transform round-tripping, Unicode prefixes, and invalid IDs.
Cargo.toml Adds quick-xml and updates the time dev-dependency.
README.md Updates the project description to include the XML writer and crypto stack.

Reviews (3): Last reviewed commit: "style(xmldsig): format NCName regression..." | Re-trigger Greptile

Comment thread src/xmldsig/builder.rs Outdated
Comment thread src/xmldsig/builder.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/xmldsig/builder.rs`:
- Line 16: The XPath transform in the XML signature builder uses the ds prefix
without guaranteeing it is bound in scope. Update the XPath emission in the
builder around XPATH_EXCLUDE_ALL_SIGNATURES so the generated <XPath> element
includes a local xmlns:ds binding, or uses the same configured prefix emitted by
ns_prefix("ds"), so not(ancestor-or-self::ds:Signature) always resolves
correctly.

In `@tests/signature_builder.rs`:
- Around line 16-29: Reject non-NCName values for both
SignatureBuilder::signature_id and ReferenceBuilder::id before build_template()
emits XML. Add the same NCName validation already used for ns_prefix into
SignatureBuilder::validate() (or the build path it calls) and ensure the check
covers Signature/@Id and each Reference/@Id so values like sig&1 are rejected
early with a clear error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9159d5d0-6ccc-4c1b-ac0a-3a6b651d496e

📥 Commits

Reviewing files that changed from the base of the PR and between 965d549 and 4122fe4.

📒 Files selected for processing (5)
  • Cargo.toml
  • README.md
  • src/xmldsig/builder.rs
  • src/xmldsig/mod.rs
  • tests/signature_builder.rs

Comment thread src/xmldsig/builder.rs Outdated
Comment thread tests/signature_builder.rs
polaz added 2 commits July 3, 2026 22:08
- Reproduce XPath transform parser incompatibility
- Cover Unicode NCName prefixes
- Require NCName validation for Signature and Reference IDs
- Bind the parser-compatible dsig prefix for XPath transforms
- Accept Unicode XML namespace prefixes
- Reject schema-invalid Signature and Reference IDs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/xmldsig/builder.rs (1)

192-233: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Tighten is_ncname to reject non-round-tripping inputs. Document::parse("<{value}/>") accepts strings that only parse because they introduce comment/PI markup before the root, so malformed Signature/@Id and Reference/@Id values can pass validation. Since quick-xml writes attribute values verbatim, those inputs produce invalid XML.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/xmldsig/builder.rs` around lines 192 - 233, The `validate` method in
`SignatureBuilder` only checks `Signature/@Id` and `Reference/@Id` with
`is_ncname`, which lets through values that parse in `Document::parse` but do
not round-trip safely when serialized by quick-xml. Update the ID validation
used in `validate` so it rejects inputs that would not serialize back as a valid
XML attribute value, and apply the stricter check consistently to both
`signature_id` and each `reference.id` by using the existing
`SignatureBuilderError::InvalidId` paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/xmldsig/builder.rs`:
- Around line 192-233: The `validate` method in `SignatureBuilder` only checks
`Signature/@Id` and `Reference/@Id` with `is_ncname`, which lets through values
that parse in `Document::parse` but do not round-trip safely when serialized by
quick-xml. Update the ID validation used in `validate` so it rejects inputs that
would not serialize back as a valid XML attribute value, and apply the stricter
check consistently to both `signature_id` and each `reference.id` by using the
existing `SignatureBuilderError::InvalidId` paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8c96f6c4-a1ee-45fc-abd4-4a32cedfc5d5

📥 Commits

Reviewing files that changed from the base of the PR and between 4122fe4 and 8797e7a.

📒 Files selected for processing (2)
  • src/xmldsig/builder.rs
  • tests/signature_builder.rs

polaz added 3 commits July 4, 2026 02:39
Reproduce parser-based validation accepting comment and processing-instruction markup for Signature and Reference IDs.
Require parser-based XML names to round-trip exactly, preventing comment or processing-instruction markup from passing Signature and Reference ID validation.
@polaz

polaz commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai Re: NCName round-trip validation outside-diff finding — fixed in ee25df4. The parsed root local name must now equal the original input exactly, preventing comment/PI markup injection for both Signature/@id and Reference/@id. Regression coverage reproduces both bypass forms; full suite passes 511/511.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cd /tmp && rm -rf repo && git clone --quiet https://github.com/structured-world/xml-sec.git repo && cd repo
git log --oneline -1 ee25df4 2>/dev/null || echo "commit not found"
git show ee25df4 --stat
echo "---diff---"
git show ee25df4 -- src/xmldsig/builder.rs

Length of output: 1980


@polaz Confirmed — commit ee25df4 addresses this correctly. The updated is_ncname now checks that document.root_element().tag_name().name() == value, so any parser-based construct like foo/><!--x or foo?><?pi? that would otherwise parse successfully but produce a mismatched root local name is now rejected. This closes the comment/PI injection bypass for both Signature/@Id and Reference/@Id validation. Nice fix, and thanks for the regression coverage.

@polaz

polaz commented Jul 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/signature_builder.rs`:
- Around line 199-213: The current invalid ID payloads in SignatureBuilder tests
fail the initial NCName character check instead of exercising the parser
round-trip path. Update the cases in
SignatureBuilder::new(...).signature_id(...) and
ReferenceBuilder::new(...).id(...) to use payloads that start as valid NCNames
but cross the boundary later, so the validation in build_template() covers the
intended injection scenario.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7dc78bf3-e133-4604-bf13-4a7a41215485

📥 Commits

Reviewing files that changed from the base of the PR and between 8797e7a and dab84be.

📒 Files selected for processing (2)
  • src/xmldsig/builder.rs
  • tests/signature_builder.rs

Comment on lines +199 to +213

let injected_signature_id =
SignatureBuilder::new(exclusive_c14n(), SignatureAlgorithm::RsaSha256)
.signature_id("!--comment--><valid")
.add_reference(ReferenceBuilder::new(DigestAlgorithm::Sha256))
.build_template()
.expect_err("markup must not satisfy Signature Id validation");
assert!(injected_signature_id.to_string().contains("Signature Id"));

let injected_reference_id =
SignatureBuilder::new(exclusive_c14n(), SignatureAlgorithm::RsaSha256)
.add_reference(ReferenceBuilder::new(DigestAlgorithm::Sha256).id("?check?><valid"))
.build_template()
.expect_err("markup must not satisfy Reference Id validation");
assert!(injected_reference_id.to_string().contains("Reference Id"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "fn is_ncname" -A 30 src/xmldsig/builder.rs

Repository: structured-world/xml-sec

Length of output: 882


🏁 Script executed:

#!/bin/bash
set -euo pipefail
ast-grep outline tests/signature_builder.rs --view expanded
sed -n '160,240p' tests/signature_builder.rs | cat -n
rg -n 'foo/><!--x|foo\?>|NCName|signature_id\(|ReferenceBuilder::new\(DigestAlgorithm::Sha256\)\.id' tests/signature_builder.rs

Repository: structured-world/xml-sec

Length of output: 4228


Use payloads that cross the NCName boundary. tests/signature_builder.rs:199-213 currently uses !--comment--><valid and ?check?><valid, which fail the first-character NCName check outright. Swap them for values like foo/><!--x and foo?><?pi? so this test actually covers the parser round-trip case the fix is meant to protect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/signature_builder.rs` around lines 199 - 213, The current invalid ID
payloads in SignatureBuilder tests fail the initial NCName character check
instead of exercising the parser round-trip path. Update the cases in
SignatureBuilder::new(...).signature_id(...) and
ReferenceBuilder::new(...).id(...) to use payloads that start as valid NCNames
but cross the boundary later, so the validation in build_template() covers the
intended injection scenario.

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.

feat: add XMLDSig signature builders

1 participant