Summary
aws_replace_quote_entities() in source/s3_util.c:339 does a literal
byte match against the 6-byte string " and copies anything else
through verbatim. As a result, ETag values returned by S3-compatible
servers that emit the equivalent numeric character reference ("
decimal or " hex) end up with the literal entity text in the
response ETag header used for subsequent If-Match requests, which
then fail with HTTP 412.
Per XML 1.0 §4.6, the predefined " and the numeric reference
" are semantically identical and both forms are spec-conformant.
A receiver should accept either.
Where it bites
source/s3_auto_ranged_put.c reads the <ETag> from the
CompleteMultipartUpload response, runs it through
aws_replace_quote_entities(), and stores the result as the response's
ETag header. If the server emitted "…", the header now
literally contains the seven characters ", and any follow-up
conditional request (If-Match, CopyObject source If-Match, etc.)
fails to match.
Repro
Any server using Go's encoding/xml exhibits this — the stdlib
escapeText hardcodes " → " (numeric refs are used uniformly
to keep escaping context-independent across attributes vs element
content). Trivial repro:
xml.NewEncoder(os.Stdout).Encode(struct{ ETag string }{ETag: `"abc-1"`})
// <ETag>"abc-1"</ETag>
Pointing aws-c-s3 (or anything CRT-based — mountpoint-s3, AWS CLI v2
with CRT) at such a server, doing a multipart upload, and then issuing
a request with If-Match: <returned ETag> reproduces the 412.
Suggested fix
Two options:
- Minimal: extend the recognized set in
aws_replace_quote_entities() to include " and " (and
ideally '/'/' for symmetry).
- Better: replace the ad-hoc string match with a proper XML
entity decoder pass (the five predefined entities + numeric
character references). The function only runs on already-extracted
XML text, so a real decoder is appropriate.
Summary
aws_replace_quote_entities()insource/s3_util.c:339does a literalbyte match against the 6-byte string
"and copies anything elsethrough verbatim. As a result, ETag values returned by S3-compatible
servers that emit the equivalent numeric character reference (
"decimal or
"hex) end up with the literal entity text in theresponse ETag header used for subsequent
If-Matchrequests, whichthen fail with HTTP 412.
Per XML 1.0 §4.6, the predefined
"and the numeric reference"are semantically identical and both forms are spec-conformant.A receiver should accept either.
Where it bites
source/s3_auto_ranged_put.creads the<ETag>from theCompleteMultipartUploadresponse, runs it throughaws_replace_quote_entities(), and stores the result as the response'sETagheader. If the server emitted"…", the header nowliterally contains the seven characters
", and any follow-upconditional request (
If-Match,CopyObjectsourceIf-Match, etc.)fails to match.
Repro
Any server using Go's
encoding/xmlexhibits this — the stdlibescapeTexthardcodes"→"(numeric refs are used uniformlyto keep escaping context-independent across attributes vs element
content). Trivial repro:
Pointing aws-c-s3 (or anything CRT-based — mountpoint-s3, AWS CLI v2
with CRT) at such a server, doing a multipart upload, and then issuing
a request with
If-Match: <returned ETag>reproduces the 412.Suggested fix
Two options:
aws_replace_quote_entities()to include"and"(andideally
'/'/'for symmetry).entity decoder pass (the five predefined entities + numeric
character references). The function only runs on already-extracted
XML text, so a real decoder is appropriate.