Skip to content

Trailing HTML comment parsing #1069

Description

@dwin-gharibi

Summary

trailingHtmlComments can return an "HTML comment" that spans visible prose. It
scans backward from the end of a comment body, pairing each --> with the nearest
preceding <!--, without checking that the two actually delimit one comment. A
stray --> in prose therefore bridges back to an earlier opener and swallows
everything in between — including any real ClawSweeper marker in that span.

Impact

This is the parser that recovers ClawSweeper's durable state markers from its own
published comment. When the bridge happens, the marker inside the swallowed span is
no longer independently recoverable:

<!-- clawsweeper-verdict:needs-human item=321 sha=head -->
The rust operator `-` then `->` renders as -->
<!-- clawsweeper-review item=321 -->

returns

["<!-- clawsweeper-verdict:needs-human item=321 sha=head -->\nrenders as -->",
 "<!-- clawsweeper-review item=321 -->"]

The first entry is not a comment. It contains an interior --> and visible text.

--> is not exotic in these bodies: Mermaid flowchart edges are literally A --> B,
and ClawSweeper renders a Mermaid architecture diagram in its own reviews. Rust,
Haskell, and ASCII diagrams produce it too.

Reachability — please read before rating severity

I could not construct a body from the real renderer that triggers this, and I
tried hard.
The trigger needs all three of:

  1. an earlier <!-- in the body to bridge back to;
  2. visible prose ending in --> after it;
  3. the trailing marker block immediately after that prose.

Four realistic bodies — a Mermaid diagram in a closed fence, the same with an
unclosed fence, prose ending in an arrow, and a table cell ending in an arrow —
all parse correctly even today, because condition 1 is missing. The durable
comment's only mid-body opener is the <!-- clawsweeper-review-history --> marker
that renderReviewHistorySection emits, and </details> always follows it before
any trailing prose.

Both consumers are also fail-safe today:

  • clawsweeper-review-comment-state.ts anchors both ends with [^>]*, so a blob
    containing > fails to match and the marker is ignored;
  • review-recovery-label-backfill.ts uses a start-anchored matcher and then scans
    the whole entry for name=value, but its canonical check is strictly anchored,
    so a blob makes the recovery label be retained rather than wrongly cleared.

So this is a latent parser defect that is currently safe by accident of its
consumers' regexes
, not a live incident. The reason it is still worth fixing:
review-recovery-label-backfill.ts harvests attributes from the entire entry with
a loose matcher. If a blob ever satisfies its canonical check — one regex
refactor away — prose-derived attributes become authoritative for a
label-clearing decision.

Rate it accordingly; I would not call it more than a P3.

Root cause

src/review-comment-markers.ts:

if (!value.endsWith("-->", end)) break;
const commentStart = value.lastIndexOf("<!--", end - 3);
if (commentStart < 0) break;
trailing.push(value.slice(commentStart, end));   // never checks what is between

An HTML comment ends at its first -->. The pairing above never verifies that.

Suggested fix

Reject the candidate unless the opener's first terminator is the one being
matched:

if (value.indexOf("-->", commentStart + 4) !== end - 3) break;

Anything else means prose lies between them, so the trailing block has ended. This
makes the function honor its documented contract — "the final contiguous comment
block"
— and changes nothing for bodies with a clean trailing block.

Existing coverage gap

test/review-comment-markers.test.ts covers the mirror case (prose containing an
unmatched opener) and an adversarial unterminated suffix. The unmatched
closer is the untested sibling.

Environment

Repo openclaw/clawsweeper @ 0588bda9
Node v24.19.0 (Crabbox local-container)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low-risk cleanup, docs, polish, ergonomics, or speculative feature.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions