Skip to content

feat(xmldsig): add XML mutation strategy#77

Merged
polaz merged 6 commits into
mainfrom
feat/#76-xml-mutation-strategy
Jul 7, 2026
Merged

feat(xmldsig): add XML mutation strategy#77
polaz merged 6 commits into
mainfrom
feat/#76-xml-mutation-strategy

Conversation

@polaz

@polaz polaz commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Add XMLDSig mutation helpers for appending Signature templates and filling DigestValue/SignatureValue content.
  • Update RustCrypto dependencies, including p256 0.14, RSA 0.10 RC, SHA 0.11, and matching curve crates.
  • Keep namespace-aware replacement fail-closed and preserve foreign same-local-name elements.

Testing

  • cargo fmt --all -- --check
  • cargo check --all-features
  • cargo clippy --all-features --all-targets -- -D warnings
  • cargo test --doc --all-features
  • cargo nextest run --all-features: 520/520 passed

Closes #76

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5de54be4-8cad-46cc-8e9f-e898edabd502

📥 Commits

Reviewing files that changed from the base of the PR and between b74b032 and 6162dc2.

📒 Files selected for processing (1)
  • src/xmldsig/mutation.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for preparing XML signatures by inserting signature content into documents and filling digest/signature values in the correct order.
    • Expanded cryptography support with updated dependency versions and an additional optional crypto component.
  • Bug Fixes

    • Improved RSA public-key handling for better compatibility during signature verification.
    • Updated ECDSA verification to work directly on the original signed content for more reliable P-256 and P-384 signature checks.

Walkthrough

Adds XMLDSig mutation helpers, updates crypto dependencies, switches RSAKeyValue parsing to crypto-bigint, and changes ECDSA P-256/P-384 verification to use raw signed data.

Changes

XML mutation and crypto updates

Layer / File(s) Summary
Dependency updates for crypto crates
Cargo.toml
Updates rsa, sha1, sha2, p256, p384, and p521 versions and adds optional crypto-bigint to the xmldsig feature.
RSAKeyValue parsing via crypto-bigint
src/xmldsig/keys.rs
Replaces RSA modulus/exponent parsing with crypto_bigint::BoxedUint and updates RSAKeyValue test construction to use a shared helper.
XML mutation module for XMLDSig signing
src/xmldsig/mod.rs, src/xmldsig/mutation.rs
Adds the mutation submodule, error type, append/fill helpers, namespace-aware replacement logic, and tests for signature insertion and DSig value filling.
ECDSA verification switched to raw-data mode
src/xmldsig/signature.rs
Changes P-256 and P-384 verification helpers to verify raw signed_data directly and updates the related imports and call sites.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant append_signature_to_root
  participant fill_digest_values
  participant fill_signature_values
  participant roxmltree
  participant quick_xml

  Caller->>append_signature_to_root: xml, signature_template
  append_signature_to_root->>roxmltree: validate template root
  append_signature_to_root->>quick_xml: stream and append template
  append_signature_to_root->>roxmltree: re-parse output
  append_signature_to_root-->>Caller: mutated XML

  Caller->>fill_digest_values: xml, values
  fill_digest_values->>roxmltree: count DigestValue elements
  fill_digest_values->>quick_xml: replace matching text
  fill_digest_values->>roxmltree: validate output
  fill_digest_values-->>Caller: mutated XML

  Caller->>fill_signature_values: xml, values
  fill_signature_values->>roxmltree: count SignatureValue elements
  fill_signature_values->>quick_xml: replace matching text
  fill_signature_values->>roxmltree: validate output
  fill_signature_values-->>Caller: mutated XML
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main XML mutation strategy change in the PR.
Description check ✅ Passed The description matches the XMLDSig mutation helpers, dependency updates, and validation work in this PR.
Linked Issues check ✅ Passed The PR implements the XMLDSig mutation layer, fail-closed replacement, parse validation, tests, and dependency updates required by #76.
Out of Scope Changes check ✅ Passed No clear unrelated changes stand out; the RSA/ECDSA updates appear tied to the XMLDSig dependency and verification work.
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/#76-xml-mutation-strategy

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.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds XMLDSig XML mutation helpers and updates the RustCrypto integration. The main changes are:

  • New helpers to append generated Signature templates to the document root.
  • New helpers to fill XMLDSig DigestValue and SignatureValue elements in document order.
  • Namespace-aware replacement that leaves foreign same-local-name elements unchanged.
  • RustCrypto dependency updates with matching RSA, ECDSA, SHA, and key encoding API changes.

Confidence Score: 5/5

Safe to merge with low risk.

No concrete functional or security issues were identified in the changed files. The XML mutation helpers include namespace-aware matching and fail-closed value-count checks. The RustCrypto API updates align with the changed dependency versions.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • Ran cargo check --all-features and confirmed it completed with exit code 0.
  • Verified that 9 XMLDSig mutation tests passed and the tests exited with code 0, while the shell wrapper reported exit code 2 due to a PIPESTATUS limitation after the Cargo run.
  • Observed that the initial integration command failed before execution with a Cargo CLI usage error about unexpected extra test-name arguments.
  • Confirmed the equivalent Cargo integration-target invocation passed, reporting 16 ECDSA, 12 RSA, and 23 signature pipeline tests.
  • Noted rustfmt is unavailable in the sandbox, as shown in the cargo_fmt_check.log, affecting the formatting check.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
Cargo.toml Updated RustCrypto dependency versions and added crypto-bigint to the xmldsig feature.
src/xmldsig/keys.rs Adapted RSA KeyValue SPKI construction and tests for crypto-bigint::BoxedUint with trimmed byte encoding.
src/xmldsig/mod.rs Exposes the new XMLDSig mutation module.
src/xmldsig/mutation.rs Adds namespace-aware XMLDSig mutation helpers with fail-closed count checks and focused edge-case tests.
src/xmldsig/signature.rs Migrates ECDSA verification calls to the updated RustCrypto verifier APIs while preserving signature encoding behavior.

Reviews (3): Last reviewed commit: "fix(xmldsig): fail closed on skipped val..." | Re-trigger Greptile

@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 `@src/xmldsig/mutation.rs`:
- Around line 155-165: In the replacing_depth handling inside mutation logic,
Event::Empty is being counted as nested content even though it has no matching
end event. Update the event match in the replacing state so only Event::Start(_)
increments depth, while Event::Empty(_) is left uncounted, preserving correct
recognition of the target element’s real Event::End and avoiding dropping it
from the output.
🪄 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: ff7d1c1a-9c88-4ab9-99fe-4e604b069e08

📥 Commits

Reviewing files that changed from the base of the PR and between d9e5e88 and 34ee90c.

📒 Files selected for processing (5)
  • Cargo.toml
  • src/xmldsig/keys.rs
  • src/xmldsig/mod.rs
  • src/xmldsig/mutation.rs
  • src/xmldsig/signature.rs

Comment thread src/xmldsig/mutation.rs
@polaz

polaz commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 6, 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.

Caution

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

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

155-195: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fail closed when the streaming pass consumes fewer values than were counted.

count_dsig_elements counts descendant matches, but once the stream enters a matching target, nested matching elements are skipped as replacement content. A parseable nested target can therefore satisfy the upfront count with two values while only writing one and still returning Ok. Add a post-loop value_index check before returning.

Suggested guard
     loop {
         let (namespace, event) = reader.read_resolved_event_into(&mut buf)?;
@@
         }
         buf.clear();
     }
 
+    if value_index != values.len() {
+        return Err(XmlMutationError::ValueCountMismatch {
+            element: local_name,
+            expected,
+            actual: value_index,
+        });
+    }
+
     let output = String::from_utf8(writer.into_inner())?;
     roxmltree::Document::parse(&output)?;
     Ok(output)
🤖 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/mutation.rs` around lines 155 - 195, Ensure the streaming
replacement in mutation.rs fails closed when fewer DSIG values are actually
consumed than `count_dsig_elements` predicted. In the `replace_dsig_values`
flow, after the event loop and before parsing/returning the output, compare
`value_index` against the number of counted values and return an error if not
all values were written, since nested matching elements can be skipped while
still being counted upfront.
🤖 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/mutation.rs`:
- Around line 155-195: Ensure the streaming replacement in mutation.rs fails
closed when fewer DSIG values are actually consumed than `count_dsig_elements`
predicted. In the `replace_dsig_values` flow, after the event loop and before
parsing/returning the output, compare `value_index` against the number of
counted values and return an error if not all values were written, since nested
matching elements can be skipped while still being counted upfront.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0c04e30c-c7c6-4012-a023-cff86ade7840

📥 Commits

Reviewing files that changed from the base of the PR and between 34ee90c and b74b032.

📒 Files selected for processing (1)
  • src/xmldsig/mutation.rs

@polaz

polaz commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai Re: outside-diff finding on src/xmldsig/mutation.rs value replacement. Fixed — fill_dsig_values now checks value_index after the streaming pass and fails closed with ValueCountMismatch if nested matching XMLDSig values were counted but skipped as replacement content. Added a regression test for nested DigestValue input. Validation passed: focused nextest, fmt, check, clippy, doctests, full nextest 520/520.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd mutation.rs src/xmldsig | xargs cat -n | sed -n '1,400p'

Length of output: 17790


Confirmed — the fix at Lines 193–199 correctly re-checks value_index against values.len() after the streaming pass, so nested XMLDSig elements that get skipped during the "replacing" state (Lines 155–168) now trigger a fail-closed ValueCountMismatch instead of silently succeeding. The new replacement_fails_when_nested_dsig_values_are_skipped test (Lines 382–395) exercises exactly this case and matches the expected expected: 2, actual: 1 outcome. This looks correct and well-covered.

Thanks for the detailed validation summary, @polaz.

@polaz

polaz commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 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.

@polaz polaz merged commit 4cece9e into main Jul 7, 2026
10 checks passed
@polaz polaz deleted the feat/#76-xml-mutation-strategy branch July 7, 2026 09:55
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: implement XMLDSig mutation strategy

1 participant