This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new 9a08f934 Escape constant pool names in generated HTML (#500)
9a08f934 is described below
commit 9a08f9342126119a135ba29fe3b28b1ecb0e1785
Author: Dexter.k <[email protected]>
AuthorDate: Fri Jun 12 01:32:08 2026 +0000
Escape constant pool names in generated HTML (#500)
---
.../java/org/apache/bcel/util/AttributeHTML.java | 4 +-
src/main/java/org/apache/bcel/util/Class2HTML.java | 6 +--
src/main/java/org/apache/bcel/util/CodeHTML.java | 6 +--
.../java/org/apache/bcel/util/ConstantHTML.java | 10 ++--
src/main/java/org/apache/bcel/util/MethodHTML.java | 2 +-
.../org/apache/bcel/util/Class2HTMLXSSTest.java | 59 ++++++++++++++++++++++
6 files changed, 75 insertions(+), 12 deletions(-)
diff --git a/src/main/java/org/apache/bcel/util/AttributeHTML.java
b/src/main/java/org/apache/bcel/util/AttributeHTML.java
index e0bafaf2..f87b1b24 100644
--- a/src/main/java/org/apache/bcel/util/AttributeHTML.java
+++ b/src/main/java/org/apache/bcel/util/AttributeHTML.java
@@ -159,7 +159,7 @@ final class AttributeHTML implements Closeable {
signature = Utility.signatureToString(signature, false);
final int start = var.getStartPC();
final int end = start + var.getLength();
- printWriter.println("<LI>" +
Class2HTML.referenceType(signature) + " <B>" + var.getName() + "</B> in
slot %" + var.getIndex()
+ printWriter.println("<LI>" +
Class2HTML.referenceType(signature) + " <B>" +
Class2HTML.toHTML(var.getName()) + "</B> in slot %" + var.getIndex()
+ "<BR>Valid from lines <A HREF=\"" + className +
"_code.html#code" + methodNumber + "@" + start + "\" TARGET=Code>" + start
+ "</A> to <A HREF=\"" + className + "_code.html#code"
+ methodNumber + "@" + end + "\" TARGET=Code>" + end + "</A></LI>");
});
@@ -173,7 +173,7 @@ final class AttributeHTML implements Closeable {
final String access;
index = clazz.getInnerNameIndex();
if (index > 0) {
- name = constantPool.getConstantUtf8(index).getBytes();
+ name =
Class2HTML.toHTML(constantPool.getConstantUtf8(index).getBytes());
} else {
name = "<anonymous>";
}
diff --git a/src/main/java/org/apache/bcel/util/Class2HTML.java
b/src/main/java/org/apache/bcel/util/Class2HTML.java
index 3a427ffc..b0ced24d 100644
--- a/src/main/java/org/apache/bcel/util/Class2HTML.java
+++ b/src/main/java/org/apache/bcel/util/Class2HTML.java
@@ -135,7 +135,7 @@ public class Class2HTML implements Constants {
String str = constantPool.getConstantString(index,
Const.CONSTANT_Class);
str = Utility.compactClassName(str);
str = Utility.compactClassName(str, classPackage + ".", true);
- return "<A HREF=\"" + className + "_cp.html#cp" + index + "\"
TARGET=ConstantPool>" + str + "</A>";
+ return "<A HREF=\"" + className + "_cp.html#cp" + index + "\"
TARGET=ConstantPool>" + toHTML(str) + "</A>";
}
static String referenceType(final String type) {
@@ -150,7 +150,7 @@ public class Class2HTML implements Constants {
if (basicTypes.contains(baseType)) {
return "<FONT COLOR=\"#00FF00\">" + type + "</FONT>";
}
- return "<A HREF=\"" + baseType + ".html\" TARGET=_top>" + shortType +
"</A>";
+ return "<A HREF=\"" + baseType + ".html\" TARGET=_top>" +
toHTML(shortType) + "</A>";
}
static String toHTML(final String str) {
@@ -221,7 +221,7 @@ public class Class2HTML implements Constants {
try (PrintWriter file = new PrintWriter(dir + className + ".html",
charset.name())) {
// @formatter:off
file.println("<HTML>\n"
- + "<HEAD><TITLE>Documentation for " + className +
"</TITLE></HEAD>\n"
+ + "<HEAD><TITLE>Documentation for " + toHTML(className) +
"</TITLE></HEAD>\n"
+ "<FRAMESET BORDER=1 cols=\"30%,*\">\n"
+ "<FRAMESET BORDER=1 rows=\"80%,*\">\n"
+ "<FRAME NAME=\"ConstantPool\" SRC=\"" + className +
"_cp.html" + "\"\n"
diff --git a/src/main/java/org/apache/bcel/util/CodeHTML.java
b/src/main/java/org/apache/bcel/util/CodeHTML.java
index 1f26c91b..53ffb3c9 100644
--- a/src/main/java/org/apache/bcel/util/CodeHTML.java
+++ b/src/main/java/org/apache/bcel/util/CodeHTML.java
@@ -231,10 +231,10 @@ final class CodeHTML {
index = c1.getNameAndTypeIndex();
final String fieldName = constantPool.constantToString(index,
Const.CONSTANT_NameAndType);
if (name.equals(className)) { // Local field
- buf.append("<A
HREF=\"").append(className).append("_methods.html#field").append(fieldName).append("\"
TARGET=Methods>").append(fieldName)
- .append("</A>\n");
+ buf.append("<A
HREF=\"").append(className).append("_methods.html#field").append(fieldName).append("\"
TARGET=Methods>")
+ .append(Class2HTML.toHTML(fieldName)).append("</A>\n");
} else {
-
buf.append(constantHtml.referenceConstant(classIndex)).append(".").append(fieldName);
+
buf.append(constantHtml.referenceConstant(classIndex)).append(".").append(Class2HTML.toHTML(fieldName));
}
break;
/*
diff --git a/src/main/java/org/apache/bcel/util/ConstantHTML.java
b/src/main/java/org/apache/bcel/util/ConstantHTML.java
index 081baf0a..a85e1cd3 100644
--- a/src/main/java/org/apache/bcel/util/ConstantHTML.java
+++ b/src/main/java/org/apache/bcel/util/ConstantHTML.java
@@ -123,6 +123,7 @@ final class ConstantHTML {
final String methodClass =
constantPool.constantToString(classIndex, Const.CONSTANT_Class);
String shortMethodClass = Utility.compactClassName(methodClass);
// I.e., remove java.lang.
shortMethodClass = Utility.compactClassName(shortMethodClass,
classPackage + ".", true); // Remove class package prefix
+ shortMethodClass = Class2HTML.toHTML(shortMethodClass);
// Get method signature
final ConstantNameAndType c2 = constantPool.getConstant(nameIndex,
Const.CONSTANT_NameAndType, ConstantNameAndType.class);
final String signature =
constantPool.constantToString(c2.getSignatureIndex(), Const.CONSTANT_Utf8);
@@ -159,14 +160,16 @@ final class ConstantHTML {
final String fieldClass =
constantPool.constantToString(classIndex, Const.CONSTANT_Class);
String shortFieldClass = Utility.compactClassName(fieldClass); //
I.e., remove java.lang.
shortFieldClass = Utility.compactClassName(shortFieldClass,
classPackage + ".", true); // Remove class package prefix
+ shortFieldClass = Class2HTML.toHTML(shortFieldClass);
final String fieldName = constantPool.constantToString(nameIndex,
Const.CONSTANT_NameAndType);
+ final String htmlFieldName = Class2HTML.toHTML(fieldName);
if (fieldClass.equals(className)) {
- ref = "<A HREF=\"" + fieldClass + "_methods.html#field" +
fieldName + "\" TARGET=Methods>" + fieldName + "</A>";
+ ref = "<A HREF=\"" + fieldClass + "_methods.html#field" +
fieldName + "\" TARGET=Methods>" + htmlFieldName + "</A>";
} else {
- ref = "<A HREF=\"" + fieldClass + ".html\" TARGET=_top>" +
shortFieldClass + "</A>." + fieldName + "\n";
+ ref = "<A HREF=\"" + fieldClass + ".html\" TARGET=_top>" +
shortFieldClass + "</A>." + htmlFieldName + "\n";
}
constantRef[index] = "<A HREF=\"" + className + "_cp.html#cp" +
classIndex + "\" TARGET=Constants>" + shortFieldClass + "</A>.<A HREF=\""
- + className + "_cp.html#cp" + index + "\"
TARGET=ConstantPool>" + fieldName + "</A>";
+ + className + "_cp.html#cp" + index + "\"
TARGET=ConstantPool>" + htmlFieldName + "</A>";
printWriter.println("<P><TT>" + ref + "</TT><BR>\n" + "<UL>" +
"<LI><A HREF=\"#cp" + classIndex + "\">Class(" + classIndex + ")</A><BR>\n"
+ "<LI><A HREF=\"#cp" + nameIndex + "\">NameAndType(" +
nameIndex + ")</A></UL>");
break;
@@ -176,6 +179,7 @@ final class ConstantHTML {
final String className2 = constantPool.constantToString(index,
tag); // / -> .
String shortClassName = Utility.compactClassName(className2); //
I.e., remove java.lang.
shortClassName = Utility.compactClassName(shortClassName,
classPackage + ".", true); // Remove class package prefix
+ shortClassName = Class2HTML.toHTML(shortClassName);
ref = "<A HREF=\"" + className2 + ".html\" TARGET=_top>" +
shortClassName + "</A>";
constantRef[index] = "<A HREF=\"" + className + "_cp.html#cp" +
index + "\" TARGET=ConstantPool>" + shortClassName + "</A>";
printWriter.println("<P><TT>" + ref + "</TT><UL>" + "<LI><A
HREF=\"#cp" + nameIndex + "\">Name index(" + nameIndex + ")</A></UL>\n");
diff --git a/src/main/java/org/apache/bcel/util/MethodHTML.java
b/src/main/java/org/apache/bcel/util/MethodHTML.java
index ee7d093a..d6c053de 100644
--- a/src/main/java/org/apache/bcel/util/MethodHTML.java
+++ b/src/main/java/org/apache/bcel/util/MethodHTML.java
@@ -78,7 +78,7 @@ final class MethodHTML {
final Attribute[] attributes;
access = Utility.replace(access, " ", " ");
printWriter.print("<TR><TD><FONT COLOR=\"#FF0000\">" + access +
"</FONT></TD>\n<TD>" + Class2HTML.referenceType(type) + "</TD><TD><A
NAME=\"field"
- + name + "\">" + name + "</A></TD>");
+ + name + "\">" + Class2HTML.toHTML(name) + "</A></TD>");
attributes = field.getAttributes();
// Write them to the Attributes.html file with anchor "<name>[<i>]"
for (int i = 0; i < attributes.length; i++) {
diff --git a/src/test/java/org/apache/bcel/util/Class2HTMLXSSTest.java
b/src/test/java/org/apache/bcel/util/Class2HTMLXSSTest.java
new file mode 100644
index 00000000..478060a9
--- /dev/null
+++ b/src/test/java/org/apache/bcel/util/Class2HTMLXSSTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bcel.util;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.FieldGen;
+import org.apache.bcel.generic.Type;
+import org.junit.jupiter.api.Test;
+
+class Class2HTMLXSSTest {
+
+ /**
+ * A field name in the constant pool is attacker controlled and may
contain HTML metacharacters; the generated
+ * documentation must escape it in text context rather than emit it raw.
+ */
+ @Test
+ void testFieldNameIsEscaped() throws Exception {
+ final ClassGen cg = new ClassGen("Evil", "java.lang.Object",
"Evil.java", Const.ACC_PUBLIC, null);
+ cg.addField(new FieldGen(Const.ACC_PUBLIC, Type.INT,
"x<script>alert(1)</script>", cg.getConstantPool()).getField());
+ final JavaClass jc = cg.getJavaClass();
+
+ final File outputDir = new File("target/test-output/html-xss");
+ if (!outputDir.mkdirs()) {
+ assertTrue(outputDir.isDirectory());
+ }
+ new Class2HTML(jc, outputDir.getAbsolutePath() + File.separator);
+
+ final String methods = new String(Files.readAllBytes(new
File(outputDir, "Evil_methods.html").toPath()), StandardCharsets.UTF_8);
+ // The field name rendered as link text must be escaped, not emitted
as a live tag.
+ assertFalse(methods.contains("\">x<script>"), "field name was emitted
unescaped in text context");
+ assertTrue(methods.contains("<script>"), "expected the field
name to be HTML-escaped");
+ }
+}