Skip to content

AVRO-4313: [java] Tighten javaAnnotation string-literal validation in SpecificCompiler - #3892

Merged
RyanSkraba merged 3 commits into
apache:mainfrom
iemejia:AVRO-4313
Jul 26, 2026
Merged

AVRO-4313: [java] Tighten javaAnnotation string-literal validation in SpecificCompiler#3892
RyanSkraba merged 3 commits into
apache:mainfrom
iemejia:AVRO-4313

Conversation

@iemejia

@iemejia iemejia commented Jul 20, 2026

Copy link
Copy Markdown
Member

What is the purpose of the change

The javaAnnotation validator in SpecificCompiler uses a regular expression
(PATTERN_STRING) whose string-literal grammar accepts an unescaped quote
inside the literal body. As a result a single string literal can extend past
its intended closing quote and absorb surrounding tokens, so a crafted
javaAnnotation value can full-match the IDENTIFIER(STRING) shape while
actually carrying extra Java declarations. Because the record template emits
annotation values verbatim, such a value would be written directly into the
generated specific-record source.

This tightens the string-literal grammar so the body may only contain
recognized escape sequences or characters that are not a quote, backslash, or
line terminator. Legitimate annotation values (for example
SuppressWarnings("unchecked"), Deprecated(forRemoval = true, since = "forever"),
and values with escaped quotes) continue to validate.

Fixes AVRO-4313.

Verifying this change

This change added tests and can be verified as follows:

  • Added annotationCannotBreakOutViaStringLiteral in TestSpecificCompiler,
    which feeds a crafted javaAnnotation value and asserts it is not emitted
    verbatim into the generated source, while a valid annotation in the same
    list is still emitted. The test fails against the previous grammar and
    passes with the tightened one.
  • The existing docsAreEscaped_avro4053 test and the full compiler module
    test suite continue to pass.

Documentation

  • Does this pull request introduce a new feature? (no)
  • If yes, how is the feature documented? (not applicable)

… SpecificCompiler

The string-literal grammar used to validate javaAnnotation values accepted
an unescaped quote inside the literal body, letting a single literal span
past its intended closing quote and absorb surrounding tokens. Constrain the
body to recognized escape sequences or characters that are not a quote,
backslash, or line terminator, and add a regression test.
@github-actions github-actions Bot added the Java Pull Requests for Java binding label Jul 20, 2026
@iemejia
iemejia requested a review from Copilot July 20, 2026 17:17

Copilot AI left a comment

Copy link
Copy Markdown

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 addresses AVRO-4313 by tightening validation of javaAnnotation values in the Java SpecificCompiler, preventing crafted annotation strings from abusing overly-permissive string-literal matching to inject additional Java tokens into generated sources.

Changes:

  • Tighten the PATTERN_STRING regex used by SpecificCompiler annotation validation to disallow unescaped quotes/backslashes and line breaks in string literals.
  • Add a regression test that attempts to inject extra declarations via a crafted javaAnnotation value and asserts it is not emitted verbatim, while a valid annotation still is.

Reviewed changes

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

File Description
lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java Tightens the regex used to validate annotation string literals to prevent quote-based breakout/injection.
lang/java/compiler/src/test/java/org/apache/avro/compiler/specific/TestSpecificCompiler.java Adds a regression test ensuring crafted javaAnnotation values can’t escape a string literal and inject code into generated output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Also reject NEL, LS and PS in addition to CR and LF so an annotation value
cannot span multiple lines in the generated source.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

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

Comment on lines +1045 to +1056
Collection<SpecificCompiler.OutputFile> outputs = new SpecificCompiler(SchemaParser.parseSingle(jsonSchema))
.compile();
for (SpecificCompiler.OutputFile outputFile : outputs) {
// The payload is echoed (safely escaped) inside the SCHEMA$ string constant,
// so we must distinguish that from a verbatim emission as code. Real injected
// code would carry unescaped quotes; the schema literal escapes them as \".
assertFalse(outputFile.contents.contains("SuppressWarnings(\"x\") static { System.exit(1); }"),
"Code injection present? " + outputFile.contents);
// The legitimate annotation in the same list must still be emitted.
assertTrue(outputFile.contents.contains("@SuppressWarnings(\"unchecked\")"),
"Valid annotation missing? " + outputFile.contents);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 90299cc. The loop now asserts the injection payload is absent from every generated file, and separately asserts the valid annotation is emitted in at least one output (via an accumulator checked after the loop), so the test no longer depends on the number of output files.

Assert the injection payload is absent from every generated file and the
valid annotation is emitted in at least one, rather than requiring it in
each output file.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@iemejia
iemejia requested a review from RyanSkraba July 20, 2026 18:04
@RyanSkraba
RyanSkraba merged commit 44f7aa3 into apache:main Jul 26, 2026
9 checks passed
@RyanSkraba

Copy link
Copy Markdown
Contributor

Cherry-picked to branch-1.12.

RyanSkraba pushed a commit that referenced this pull request Jul 26, 2026
… SpecificCompiler (#3892)

* AVRO-4313: [java] Tighten javaAnnotation string-literal validation in SpecificCompiler

The string-literal grammar used to validate javaAnnotation values accepted
an unescaped quote inside the literal body, letting a single literal span
past its intended closing quote and absorb surrounding tokens. Constrain the
body to recognized escape sequences or characters that are not a quote,
backslash, or line terminator, and add a regression test.

* AVRO-4313: Exclude all line terminators from annotation string literals

Also reject NEL, LS and PS in addition to CR and LF so an annotation value
cannot span multiple lines in the generated source.

* AVRO-4313: Make injection regression test robust to multiple outputs

Assert the injection payload is absent from every generated file and the
valid annotation is emitted in at least one, rather than requiring it in
each output file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Java Pull Requests for Java binding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants