Skip to content

GROOVY-12277: Encode source-derived text emitted into generated HTML - #2814

Open
paulk-asert wants to merge 1 commit into
apache:masterfrom
paulk-asert:groovy12277
Open

GROOVY-12277: Encode source-derived text emitted into generated HTML#2814
paulk-asert wants to merge 1 commit into
apache:masterfrom
paulk-asert:groovy12277

Conversation

@paulk-asert

Copy link
Copy Markdown
Contributor

Two places where groovydoc builds HTML around text taken from the source it is documenting, without encoding it for the context it lands in.

A {@link} or @see reference is split into a target and a label, both of which are then concatenated into an anchor the tool constructs: the target into href, the label into the element text, and for a resolved class the short name into title as well. None was encoded, so a reference could close the attribute and open a tag of its own. Encode each for its context, the attributes through encodeAttribute and the text through encodeAngleBrackets.

An annotation's name and description are emitted into the class declaration and every member heading. description() carries the annotation's arguments as they were written, so an annotation holding a string literal put that literal into the page verbatim. Encode both, leaving the linkable() call alone since that one does produce markup.

This is groovydoc's own construction rather than the raw HTML a doc comment body may contain by javadoc parity, so the passthrough that covers a comment body does not extend to it. For the annotation case the text is not from a comment at all: it is source code, and reaches the page without any doc comment being written.

Not changed: the @default tag GroovydocJavaVisitor appends for an annotation member's default value. It is added to the raw comment text and no template renders it as a declaration, and constantValueExpression() is only tested for nullity, so that value does not reach a declaration block.

@codecov-commenter

codecov-commenter commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.1763%. Comparing base (1c2f0f2) to head (0686a9f).
⚠️ Report is 5 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #2814        +/-   ##
==================================================
- Coverage     70.1794%   70.1763%   -0.0031%     
- Complexity      35853      35886        +33     
==================================================
  Files            1563       1564         +1     
  Lines          132546     132606        +60     
  Branches        24385      24399        +14     
==================================================
+ Hits            93020      93058        +38     
- Misses          31113      31137        +24     
+ Partials         8413       8411         -2     
Files with missing lines Coverage Δ
...s/groovy/tools/groovydoc/SimpleGroovyClassDoc.java 83.4615% <100.0000%> (+0.0639%) ⬆️

... and 13 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@testlens-app

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens GroovyDoc HTML generation by encoding source-derived/doc-comment-derived strings before inserting them into HTML attributes or element text, preventing markup/attribute injection in generated documentation.

Changes:

  • Encode {@link}-derived URL/label when GroovyDoc constructs anchors (attribute vs. text contexts).
  • Encode annotation name/description when rendering declaration headings in the class template.
  • Add regression tests covering the above injection vectors.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java Adds regression tests for encoding of link and annotation text in generated HTML.
subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html Encodes annotation name/description when emitting declaration text.
subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java Encodes link targets/labels for href, title, and anchor text when constructing HTML.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Two places where groovydoc builds HTML around text taken from the source it
is documenting, without encoding it for the context it lands in.

A {@link} or @see reference is split into a target and a label, both of
which are then concatenated into an anchor the tool constructs: the target
into href, the label into the element text, and for a resolved class the
short name into title as well. None was encoded, so a reference could close
the attribute and open a tag of its own. Encode each for its context, the
attributes through encodeAttribute and the text through encodeAngleBrackets.

An annotation's name and description are emitted into the class declaration
and every member heading. description() carries the annotation's arguments
as they were written, so an annotation holding a string literal put that
literal into the page verbatim. Encode both, leaving the linkable() call
alone since that one does produce markup.

This is groovydoc's own construction rather than the raw HTML a doc comment
body may contain by javadoc parity, so the passthrough that covers a comment
body does not extend to it. For the annotation case the text is not from a
comment at all: it is source code, and reaches the page without any doc
comment being written.

Not changed: the @default tag GroovydocJavaVisitor appends for an annotation
member's default value. It is added to the raw comment text and no template
renders it as a declaration, and constantValueExpression() is only tested
for nullity, so that value does not reach a declaration block.
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