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 279ec230 Sort members
279ec230 is described below
commit 279ec2300c1d9757061454c200d5e19090bee39e
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 4 20:19:11 2026 -0400
Sort members
---
.../java/org/apache/bcel/classfile/Signature.java | 12 ++--
src/main/java/org/apache/bcel/util/BCELifier.java | 22 ++++----
src/main/java/org/apache/bcel/util/Class2HTML.java | 64 +++++++++++-----------
.../bcel/verifier/structurals/Subroutines.java | 16 +++---
.../java/org/apache/bcel/util/BCELifierTest.java | 22 ++++----
5 files changed, 68 insertions(+), 68 deletions(-)
diff --git a/src/main/java/org/apache/bcel/classfile/Signature.java
b/src/main/java/org/apache/bcel/classfile/Signature.java
index 68b12c88..4a43cccf 100644
--- a/src/main/java/org/apache/bcel/classfile/Signature.java
+++ b/src/main/java/org/apache/bcel/classfile/Signature.java
@@ -55,6 +55,12 @@ public final class Signature extends Attribute {
}
}
+ /**
+ * The maximum nesting depth of a signature accepted by {@link
#translate(String)}. Guards against a
+ * {@link StackOverflowError} from deeply nested, attacker-supplied
generic signatures.
+ */
+ private static final int MAX_NESTING_DEPTH = 512;
+
private static boolean identStart(final int ch) {
return ch == 'T' || ch == 'L';
}
@@ -81,12 +87,6 @@ public final class Signature extends Attribute {
return s.startsWith("<") && s.indexOf(':') > 0;
}
- /**
- * The maximum nesting depth of a signature accepted by {@link
#translate(String)}. Guards against a
- * {@link StackOverflowError} from deeply nested, attacker-supplied
generic signatures.
- */
- private static final int MAX_NESTING_DEPTH = 512;
-
private static void matchGJIdent(final MyByteArrayInputStream in, final
StringBuilder buf) {
matchGJIdent(in, buf, 0);
}
diff --git a/src/main/java/org/apache/bcel/util/BCELifier.java
b/src/main/java/org/apache/bcel/util/BCELifier.java
index ab869363..a40c8a94 100644
--- a/src/main/java/org/apache/bcel/util/BCELifier.java
+++ b/src/main/java/org/apache/bcel/util/BCELifier.java
@@ -71,17 +71,6 @@ public class BCELifier extends
org.apache.bcel.classfile.EmptyVisitor {
private static final String BASE_PACKAGE =
Const.class.getPackage().getName();
private static final String CONSTANT_PREFIX = Const.class.getSimpleName()
+ ".";
- private static String[] escape(final String[] names) {
- if (names == null) {
- return null;
- }
- final String[] escaped = new String[names.length];
- for (int i = 0; i < names.length; i++) {
- escaped[i] = names[i] == null ? null :
Utility.convertString(names[i]);
- }
- return escaped;
- }
-
/**
* Checks that a name from the parsed class file is a dotted sequence of
valid Java identifiers before it is
* emitted in identifier position of the generated source. The class file
format allows characters in names (for
@@ -113,6 +102,17 @@ public class BCELifier extends
org.apache.bcel.classfile.EmptyVisitor {
return name;
}
+ private static String[] escape(final String[] names) {
+ if (names == null) {
+ return null;
+ }
+ final String[] escaped = new String[names.length];
+ for (int i = 0; i < names.length; i++) {
+ escaped[i] = names[i] == null ? null :
Utility.convertString(names[i]);
+ }
+ return escaped;
+ }
+
// Needs to be accessible from unit test code
static JavaClass getJavaClass(final String name) throws
ClassNotFoundException, IOException {
JavaClass javaClass;
diff --git a/src/main/java/org/apache/bcel/util/Class2HTML.java
b/src/main/java/org/apache/bcel/util/Class2HTML.java
index 69d010c3..bb3e252e 100644
--- a/src/main/java/org/apache/bcel/util/Class2HTML.java
+++ b/src/main/java/org/apache/bcel/util/Class2HTML.java
@@ -71,6 +71,29 @@ public class Class2HTML implements Constants {
basicTypes.add("float");
}
+ /**
+ * The class name comes from the attacker-controlled this_class constant
of the parsed class file and is
+ * concatenated into the five output file paths ("dir + className +
suffix"). Class file parsing only folds
+ * '/' into '.', so Windows separators ('\\'), drive designators (':') and
".." segments survive and would
+ * let a crafted class file write its HTML output outside the target
directory (CWE-22).
+ *
+ * @param name the class name about to be used as part of a file name.
+ * @throws IOException if the name contains a path separator, a
Windows-reserved file name character, a
+ * control character, or a ".." sequence.
+ */
+ private static void checkFileNameSafe(final String name) throws
IOException {
+ for (int i = 0; i < name.length(); i++) {
+ final char c = name.charAt(i);
+ if (c < ' ' || "\\/:*?\"<>|".indexOf(c) >= 0) {
+ throw new IOException("Refusing to write HTML for a class
whose name contains the unsafe character (0x"
+ + Integer.toHexString(c) + "): " + name);
+ }
+ }
+ if (name.contains("..")) {
+ throw new IOException("Refusing to write HTML for a class whose
name contains \"..\": " + name);
+ }
+ }
+
/**
* Main program to convert class files to HTML.
*
@@ -153,15 +176,6 @@ public class Class2HTML implements Constants {
return "<A HREF=\"" + toHTMLRef(baseType) + ".html\" TARGET=_top>" +
toHTML(shortType) + "</A>";
}
- /**
- * Escapes a class or type name taken from the constant pool for use as a
relative link target inside an HREF
- * attribute value. On top of the text escaping done by {@code
toHTML(String)}, any ':' is replaced so an
- * attacker-chosen name cannot smuggle a URL scheme such as "javascript:"
into the generated link.
- */
- static String toHTMLRef(final String str) {
- return toHTML(str.replace(':', '_'));
- }
-
static String toHTML(final String str) {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
@@ -195,6 +209,15 @@ public class Class2HTML implements Constants {
return buf.toString();
}
+ /**
+ * Escapes a class or type name taken from the constant pool for use as a
relative link target inside an HREF
+ * attribute value. On top of the text escaping done by {@code
toHTML(String)}, any ':' is replaced so an
+ * attacker-chosen name cannot smuggle a URL scheme such as "javascript:"
into the generated link.
+ */
+ static String toHTMLRef(final String str) {
+ return toHTML(str.replace(':', '_'));
+ }
+
private final JavaClass javaClass; // current class object
private final String dir;
@@ -236,29 +259,6 @@ public class Class2HTML implements Constants {
}
}
- /**
- * The class name comes from the attacker-controlled this_class constant
of the parsed class file and is
- * concatenated into the five output file paths ("dir + className +
suffix"). Class file parsing only folds
- * '/' into '.', so Windows separators ('\\'), drive designators (':') and
".." segments survive and would
- * let a crafted class file write its HTML output outside the target
directory (CWE-22).
- *
- * @param name the class name about to be used as part of a file name.
- * @throws IOException if the name contains a path separator, a
Windows-reserved file name character, a
- * control character, or a ".." sequence.
- */
- private static void checkFileNameSafe(final String name) throws
IOException {
- for (int i = 0; i < name.length(); i++) {
- final char c = name.charAt(i);
- if (c < ' ' || "\\/:*?\"<>|".indexOf(c) >= 0) {
- throw new IOException("Refusing to write HTML for a class
whose name contains the unsafe character (0x"
- + Integer.toHexString(c) + "): " + name);
- }
- }
- if (name.contains("..")) {
- throw new IOException("Refusing to write HTML for a class whose
name contains \"..\": " + name);
- }
- }
-
private void writeMainHTML(final AttributeHTML attributeHtml, final
Charset charset) throws FileNotFoundException, UnsupportedEncodingException {
try (PrintWriter file = new PrintWriter(dir + className + ".html",
charset.name())) {
// @formatter:off
diff --git
a/src/main/java/org/apache/bcel/verifier/structurals/Subroutines.java
b/src/main/java/org/apache/bcel/verifier/structurals/Subroutines.java
index 2013b7b4..6e00db5d 100644
--- a/src/main/java/org/apache/bcel/verifier/structurals/Subroutines.java
+++ b/src/main/java/org/apache/bcel/verifier/structurals/Subroutines.java
@@ -394,6 +394,14 @@ public class Subroutines {
return single;
}
+ private static StructuralCodeConstraintException
recursiveCallException(final Subroutine sub2) {
+ // Don't use toString() here because of possibly infinite recursive
subSubs() calls then.
+ final SubroutineImpl si = (SubroutineImpl) sub2;
+ return new StructuralCodeConstraintException("Subroutine with local
variable '" + si.localVariable + "', JSRs '" + si.theJSRs + "', RET '"
+ + si.theRET + "' is called by a subroutine which uses the same
local variable index as itself; maybe even a recursive call?"
+ + " JustIce's clean definition of a subroutine forbids both.");
+ }
+
/**
* The map containing the subroutines found. Key: InstructionHandle of the
leader of the subroutine. Elements:
* SubroutineImpl objects.
@@ -678,14 +686,6 @@ public class Subroutines {
return subtreeLocals;
}
- private static StructuralCodeConstraintException
recursiveCallException(final Subroutine sub2) {
- // Don't use toString() here because of possibly infinite recursive
subSubs() calls then.
- final SubroutineImpl si = (SubroutineImpl) sub2;
- return new StructuralCodeConstraintException("Subroutine with local
variable '" + si.localVariable + "', JSRs '" + si.theJSRs + "', RET '"
- + si.theRET + "' is called by a subroutine which uses the same
local variable index as itself; maybe even a recursive call?"
- + " JustIce's clean definition of a subroutine forbids both.");
- }
-
/**
* Returns the subroutine object associated with the given instruction.
This is a costly operation, you should consider
* using getSubroutine(InstructionHandle). Returns 'null' if the given
InstructionHandle lies in so-called 'dead code',
diff --git a/src/test/java/org/apache/bcel/util/BCELifierTest.java
b/src/test/java/org/apache/bcel/util/BCELifierTest.java
index cd605564..715b4016 100644
--- a/src/test/java/org/apache/bcel/util/BCELifierTest.java
+++ b/src/test/java/org/apache/bcel/util/BCELifierTest.java
@@ -177,6 +177,17 @@ class BCELifierTest extends AbstractTest {
}
}
+ @Test
+ void testClassNameRejectedWhenNotJavaIdentifier() {
+ // Class file names may contain characters the Java language forbids
(JVMS 4.2.2 bans only . ; [ /).
+ // BCELifier must refuse to emit such a name in identifier position
rather than let a crafted
+ // this_class inject statements into the generated source.
+ final ClassGen cg = new ClassGen("Evil {}\nclass Injected {}//",
"java.lang.Object", "Evil.java", Const.ACC_PUBLIC | Const.ACC_SUPER,
+ new String[] {});
+ final BCELifier bcelifier = new BCELifier(cg.getJavaClass(), new
ByteArrayOutputStream());
+ assertThrows(IllegalArgumentException.class, bcelifier::start);
+ }
+
@Test
void testClassNamesEscapedInOutput() throws Exception {
// Superclass and source file names are constant-pool derived and can
hold any UTF-8.
@@ -194,17 +205,6 @@ class BCELifierTest extends AbstractTest {
assertFalse(source.contains('"' + toEscapeSource + '"'), source);
}
- @Test
- void testClassNameRejectedWhenNotJavaIdentifier() {
- // Class file names may contain characters the Java language forbids
(JVMS 4.2.2 bans only . ; [ /).
- // BCELifier must refuse to emit such a name in identifier position
rather than let a crafted
- // this_class inject statements into the generated source.
- final ClassGen cg = new ClassGen("Evil {}\nclass Injected {}//",
"java.lang.Object", "Evil.java", Const.ACC_PUBLIC | Const.ACC_SUPER,
- new String[] {});
- final BCELifier bcelifier = new BCELifier(cg.getJavaClass(), new
ByteArrayOutputStream());
- assertThrows(IllegalArgumentException.class, bcelifier::start);
- }
-
private void testClassOnPath(final String javaClassFileName) throws
Exception {
final File workDir = new File("target", getClass().getSimpleName());
Files.createDirectories(workDir.getParentFile().toPath());