Visitar URL original
Fix #15107: Exporting XML via GUI escapes special characters twice. by ludviggunne · Pull Request #8943 · cppcheck-opensource/cppcheck · GitHub
Skip to content

Fix #15107: Exporting XML via GUI escapes special characters twice. - #8943

Merged
ludviggunne merged 2 commits into
cppcheck-opensource:mainfrom
ludviggunne:15107-gui-escape
Oct 29, 2026
Merged

ludviggunne merged 2 commits into
cppcheck-opensource:mainfrom
ludviggunne:15107-gui-escape

Conversation

@ludviggunne

Copy link
Copy Markdown
Collaborator

No description provided.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

The writer no longer escapes twice, but the reader still unescapes twice. XmlReportV2::writeError() now relies on QXmlStreamWriter's escaping alone. XmlReportV2::readError() still runs XmlReport::unquoteMessage() on msg, verbose, file, file0 and info after QXmlStreamReader has already decoded them. Any text that contains a literal entity such as <, &, " or ' therefore changes on a save/load round trip. Before this PR the round trip kept the text intact, because the extra quoting and unquoting cancelled out.

Cppcheck produces such messages itself. if (strcmp(s, "&lt;") == 0 && strcmp(s, "&lt;") == 0) gives Condition 'strcmp(s,"&lt;")==0' is always true. After saving this from the GUI (or after it is written to the project's lastResults.xml) and loading it again, the message reads strcmp(s,"<"). ErrorItem::same() compares message and errorPath when there is no hash, so these items can also lose their sinceDate and tags in updateFromOldReport.

To keep reading old double-escaped files, the unquoting should depend on the file, for example on a version or marker in the header that this PR's writer emits. It should not run on every file. The new test does not catch the problem because its strings contain no literal entity. The XML that the CLI writes was already decoded twice by the GUI before this PR; this PR adds the same problem for files the GUI writes itself.

Testing

  • Qt is not installed in the container, so I could not build the GUI or run gui/test/xmlreportv2. The problem is found by reading the code, but it is concrete.
  • Built cppcheck and testrunner in pr/ with make -j8; ./testrunner TestErrorLogger passes. The PR does not change the core library or the CLI.
  • Showed that cppcheck emits messages with literal entities:
// scratch/a.c
#include <string.h>
void f(const char *s) {
    if (strcmp(s, "&lt;") == 0 && strcmp(s, "&lt;") == 0) {}
}
$ pr/cppcheck --enable=all --xml a.c
<error id="knownConditionTrueFalse" ... msg="Condition &apos;strcmp(s,&quot;&amp;lt;&quot;)==0&apos; is always true" ...>
    <location file="a.c" line="3" column="53" info="Condition &apos;strcmp(s,&quot;&amp;lt;&quot;)==0&apos; is always true"/>
  • Round trip in the GUI, traced in the code. The message is Condition 'strcmp(s,"&lt;")==0' is always true.
    • With the PR, the writer stores &amp;lt;. QXmlStreamReader decodes that to &lt;, and unquoteMessage then turns it into <, so the reloaded message has strcmp(s,"<").
    • On base, the writer stored &amp;amp;lt;. Reading decoded it to &amp;lt; and unquoting gave &lt;, so the message came back correctly.
  • A version of the new test that would catch this: set item.summary = "x &lt; y", then check QCOMPARE(errors[0].summary, item.summary) after reading.

Automated review of 54b2dde by Claude. The pull request was built and tested locally.

Comment thread gui/xmlreportv2.cpp Resolved
Comment thread gui/xmlreport.h Resolved
@ludviggunne

Copy link
Copy Markdown
Collaborator Author

Just drops double-escaping in GUI. This is intentionally not backwards compatible with previous results generated in GUI. There is just no change to the format that would make it possible to distinguish and old, single-escaped file from CLI, or an old, double-escaped file from GUI.

@ludviggunne

Copy link
Copy Markdown
Collaborator Author

I suppose we could add a script to tools/ if someone ever needs to fix an old double-escaped results file. But not sure how relevant that would be.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

I found no new problems. The earlier review said the reader still unescaped a second time. That is fixed in this version: readError() no longer calls unquoteMessage, and both functions are removed. A grep finds no other callers. Text that contains a literal entity such as &lt; now comes back unchanged after a save and reload. This includes the XML that the CLI writes, which the GUI used to decode twice. Old results files written by the GUI will now show the extra escaping, and items without a hash may stop matching in updateFromOldReport. The author has said this break with old files is intended (comments.md), so I don't report it again. The stubs removed from testresultstree.cpp are no longer needed, because nothing refers to the removed functions any more.

Testing

  • GUI not built or tested: Qt is not installed in the container, so I could not build the GUI or run gui/test/xmlreportv2. My conclusions about the GUI code come from reading it.
  • CLI: built cppcheck in pr/ with make -j8 cppcheck; the build succeeded.
  • Round trip: checked with CLI output that contains a literal entity:
    // scratch/a.c
    #include <string.h>
    void f(const char *s) {
        if (strcmp(s, "&lt;") == 0 && strcmp(s, "&lt;") == 0) {}
    }
    pr/cppcheck --enable=all --xml a.c writes msg="Condition &apos;strcmp(s,&quot;&amp;lt;&quot;)==0&apos; is always true".
    • I read this attribute with a standard XML parser, without the extra unquoting, the way the PR's readError() does now. Then I wrote it out and read it again, the way writeError() and readError() do (python3 -I rt.py r.xml).
    • Both times the message was Condition 'strcmp(s,"&lt;")==0' is always true, so it came back intact.
    • Before the PR, the GUI's unquoteMessage would have turned it into strcmp(s,"<").
  • Leftover calls: grep -rn quoteMessage over the PR tree finds nothing.

Automated review of f968345 by Claude. The pull request was built and tested locally.

<location file="test.cxx" line="11"/>
</error>
<error id="unreadVariable" severity="style" msg="Variable &amp;#039;b&amp;#039; is assigned a value that is never used" verbose="Variable &amp;#039;b&amp;#039; is assigned a value that is never used">
<error id="unreadVariable" severity="style" msg="Variable 'b' is assigned a value that is never used" verbose="Variable 'b' is assigned a value that is never used">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Cppcheck CLI writes such output:

        <error id="unreadVariable" severity="style" msg="Variable &apos;x&apos; is assigned a value that is never used." verbose="Variable &apos;x&apos; is assigned a value that is never used." cwe="563" hash="14160018506957104265" file0="1.c">

Is the CLI or GUI wrong? Or are both versions fine? Is one version preferable?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both are fine, altough escaping isn't actually necessary, only if the attribute value uses single quotes. I'm not sure if either is more preferable.

@ludviggunne
ludviggunne merged commit 5114436 into cppcheck-opensource:main Oct 29, 2026
71 checks passed
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.

3 participants