Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,11 @@ public static String javaEscape(String o) {
* Utility for template use. Escapes comment end with HTML entities.
*/
public static String escapeForJavadoc(String s) {
return s.replace("*/", "*&#47;").replace("<", "&lt;").replace(">", "&gt;");
// Double backslashes first so a value cannot smuggle in a Unicode escape such
// as \\u002a\\u002f: javac processes Unicode escapes before comments, so an
// unneutralized \\u002a\\u002f would decode to */ inside the generated Javadoc
// and let a schema doc break out of the comment into code.
return s.replace("\\", "\\\\").replace("*/", "*&#47;").replace("<", "&lt;").replace(">", "&gt;");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,27 @@ void annotationCannotBreakOutViaStringLiteral() {
assertTrue(validAnnotationEmitted, "Valid annotation missing from generated output");
}

@Test
void docCannotBreakOutViaUnicodeEscape() {
// javac decodes Unicode escapes before it strips comments, so a schema doc
// carrying a backslash-u escape for the comment terminator decodes to that
// terminator inside the generated Javadoc and ends the comment early, turning
// the rest of the doc into code. The escaper must neutralize such escapes.
String jsonSchema = "{\n" + " \"type\": \"record\",\n" + " \"name\": \"DocInjected\",\n" + " \"fields\": [\n"
+ " {\"name\": \"value\", \"type\": \"string\", \"doc\": "
+ "\"\\\\u002a\\\\u002f public static int PWNED = 1; \\\\u002f\\\\u002a\"}\n" + " ]\n" + "}";
Collection<SpecificCompiler.OutputFile> outputs = new SpecificCompiler(SchemaParser.parseSingle(jsonSchema))
.compile();
// A Unicode escape is only decoded by javac when the leading backslash is
// preceded by an even number of backslashes. An eligible escape for a comment
// char in the generated source is the breakout; the doubled form is inert.
Pattern eligibleEscape = Pattern.compile("(?<!\\\\)(?:\\\\\\\\)*\\\\u002[afAF]");
for (SpecificCompiler.OutputFile outputFile : outputs) {
assertFalse(eligibleEscape.matcher(outputFile.contents).find(),
"Unicode-escape comment breakout present? " + outputFile.contents);
}
}

private int countOccurrences(Pattern pattern, String textToSearch) {
int count = 0;
for (Matcher matcher = pattern.matcher(textToSearch); matcher.find();) {
Expand Down