Skip to content

Fix duplicate attribute when the Body redeclares an Envelope namespace prefix #1196 - #1197

Open
falc0r wants to merge 2 commits into
DigDes:developfrom
falc0r:fix/1196-duplicate-namespace-attribute
Open

falc0r wants to merge 2 commits into
DigDes:developfrom
falc0r:fix/1196-duplicate-namespace-attribute

Conversation

@falc0r

@falc0r falc0r commented Sep 18, 2026

Copy link
Copy Markdown

Problem

ParsedMessage.ExtractSoapBody throws InvalidOperationException: Duplicate attribute. when an envelope declares a namespace prefix on both <Envelope> and <Body>. The exception is thrown while the message is being read, so the operation is never reached and the caller gets a 500.

This is legal XML: a descendant element may redeclare a prefix, and redeclaring it to the same URI is redundant rather than invalid. Some SOAP toolkits emit this shape routinely, so a service can start failing on traffic that has not changed.

Fixes #1196.

Cause

var rootAttributes = root.Attributes();
var bodyAttributes = bodyNode.Attributes();
var allAttributes = bodyAttributes.Union(rootAttributes);
bodyNode.ReplaceAttributes(allAttributes);

XAttribute does not override Equals, so Union compares by reference. A declaration appearing on both elements is two distinct objects, both survive the union, and ReplaceAttributes then adds two attributes with the same name.

Change

Take the body's own attributes, then only those envelope attributes whose name the body does not already carry.

This also corrects precedence. When <Body> rebinds a prefix to a different URI than <Envelope>, the body's binding has to win for the body subtree. Union kept both attributes, so that case threw as well, and merging the other way round would have resolved names against the wrong namespace.

ToList forces both sequences to be evaluated while the body still has its attributes. ReplaceAttributes does snapshot its content before removing anything, so this is not strictly required, but it makes the ordering explicit rather than depending on that detail.

Tests

Two tests added to RawRequestSoap12Tests. Both fail on develop and pass with this change.

Soap12PingWithNamespaceDeclarationsRepeatedOnBody posts the existing Ping payload with the envelope's three prefixes repeated, unchanged, on <soap12:Body>. This is the reported failure.

Soap12PingWithPrefixShadowedOnBody covers the precedence half:

<soap12:Envelope xmlns:tns="http://example.org/not-tempuri">
  <soap12:Body xmlns:tns="http://tempuri.org/">
    <tns:Ping>

tns is bound to a decoy URI on the envelope and to the service namespace on the body, and the operation element uses the prefix. The request can only dispatch if the body's binding wins; if the envelope's binding won, tns:Ping would resolve to http://example.org/not-tempuri and the operation would not be found.

The full suite passes: 295 tests, 0 failures.

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.

ParsedMessage.ExtractSoapBody throws "Duplicate attribute" when Body redeclares a prefix already declared on Envelope

1 participant