3 ms·
> The ruby 2.1 behavior of serializing out as a Time but then parsing back in to a String anyway -- seems particularly sketchy, and ripe for roundtrip bugs wher
by tenderlove 12y ago
> The ruby 2.1 behavior of serializing out as a Time but then parsing back in to a String anyway -- seems particularly sketchy, and ripe for roundtrip bugs where the string it gets turned into on parse isn't _quite_ the same one that was original before serialization. (Say, winds up with a different timezone or something).
It's not serializing it out as a time, but serializing it as an "implicit string" (a string with no quotes). When Psych goes to dump the string out, it checks to see if the string could be interpreted as something else. Since the string being dumped doesn't match a YAML timestamp, it doesn't add explicit quoting on the string.
Here's an example. From the blog post, they are doing this:
Psych.dump Time.now.utc.strftime("%Y-%m-%d %H:%M:%S.%6N %Z")
Output:
"--- 2014-05-23 15:42:29.882127 UTC\n...\n"
The `strftime` adds the string "UTC" to the timestamp. But "UTC" isn't part of the [yaml timestamp spec](http://yaml.org/type/timestamp.html http://yaml.org/type/timestamp.html). Since it doesn't match a YAML timestamp, Psych dumps it out as an implicit string.
If we change the `strftime` to produce a string that does look like a YAML timestamp like this:
Psych.dump Time.now.utc.strftime("%Y-%m-%d %H:%M:%S.%6N %z")
Output:
"--- '2014-05-23 15:46:31.197338 +0000'\n"
Then the output is quoted so that when we load it isn't ambiguous (quoted strings are always considered strings, and are never candidates for coercion). If we were to manually modify that YAML and remove the quotes, it is indeed converted to a Time object:
irb(main):012:0> Psych.load "--- '2014-05-23 15:46:31.197338 +0000'\n"
=> "2014-05-23 15:46:31.197338 +0000"
irb(main):013:0> Psych.load "--- 2014-05-23 15:46:31.197338 +0000\n"
=> 2014-05-23 08:46:31 -0700
The article says:
>The same would happen in Psych 2.0.5 if it were not for a new feature called Safe Load which was introduced after the recent Rails YAML deserialisation security vulnerabilities.
I have no idea where they got this. You have to explicitly call `safe_load`, and it doesn't sound like they're doing that. If your format the timestamp correctly in your YAML, it will happily deserialize as a Time object. :-/
- jrochkind1 12y agoSo, the fact that there's such thing as an 'implicit string' in the YAML data model, and that I didn't know this, and that I still don't completely understand how it works even after reading your comment (thanks though!)... ...is leading me back to suggesting YAMLs data model is overly complex, and not understandable with a simple developer mental model, and that's the root of a lot of problems. I previously thought leaving quotes off string literals in YAML was simply a convenience for hand-writing YAML and producing YAML with less noise for human readability; I didn't realize it resulted in an 'implicit string' which would be de-serialized by YAML parsers differently than quoted strings. (Are these patterns by which 'implicit strings' are recognized to be coerceable to certain types part of the YAML standard, or up to the particular YAML parsing implementation as to how/whether to do it? If the latter... woah, just asking for trouble, no wonder we got it.) update Oh wait, I just realized I totally misunderstood what you were saying. Strings are Strings, but on serializing, the serializer is supposed to ensure quotes are used if the string, were it unquoted would be misinterpreted as something other than a string. Okay, seems reasonable -- but OP is reporting this is failing somehow, right? And in some cases something meant to be a string is winding up unquoted, and being interpreted as something else on load? Or I'm probably still not quite getting it. Okay, yeah, tldr, we can stick with: YAML's data/processing model ends up being way too complex making it's behavior hard to predict and hard to maintain consistent between parsers/versions.
- tenderlove 12y ago> And in some cases something meant to be a string is winding up unquoted, and being interpreted as something else on load? In this particular case, a timestamp with "UTC" in it should not be interpreted as a timestamp, and older versions of psych would interpret it as a timestamp even though the spec says it's not valid. > Okay, yeah, tldr, we can stick with: YAML's data/processing model ends up being way too complex making it's behavior hard to predict and hard to maintain consistent between parsers/versions. Absolutely. If we eliminated implicit strings, it would make life lots easier. OTOH, you could just use JSON. :-D
- jrochkind1 12y agoMe, I'd keep implicit Strings (meaning optional to use quotes, for convenience), but stick to JSON-compatible data types. For most cases where YAML is used, what's really wnated is something that is pretty much exactly as expressive as json (no more), but easier and neater to write/read by hand. Of course, I realize the devil is in the details, and that may be what those developing YAML thought they were doing, but always adding just one more tiny thing that would be useful too. Complexity vs flexibility, one of the fundamental tensions of software design. I see how we got here through good intentions, but in retrospect YAML made some decisions on this balance that most of us would do differently. Or, did those designing YAML realize what a fundamental part of the ruby ecosystem it was to become, and what the implications of that were? Maybe part of the lesson is that some things really do need to be carefully designed, not just iteratively designed "you can always fix it later" by an ever-changing community of volunteers; is that part of what happened with YAML spec and implementations and uses? Also, thanks for explaining the bug-fix as part of the story, that makes a lot more sense. I understand that bug fixes will inevitably sometimes be backward-incompat. I think it has maybe been consistently under-estimated how important/useful it would to allow upgrades of ruby itself while keeping the exact same YAML processor and version. The psych/syck stuff ended up being one of the biggest pain points for many people way back in 1.8.7->1.9.3 too. Most gems ended up compatible with multiple ruby versions, so you could upgrade ruby versions while keeping your gem dependencies identical. YAML serializing/parsing, so fundamental, where minor differences in implementation can have disastrous consequences, was a painful exception.