Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

CGI::Cookie and CookieStore performing unnecessary work causing interoperability bug with Merb

#130

CookieStore is currently escaping and unescaping while this is handled in other places in ActionController. This means double the work which does, in some cases result in different output.

CGI::Cookie had a bug in it's handling of a Hash parameter. It handled a string value with newlines in it differently depending on whether it was handed a Hash or not. Given a Hash with a string value containing newlines, the value would be split into multiple values. This resulted in '&' being inserted into the CookieStore values due to how multiple cookie values get concatenated.

Both of these issues caused interoperability bugs with my Merb application that shared the CookieStore with the Rails app.

All existing Rails tests pass with these patches. I've added tests where appropriate. These minor fixes did correct all related issues with my applications.

Reported by Rich Cavanaugh · May 7th, 2008 @ 05:58 AM

State: resolved
Milestone: 2.1.1
Assigned to: Jeremy Kemper Jeremy Kemper
Importance: none

Activity

  1. Pratik
    Pratik
    • State changed from new to invalid

    This is not a backward compatible change and will cause all existing cookie sessions to raise CGI::Session::CookieStore::TamperedWithCookie exception.

    Thanks.

    May 11th, 2008 @ 10:33 PM

  2. Rich Cavanaugh
    Rich Cavanaugh

    It would be simple enough to make this work with existing cookies. Would a patch implementing this be likely to be accepted?

    May 11th, 2008 @ 11:19 PM

  3. Pratik
    Pratik
    • Assigned user set to Jeremy Kemper

    May 11th, 2008 @ 11:23 PM

  4. Rich Cavanaugh
  5. Rich Cavanaugh
    Rich Cavanaugh

    Here's a patch that passes all of rails' tests, my application specific tests and is backwards compatible (with tests for that too).

    May 12th, 2008 @ 07:27 PM

  6. Jeremy Kemper
    Jeremy Kemper
    • State changed from invalid to open
    -        @value = Array(name['value'])
    +        @value = name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])
    

    Why? Array('foo') == ['foo']

    May 12th, 2008 @ 09:28 PM

  7. Rich Cavanaugh
    Rich Cavanaugh

    @value is expected to be an array but if you pass "one\ntwo" to Array you get:

    Array("one\ntwo") # => ["one\n", "two"]

    Which gets written out in the cookie header to "one%0A&two" (notice the & used for joining the separate elements). This issue, when used with other systems without this behavior (in my case merb), causes digest mis-matches.

    Array("one\ntwo") != ["one\ntwo"] # => true

    May 12th, 2008 @ 09:55 PM

  8. Jeremy Kemper
    Jeremy Kemper

    Ah, I see. Could you include a test for that case? The patch looks good, otherwise.

    May 12th, 2008 @ 10:03 PM

  9. Rich Cavanaugh
    Rich Cavanaugh

    I actually did, it's called test_cookies_should_not_be_split_on_values_with_newlines

    May 12th, 2008 @ 10:05 PM

  10. Jeremy Kemper
    Jeremy Kemper

    Sorry, I missed that. I have mixed feelings about changing this behavior for all cookie values. Backward compatibility applies to the cookie store only.

    May 12th, 2008 @ 10:39 PM

  11. Rich Cavanaugh
    Rich Cavanaugh

    Yeah, I agree but, taken in context CGI::Cookie is plainly broken and acting differently depending on how you initialize.

    When name is a string the value is already an array so:

    CGI::Cookie.new("avar", "one\ntwo").value # => ["one\ntwo"]

    While:

    CGI::Cookie.new(:name => "avar", :value => "one\ntwo").value # => ["one\n", "two"]

    That's why I didn't make the same change to the String path in CGI::Cookie#initialize because the variable argument *value is always an array and Array doesn't mangle an array's values the same way it does strings.

    So the behavior is already inconsistent and this patch simply makes the initializer act consistently.

    May 12th, 2008 @ 10:52 PM

  12. Jeremy Kemper
    Jeremy Kemper

    I buy that. Thanks for clarifying, Rich.

    May 12th, 2008 @ 11:14 PM

  13. Repository
    Repository
    • State changed from open to resolved

    (from [a425cd147363a0e8d7e17177ef252dd760197f15]) Don't double-escape cookie store data. Don't split cookie values with newlines into an array. [#130 state:resolved]

    Signed-off-by: Jeremy Kemper

    http://github.com/rails/rails/co...

    May 12th, 2008 @ 11:27 PM

  14. Tarmo Tänav
    Tarmo Tänav

    This change seems to change the behavior of deleting a cookie. When a cookie is deleted it's value is set to an empty string. Previously that would have meant:

    Array('') => []

    but now it is turned into: ['']

    I'm not really familiar with how cookies are supposed to behave so I'm not sure where the bug is, but for example with the restful_authentication plugin generated code what happens is:

    1. action does 'cookies.delete :auth_token'

    2. test fails on 'assert_equal @response.cookies["auth_token"], []' with [""] not being equal to []

    Is this expected?

    May 13th, 2008 @ 05:04 PM

  15. Rich Cavanaugh
    Rich Cavanaugh

    I've attached a new patch addressing this issue along with a test for it.

    May 13th, 2008 @ 05:21 PM

  16. Jeremy Kemper