shashank created CAMEL-25150:
--------------------------------

             Summary: camel-bindy - with quotingEscaped=true a CSV field whose 
value ends with a quote (He said "hi", 12") is not closed on unmarshal, so it 
swallows the next fields and the columns shift (regression of CAMEL-22068)
                 Key: CAMEL-25150
                 URL: https://issues.apache.org/jira/browse/CAMEL-25150
             Project: Camel
          Issue Type: Bug
          Components: camel-bindy
            Reporter: shashank


With {{@CsvRecord(quoting = true, quotingEscaped = true)}} Bindy writes a quote 
inside a field as a backslash and the quote (CAMEL-7519): the value {{He said 
"hi"}} is written {{"He said \"hi\""}}. Unmarshal cannot read this field back 
when the quote character is the double quote and the value ends with an odd 
number of quotes:

{noformat}
line:      "123","He said \"hi\"","10"
expected:  firstField=123, secondField=He said "hi", number=10
actual:    firstField=123, secondField=He said "hi"",10  (the third column is 
merged into the second), number=null
{noformat}

The cause is the loop that CAMEL-22068 added to 
{{BindyCsvDataFormat.unquoteTokens}} to support RFC 4180 doubled quotes: for a 
token that ends with the quote, it counts the quotes in front of the final one, 
skipping backslashes, and treats the final quote as escaped when the count is 
odd. That is the RFC 4180 rule ({{""}} is one quote in the value), but it also 
runs when {{quotingEscaped=true}}, where the value's quote is written {{\"}}. 
For {{...\""}} the loop skips the backslash, counts one quote, decides that the 
closing quote is escaped, and keeps the field open until a later token ends 
with a quote. The result is silent data corruption: the value gets the 
separator and the following columns appended, the following fields are shifted 
or empty, and a typed field (number, date) that receives the wrong text fails.

Values with an even number of trailing quotes (the value {{""foo""}} of the 
existing test) and values without a trailing quote are read correctly, which is 
why the existing tests pass. The single quote as quote character is not 
affected, because the loop only runs for the double quote.

Before 4.8.8 / 4.10.5 / 4.12.0 a token ending with the quote always closed the 
field, and these values were read back correctly. A brute force over all values 
of up to 3 characters from {{a , " \ space}} in each of three columns (marshal, 
then unmarshal): 120 of 468 records fail today, 33 with the unquote code of 
4.10.0, 33 with the fix below. The 33 remaining ones are values with an escaped 
quote directly in front of the separator ({{a",b}}), which already failed 
before CAMEL-22068 and are not a regression.

h3. Reproduction

{code:java}
@CsvRecord(separator = ",", quote = "\"", quoting = true, quotingEscaped = true)
public static class Row {
    @DataField(pos = 1) private String firstField;
    @DataField(pos = 2) private String secondField;
    @DataField(pos = 3, pattern = "########.##") private BigDecimal number;
}
// marshal of (123, He said "hi", 10) gives "123","He said \"hi\"","10"
// unmarshal of that line gives secondField = He said "hi"",10 and number = null
{code}

A unit test fails on main (three runs) for the values {{He said "hi"}}, 
{{12"}}, {{"}} and {{a "b" "c"}}. A small formal model (Lean 4) of the loop 
shows that for every value that ends with {{n}} quotes after another character 
the field is closed exactly when {{n}} is even, and that with the fix every 
quoted field without a separator in it is read back as written.

h3. Affected versions

The loop is in 4.8.8, 4.10.5, 4.12.0 and every later release up to main; 4.8.7 
and 4.10.4 do not have it.

h3. Proposed fix

Apply the doubled-quote rule only when {{quotingEscaped}} is false:

{code:java}
if (quote.equals("\"") && !quotingEscaped) {
    // RFC 4180 doubled quotes
    ...
}
{code}

With {{quotingEscaped=true}} a token that ends with the quote closes the field 
again, as before CAMEL-22068. RFC 4180 input ({{quotingEscaped=false}}, the 
default) is read exactly as today, including the cases of CAMEL-22068. Values 
that end with a backslash ({{C:\temp\}}) keep working.

The one input that is read differently is RFC 4180 style input in 
{{quotingEscaped=true}} mode with a separator inside the quotes and an odd 
number of quotes in front of it ({{"x"",y"}}): today it stays one field (with 
the doubled quote kept, since this mode does not unescape {{""}}), with the fix 
it is split, as in 4.8.7 and 4.10.4. Such input is not the format 
{{quotingEscaped}} describes.

Duplicate check (2026-09-30): JIRA "quotingEscaped", "bindy" with "escape" and 
"quote": CAMEL-7519, CAMEL-12274, CAMEL-22068, CAMEL-19143, all fixed, none 
about this case. No open pull request touches {{unquoteTokens}}.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to