nodeca / js-yaml

JavaScript YAML parser and dumper. Very fast.
http://nodeca.github.io/js-yaml/
MIT License
6.32k stars 775 forks source link

Can't tokenize alias as key without seperator #319

Open stealthybox opened 7 years ago

stealthybox commented 7 years ago

js-yaml: @3.7.0 Node v7.2.1 Windows 10 Pro

disclaimer: I'm not an expert on the YAML spec

the bug

":" is a valid character when defining anchors and using aliases. It is valid to use aliases as keys.

":" is also used to denote a node. This seems to cause a bug where you are unable to use aliases as keys unless you separate the alias from the ":" with whitespace.

Trailing ":"'s look like they're being included in the alias token by the while loop in loader.readAlias(). I don't know the code very well, but a fix might need some extra logic in loader.composeNode().

some tests

const tests = [
  // working cases  (space before `:`)
  `
    adjective: &adj  stealthy!
    *adj :           box :)
  `,
  `
    adjective: &adj: stealthy!
    *adj: :          box :)
  `,
  `
    adjective: &:    stealthy!
    *: :             box :)
  `,
  // broken cases  (no space)
  `
    adjective: &adj  stealthy!
    *adj:            box :)
  `,
  `
    adjective: &adj: stealthy!
    *adj::           box :)
  `,
  `
    adjective: &:    stealthy!
    *::              box :)
  `,
]
for ( const content of tests )
  try{ console.log(require('js-yaml').load(content)) }
  catch(e) { console.log(e) }

output

{ adjective: 'stealthy!', 'stealthy!': 'box :)' }
{ adjective: 'stealthy!', 'stealthy!': 'box :)' }
{ adjective: 'stealthy!', 'stealthy!': 'box :)' }
{ Error
    at generateError (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:162:10)
    at throwError (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:168:9)
    at readAlias (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1244:5)
    at composeNode (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1336:20)
    at readBlockMapping (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1004:16)
    at composeNode (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1327:12)
    at readDocument (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1489:3)
    at loadDocuments (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1545:5)
    at Object.load (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1562:19)
    at Object.<anonymous> (C:\Users\leigh\repos\puppet\test-site\data\yaml.js:31:25)
  name: 'YAMLException',
  reason: 'unidentified alias "adj:"',
  mark:
   Mark {
     name: null,
     buffer: '\n    adjective: &adj  stealthy!\n    *adj:            box :)\n  \n\u0000',
     position: 41,
     line: 2,
     column: 9 },
  message: 'unidentified alias "adj:" at line 3, column 10:\n        *adj:            box :)\n             ^' }
{ Error
    at generateError (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:162:10)
    at throwError (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:168:9)
    at readAlias (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1244:5)
    at composeNode (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1336:20)
    at readBlockMapping (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1004:16)
    at composeNode (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1327:12)
    at readDocument (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1489:3)
    at loadDocuments (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1545:5)
    at Object.load (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1562:19)
    at Object.<anonymous> (C:\Users\leigh\repos\puppet\test-site\data\yaml.js:31:25)
  name: 'YAMLException',
  reason: 'unidentified alias "adj::"',
  mark:
   Mark {
     name: null,
     buffer: '\n    adjective: &adj: stealthy!\n    *adj::           box :)\n  \n\u0000',
     position: 42,
     line: 2,
     column: 10 },
  message: 'unidentified alias "adj::" at line 3, column 11:\n        *adj::           box :)\n              ^' }
{ Error
    at generateError (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:162:10)
    at throwError (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:168:9)
    at readAlias (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1244:5)
    at composeNode (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1336:20)
    at readBlockMapping (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1004:16)
    at composeNode (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1327:12)
    at readDocument (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1489:3)
    at loadDocuments (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1545:5)
    at Object.load (C:\Users\leigh\repos\puppet\test-site\data\node_modules\js-yaml\lib\js-yaml\loader.js:1562:19)
    at Object.<anonymous> (C:\Users\leigh\repos\puppet\test-site\data\yaml.js:31:25)
  name: 'YAMLException',
  reason: 'unidentified alias "::"',
  mark:
   Mark {
     name: null,
     buffer: '\n    adjective: &:    stealthy!\n    *::              box :)\n  \n\u0000',
     position: 39,
     line: 2,
     column: 7 },
  message: 'unidentified alias "::" at line 3, column 8:\n        *::              box :)\n           ^' }
perlpunk commented 6 years ago

This is an edge case. The spec is not very clear on that. @flyx mentioned that he had been asking about that on the mailing list two years ago: https://sourceforge.net/p/yaml/mailman/message/34909032/

I would generally add a space after an alias key to make sure it's portable. It should also be noted that libyaml and pyyaml do not support : anywhere in aliases/anchors.

When playing with that, it seems I found a bug in libyaml, pyyaml and snakeyaml: foo: &a:b bar is parsed the same as foo: &a :b bar

So I would avoid : in aliases completely.

stealthybox commented 6 years ago

@perlpunk In personal experience, I have not seen other usage in which people add spaces after their aliases to mitigate this problem, but it's good advice for compat /w this version of js-yaml.

I agree about disallowing : in aliases in the tokenizer. Since the spec is so lax on this point, it's probably a source of implementation specific divergences like you discovered.

Neat find!