Skip to content

dictdumper PLIST writer does not escape angle brackets, so a pseudo-member makes the dump unparseable #772

Description

@JarryShaw

Describe the bug

dictdumper's PLIST writer does not escape XML special characters in string values, so any dumped
value containing < or > produces malformed XML. pcapkit's own nameless pseudo-member renders as
<unknown>::<unassigned> [N] (pcapkit/dumpkit/common.py:168, render_enum), so a plist dump
containing one is unparseable.

Reproduction — three lines, no pcapkit involved:

import dictdumper, tempfile, os, xml.etree.ElementTree as ET
p = os.path.join(tempfile.mkdtemp(), 't.plist')
dictdumper.PLIST(p)({'val': '<unknown>::<unassigned> [54321]'}, name='frame1')
ET.parse(p)
plist contains RAW '<unknown>': True
plist contains escaped '&lt;unknown&gt;': False
parses as XML: False -> mismatched tag: line 8, column 43
   <string><unknown>::<unassigned> [54321]</string>

Expected behavior

< and > escaped as &lt;/&gt; inside <string>, so the document parses.

Additional context

This is live on main today, not new. The pseudo-member has existed since #701 and
EnumField.post_process's docstring already names <unknown>::<unassigned> [28] as its rendered
form, so any capture carrying a genuinely unassigned option code already produces an unparseable
plist. It has gone unnoticed only because no fixture in the corpus hits that path, so
tests/integration/test_output_formats.py::PlistReportTests::test_plist_report_is_well_formed_xml_with_one_key_per_frame
has never seen one.

#771 makes it reachable on essentially every capture, because it routes unassigned ports through
the same pseudo-member and ephemeral ports are ubiquitous. Four CI legs on that PR fail on this single
test — one defect, not four.

The writer is dictdumper's (dictdumper.PLIST), so the escaping is not ours to fix in-tree. Options,
none chosen: fix upstream and pin a new version; escape per-format in make_dumper's hook rather than
in render_enum, since < is legal in the json/text outputs; or rename the pseudo-member to avoid
angle brackets, which changes an established dump spelling. Blocking #771 — see the decision request
there.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugdependenciesPull requests that update a dependency file

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions