Escaping `XmlNameProcessors` write a colon in XML names as-is, producing output that can not be read back

オープン 初心者向け
#923 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
85/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
java
領域
backend

調査の方向性

Base64NameProcessor と ReplaceNameProcessor を確認してください。コロンを除外するため、VALID_XML_NAME の正規表現パターンと許可された文字セットを変更し、a:b のような名前がエスケープまたは置換されるようにしてください。それに応じて encodeName() メソッドを更新してください。コロンを含む名前が有効なXMLにシリアライズされ、StreamReadException なしでデシリアライズされることを確認するためのラウンドトリップテストを追加してください。

索引モデルが issue の本文から書いたものです。

説明

Describe the bug

Both escaping processors from XmlNameProcessors treat a colon as a valid XML name character, so a name that contains one is written out verbatim:

  • Base64NameProcessor.VALID_XML_NAME is [a-zA-Z_:]([a-zA-Z0-9_:.-])*, i.e. a name holding a colon counts as already valid and is not escaped
  • ReplaceNameProcessor keeps : in both of its allowed sets (^[^a-zA-Z_:], [^a-zA-Z0-9_:-])

Output is namespace-aware, though, so the colon comes back as a prefix that nothing declares and the document can no longer be read by this module:

<DTO><badMap><a:b>xyz</a:b></badMap></DTO>
tools.jackson.core.exc.StreamReadException:
Undeclared namespace prefix "a"
 at [row,col {unknown-source}]: [1,18]
 at [Source: (String)"<DTO><badMap><a:b>xyz</a:b></badMap></DTO>"; line: 1, column: 19]
Caused by: com.ctc.wstx.exc.WstxParsingException: Undeclared namespace prefix "a"

Keys like a:b:c or :leading are not even well-formed, so they fail in the Stax reader before namespace resolution.

The javadoc of both processors lists the colon as "only exposed if underlying parser is in non-namespace-aware mode", but encoding has no knowledge of which parser will read the result back, and newAlwaysOnBase64Processor() already handles the same keys cleanly. Round-tripping names that are not valid XML names is what these two processors exist for, so the colon looks like an oversight in the allowed sets rather than a deliberate exception.

Version information

3.3.0-SNAPSHOT (3.x). The allowed sets have looked this way since the mechanism was added (#531), so 3.0/3.1/3.2 and 2.x behave the same.

Reproduction
public static class DTO {
    public Map<String, String> badMap = new HashMap<>();
}

private XmlFactory xmlFactory(XmlNameProcessor nameProcessor) {
    return XmlFactory.builder().xmlNameProcessor(nameProcessor).build();
}

@Test
public void colonInNameRoundTrips() throws Exception {
    XmlMapper mapper = XmlMapper.builder(
            xmlFactory(XmlNameProcessors.newBase64Processor())).build();

    DTO dto = new DTO();
    dto.badMap.put("a:b", "xyz");

    String xml = mapper.writeValueAsString(dto);
    // writes <DTO><badMap><a:b>xyz</a:b></badMap></DTO>
    mapper.readValue(xml, DTO.class); // StreamReadException: Undeclared namespace prefix "a"
}

Same for newReplacementProcessor(), which writes <a:b> rather than replacing the colon. newAlwaysOnBase64Processor() is fine, since it escapes every name.

Expected behavior

A name containing a colon should be escaped (base64, still a lossless round trip) or replaced, like any other character that cannot appear in a written XML name, so that what these processors write can be read back.

Only encodeName() would need to change: decodeName() keys off the prefix for base64 and is a no-op for replacement, so names read from a non-namespace-aware parser are still passed through untouched.

Additional context

PR #921 has the fix plus round-trip tests. Targeted at 3.x since it changes what gets written for these names.

主要言語
Java
スター
636
フォーク
250
平均マージ
7時間 10分
マージ済み PR(30日)
8

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

FasterXML/jackson-dataformat-xml のほかの issue

FasterXML/jackson-dataformat-xml の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。