Skip to content

harden(winrm): strict, namespace-correct XML rejection (Codex round)#39

Merged
irvingouj@Devolutions (irvingoujAtDevolution) merged 6 commits into
masterfrom
stack/xml-fromxml-strict
Jun 25, 2026
Merged

harden(winrm): strict, namespace-correct XML rejection (Codex round)#39
irvingouj@Devolutions (irvingoujAtDevolution) merged 6 commits into
masterfrom
stack/xml-fromxml-strict

Conversation

@irvingoujAtDevolution

Copy link
Copy Markdown
Collaborator

Part 7/7. Base: stack/xml-fromxml-tests (#38).

Addresses every finding from the independent Codex review pass — reject malformed/untrusted input uniformly instead of silently absorbing it:

  • Attributes matched by (namespace-URI, local-name); a parse error on a known attribute is propagated, not dropped (cores/attribute.rs, cores/tag.rs).
  • <Selector>/<Option> require an unqualified Name attribute; missing → error (ws_management/header.rs).
  • SoapEnvelope::from_xml validates the root really is {SOAP}Envelope before reading children — the value parser alone would accept any wrapper carrying a Body (soap/mod.rs).
  • Mixed content (non-whitespace text in an element container) is rejected, via a shared reject_mixed_content helper applied in the derive and every hand-written container (ironposh-xml/mapping.rs).
  • leaf_text now tolerates comments/PIs and concatenates text runs, so a comment can neither be rejected nor silently truncate the value (cores/tag_value.rs).

Negative tests added for each. Verified: fmt + clippy -D warnings + workspace tests clean; real-server matrix 10/10 and command matrix green (the new strictness does not reject real WinRM responses).

FromXml stack (review/merge bottom-up):

  1. core + winrm migration (feat(xml): namespace-correct FromXml core + winrm migration #33)
  2. nested-tag descend fix (fix(winrm): descend into child for a tag whose value is a tag #34)
  3. delete dead AnyTag/TagList + markers (refactor(winrm): delete dead AnyTag/TagList and stale tag markers #35)
  4. tag! macro + aliases (refactor(winrm): alias-based tags via tag! macro #36)
  5. harden / reject malformed XML (harden(winrm): reject malformed XML in the FromXml layer #37)
  6. tests (test: negative FromXml rejection tests + command-not-found e2e #38)
  7. strict rejection — Codex round (this PR)

Pin the rejection behavior the hardening round added: mixed-content leaf
rejection, Empty non-empty rejection, single-child descend (zero/multiple/
wrong-name), duplicate singleton (Command/CommandState/derived ExitCode),
and duplicate selector/option keys.
A non-existent command surfaces as an error record (non-fatal), printed via
render_concise to stdout while the process still exits 0. Asserts both, across
all auth methods.
Polish from the independent review pass (no behavior change except a clearer
error message): remove dead commented-out visitor block in ws_addressing;
correct the tag! marker-visibility doc; drop two what/how comments; loosen the
paste version pin; clearer 'found none' diagnostic for an empty tag wrapper;
restore optional-permutation derive tests and add mixed-content rejection tests
for numeric/uuid leaves.
Address the independent Codex pass — reject malformed input uniformly rather
than silently absorbing it:

- attributes matched by (namespace-URI, local-name) with parse errors on known
  attributes propagated instead of dropped (cores/attribute.rs, cores/tag.rs)
- <Selector>/<Option> require an unqualified Name attribute (ws_management/header.rs)
- SoapEnvelope::from_xml validates the root really is {SOAP}Envelope before
  reading children (soap/mod.rs)
- reject mixed content (non-whitespace text) in element containers, applied in
  the derive and every hand-written container (ironposh-xml mapping helper)
- leaf_text tolerates comments/PIs and concatenates text runs so a comment can
  neither be rejected nor truncate the value (cores/tag_value.rs)

Adds negative tests for each.
Two issues the strict pass exposed:
- xs:boolean attributes (mustUnderstand/MustComply/End/EndUnit) now accept the
  1/0 forms, not just true/false, so propagating parse errors can't reject a
  spec-compliant <s:mustUnderstand="1"> response.
- xml:lang is modeled literally as "xml:lang" (the reserved prefix is never
  declared); fold roxmltree's expanded (xml-namespace, "lang") form back to
  that spelling so it matches on input instead of being dropped.
SOAP 1.2 permits multiple <Text xml:lang> entries in a fault <Reason>; the
derived singleton rejected them as duplicates. Hand-write FromXml to keep the
first and ignore the rest.
@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) changed the base branch from stack/xml-fromxml-tests to master June 25, 2026 18:52
@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) deleted the stack/xml-fromxml-strict branch June 25, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant