allthingssecurity opened a new pull request, #28711: URL: https://github.com/apache/camel/pull/28711
# Description [CAMEL-26306](https://issues.apache.org/jira/browse/CAMEL-26306) `AS2SessionInputBuffer.readLine` reported a line as ending with CRLF (`isLastLineReadEnrichedByCarriageReturn()`) only when the CR was in the read buffer right before the LF (`i > 0 && buffer[i - 1] == CR`). When the CR was the last byte of one read and the LF the first byte of the next, the CR was already in the line buffer: the line itself was right, but it was reported as ending with a bare LF. `EntityParser.parseBodyPartText` rebuilds the content of a body part from the lines and these flags (since CAMEL-15111, Camel 3.18.5 and 4.0, LF and CRLF line endings are kept as received), so that CRLF became LF in the EDI message (`parseEDIEntityBody` -> `parseBodyPartBytes`), in a text/plain part (the human readable part of an MDN) and in the other parts read through `parseBodyPartBytes`. When that CRLF was the one before a delimiter line, `parseBodyPartText` removed two characters for it (`setLength(length - 2)`), so the last byte of the content was lost instead. The line reader reads the entity content 8 KB at a time. The server parses the received body from a `ByteArrayInputStream` (`AS2BHttpServerConnection.receiveRequestEntity`), and the decrypted or decompressed content of an encrypted or compressed message is parsed the same way (`EntityParser.parseEntity`), so this happened for every CRLF whose CR is the last byte of an 8 KB read: offset 8191, 16383, ... of the body (of the decrypted or decompressed content for those). With 80 byte lines about one read in 80 ends that way, so an EDI file of 1 MB is more likely than not to be changed. The client parses an MDN response straight from the connection, where a read can end at any byte (not tested). What it does to a message: - signed: the signature is verified over the parsed content (`SigningUtils.isValid`), so verification fails and the message is rejected ("Failed to validate the signature") when `validateSigningCertificateChain` is set. The new signed test checks that this signature check fails on main. - unsigned: the route gets the changed content. The MIC in the MDN is computed over the changed content too (`MicUtils.createReceivedContentMic`), so it does not match the sender's MIC (reasoned from the code, not tested). Changes: - `readLine`: when the LF is the first byte of the read buffer, the CR is taken from the end of the line buffer. Otherwise the byte before the LF must belong to this line (`i > bufferpos`), so a CR consumed before the line by `read(InputStream)` is no longer taken for its line ending either. - No option, default or API changes; no upgrade guide entry. The same check is on camel-4.22.x and 4.18.x (read, not run); the change is local to `readLine` and can be backported. On 4.18.x `AS2BHttpServerConnection.receiveRequestEntity` has no `ByteArrayInputStream` copy: the server parses the body straight from the connection, so there a read can end at any byte (read, not run). Tests: - New `AS2SessionInputBufferTest`: a CRLF split between two reads of a chunked stream, a CR that is the last byte of a full 8 byte read buffer, and a CR consumed by `read` before an empty line that ends with a bare LF. All three fail on main and pass with the fix. - New `EntityParserContentLineEndingTest.parseEdiMessageKeepsACrLfSplitByTheReadBuffer`: an unsigned `application/edifact` request through `EntityParser.parseAS2MessageEntity` (the call the server connection makes) whose first line is 8191 bytes, so its CR is the last byte of the first 8 KB read. It fails on main (the CR at offset 8191 is missing from `getEdiMessage()`) and passes with the fix. - New `EntityParserContentLineEndingTest.signedEdiMessageWithACrLfSplitByTheReadBufferKeepsItsSignature` (2 cases): a `multipart/signed` EDI message whose body has a CR at offset 8191, inside the EDI content or before the delimiter line that follows it. On main the signature check of the parsed message fails for both (and the content has a CR less, or its last byte less); with the fix the signature is valid and the content is unchanged. - camel-as2-api: 117 tests, 0 failures. camel-as2-component: 157 tests (the 45 unit tests and the 112 tests of the `*IT` classes, which start the AS2 client and server in the JVM), 0 failures, 1 skipped. The core modules were built from this branch, as the local snapshot jars are older; `httpclient-version` 5.6.4 locally (5.6.5 is not in the offline repository). The formatter and import-sort plugins ran. How it was found: an OSS-Fuzz-style harness for the MIME boundary handling that Camel does itself (`EntityParser.parseBodyPartText` on `AS2SessionInputBuffer`, property: the text before the first delimiter line, whatever the read chunking), run on HotSpot with seeded random and grammar-aware inputs, found it in its first second; the minimised input is a body of CR and LF delivered in two reads. A JVM model in Rocq that executes the real class files then states it as kernel-checked theorems on the class files of main and of this change (`main_split_crlf_reported_as_lf`: "a CR LF b CR LF" read as "a CR" + "LF b CR LF" reports the first line with a bare LF; `fix_split_crlf_reported_as_crlf`). HotSpot gives the same results for the same class files. # Target - [x] I checked that the commit is targeting the correct branch (Camel 4 uses the `main` branch) # Tracking - [x] If this is a large change, bug fix, or code improvement, I checked there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL-26306) filed for the change (usually before you start working on it): [CAMEL-26306](https://issues.apache.org/jira/browse/CAMEL-26306). # Apache Camel coding standards and style - [x] I checked that each commit in the pull request has a meaningful subject line and body. - [ ] I have run `mvn clean install -DskipTests` locally from root folder and I have committed all auto-generated changes. (I built the core modules and camel-as2 and ran the tests above, including the formatter and import-sort plugins. No generated file changes. I did not run the full root build.) # AI-assisted contributions - [x] If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR description identifies the AI tool used. This PR was prepared with Claude Code (Claude Opus 5.5). The commit carries a `Co-Authored-By` trailer. _Claude Code on behalf of allthingssecurity_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
