diff --git a/CHANGELOG.md b/CHANGELOG.md index f07f612b..3f1cc985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +3.13.1 / 2019-04-05 +------------------- + +- Fix possible code execution in (already unsafe) `.load()`, #480. + + +3.13.0 / 2019-03-20 +------------------- + +- Security fix: `safeLoad()` can hang when arrays with nested refs + used as key. Now throws exception for nested arrays. #475. + + +3.12.2 / 2019-02-26 +------------------- + +- Fix `noArrayIndent` option for root level, #468. + + +3.12.1 / 2019-01-05 +------------------- + +- Added `noArrayIndent` option, #432. + + 3.12.0 / 2018-06-02 ------------------- diff --git a/README.md b/README.md index c165c357..03a9e5ae 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ options: - `filename` _(default: null)_ - string to be used as a file path in error/warning messages. - `onWarning` _(default: null)_ - function to call on warning messages. - Loader will throw on warnings if this function is not provided. + Loader will call this function with an instance of `YAMLException` for each warning. - `schema` _(default: `DEFAULT_SAFE_SCHEMA`)_ - specifies a schema to use. - `FAILSAFE_SCHEMA` - only strings, arrays and plain objects: http://www.yaml.org/spec/1.2/spec.html#id2802346 @@ -170,6 +170,7 @@ disable exceptions by setting the `skipInvalid` option to `true`. options: - `indent` _(default: 2)_ - indentation width to use (in spaces). +- `noArrayIndent` _(default: false)_ - when true, will not add an indentation level to array elements - `skipInvalid` _(default: false)_ - do not throw on invalid types (like function in the safe schema) and skip pairs and single values with such types. - `flowLevel` (default: -1) - specifies level of nesting, when to switch from diff --git a/dist/js-yaml.js b/dist/js-yaml.js index 0c9e648b..fad044a4 100644 --- a/dist/js-yaml.js +++ b/dist/js-yaml.js @@ -1,4 +1,4 @@ -/* js-yaml 3.12.0 https://github.com/nodeca/js-yaml */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsyaml = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0)) ? level - 1 : level; if (block && (state.dump.length !== 0)) { - writeBlockSequence(state, level, state.dump, compact); + writeBlockSequence(state, arrayLevel, state.dump, compact); if (duplicate) { state.dump = '&ref_' + duplicateIndex + state.dump; } } else { - writeFlowSequence(state, level, state.dump); + writeFlowSequence(state, arrayLevel, state.dump); if (duplicate) { state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; } @@ -1005,6 +1007,8 @@ var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; +function _class(obj) { return Object.prototype.toString.call(obj); } + function is_EOL(c) { return (c === 0x0A/* LF */) || (c === 0x0D/* CR */); } @@ -1260,6 +1264,31 @@ function mergeMappings(state, destination, source, overridableKeys) { function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) { var index, quantity; + // The output is a plain object here, so keys can only be strings. + // We need to convert keyNode to a string, but doing so can hang the process + // (deeply nested arrays that explode exponentially using aliases). + if (Array.isArray(keyNode)) { + keyNode = Array.prototype.slice.call(keyNode); + + for (index = 0, quantity = keyNode.length; index < quantity; index += 1) { + if (Array.isArray(keyNode[index])) { + throwError(state, 'nested arrays are not supported inside keys'); + } + + if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') { + keyNode[index] = '[object Object]'; + } + } + } + + // Avoid code execution in load() via toString property + // (still use its own toString for arrays, timestamps, + // and whatever user schema extensions happen to have @@toStringTag) + if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') { + keyNode = '[object Object]'; + } + + keyNode = String(keyNode); if (_result === null) { diff --git a/dist/js-yaml.min.js b/dist/js-yaml.min.js index 0dcb7bac..0623500e 100644 --- a/dist/js-yaml.min.js +++ b/dist/js-yaml.min.js @@ -1 +1 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).jsyaml=e()}}(function(){return function o(a,s,c){function u(t,e){if(!s[t]){if(!a[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(l)return l(t,!0);var i=new Error("Cannot find module '"+t+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[t]={exports:{}};a[t][0].call(r.exports,function(e){return u(a[t][1][e]||e)},r,r.exports,o,a,s,c)}return s[t].exports}for(var l="function"==typeof require&&require,e=0;e=i.flowLevel;switch(H(r,n,i.indent,t,function(e){return function(e,t){var n,i;for(n=0,i=e.implicitTypes.length;n"+V(r,i.indent)+Z(L(function(e,t){var n,i,r=/(\n+)([^\n]*)/g,o=(s=e.indexOf("\n"),s=-1!==s?s:e.length,r.lastIndex=s,z(e.slice(0,s),t)),a="\n"===e[0]||" "===e[0];var s;for(;i=r.exec(e);){var c=i[1],u=i[2];n=" "===u[0],o+=c+(a||n||""===u?"":"\n")+z(u,t),a=n}return o}(r,t),e));case $:return'"'+function(e){for(var t,n,i,r="",o=0;ot&&o tag resolver accepts not "'+c+'" style');i=s.represent[c](t,c)}e.dump=i}return!0}return!1}function Q(e,t,n,i,r,o){e.tag=null,e.dump=n,J(e,n,!1)||J(e,n,!0);var a=l.call(e.dump);i&&(i=e.flowLevel<0||e.flowLevel>t);var s,c,u="[object Object]"===a||"[object Array]"===a;if(u&&(c=-1!==(s=e.duplicates.indexOf(n))),(null!==e.tag&&"?"!==e.tag||c||2!==e.indent&&0 "+e.dump)}return!0}function X(e,t){var n,i,r=[],o=[];for(function e(t,n,i){var r,o,a;if(null!==t&&"object"==typeof t)if(-1!==(o=n.indexOf(t)))-1===i.indexOf(o)&&i.push(o);else if(n.push(t),Array.isArray(t))for(o=0,a=t.length;ot)&&0!==i)_(e,"bad indentation of a sequence entry");else if(e.lineIndentt?d=1:e.lineIndent===t?d=0:e.lineIndentt?d=1:e.lineIndent===t?d=0:e.lineIndentt)&&(K(e,t,b,!0,r)&&(m?d=e.result:h=e.result),m||(D(e,l,p,f,d,h,o,a),f=d=h=null),q(e,!0,-1),s=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==s)_(e,"bad indentation of a mapping entry");else if(e.lineIndentl&&(l=e.lineIndent),j(o))p++;else{if(e.lineIndent>10),56320+(c-65536&1023)),e.position++}else _(e,"unknown escape sequence");n=i=e.position}else j(s)?(T(e,n,i,!0),R(e,q(e,!1,t)),n=i=e.position):e.position===e.lineStart&&Y(e)?_(e,"unexpected end of the document within a double quoted scalar"):(e.position++,i=e.position)}_(e,"unexpected end of the stream within a double quoted scalar")}(e,p)?m=!0:!function(e){var t,n,i;if(42!==(i=e.input.charCodeAt(e.position)))return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!I(i)&&!E(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&_(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),e.anchorMap.hasOwnProperty(n)||_(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],q(e,!0,-1),!0}(e)?function(e,t,n){var i,r,o,a,s,c,u,l,p=e.kind,f=e.result;if(I(l=e.input.charCodeAt(e.position))||E(l)||35===l||38===l||42===l||33===l||124===l||62===l||39===l||34===l||37===l||64===l||96===l)return!1;if((63===l||45===l)&&(I(i=e.input.charCodeAt(e.position+1))||n&&E(i)))return!1;for(e.kind="scalar",e.result="",r=o=e.position,a=!1;0!==l;){if(58===l){if(I(i=e.input.charCodeAt(e.position+1))||n&&E(i))break}else if(35===l){if(I(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&Y(e)||n&&E(l))break;if(j(l)){if(s=e.line,c=e.lineStart,u=e.lineIndent,q(e,!1,-1),e.lineIndent>=t){a=!0,l=e.input.charCodeAt(e.position);continue}e.position=o,e.line=s,e.lineStart=c,e.lineIndent=u;break}}a&&(T(e,r,o,!1),R(e,e.line-s),r=o=e.position,a=!1),S(l)||(o=e.position+1),l=e.input.charCodeAt(++e.position)}return T(e,r,o,!1),!!e.result||(e.kind=p,e.result=f,!1)}(e,p,x===n)&&(m=!0,null===e.tag&&(e.tag="?")):(m=!0,null===e.tag&&null===e.anchor||_(e,"alias node should not have any properties")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===d&&(m=s&&B(e,f))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(c=0,u=e.implicitTypes.length;c tag; it should be "'+l.kind+'", not "'+e.kind+'"'),l.resolve(e.result)?(e.result=l.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):_(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):_(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||m}function $(e){var t,n,i,r,o=e.position,a=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(r=e.input.charCodeAt(e.position))&&(q(e,!0,-1),r=e.input.charCodeAt(e.position),!(0t/2-1){n=" ... ",i+=5;break}for(r="",o=this.position;ot/2-1){r=" ... ",o-=5;break}return a=this.buffer.slice(i,o),s.repeat(" ",e)+n+a+r+"\n"+s.repeat(" ",e+this.position-i+n.length)+"^"},i.prototype.toString=function(e){var t,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet())&&(n+=":\n"+t),n},t.exports=i},{"./common":2}],7:[function(e,t,n){"use strict";var i=e("./common"),r=e("./exception"),o=e("./type");function a(e,t,i){var r=[];return e.include.forEach(function(e){i=a(e,t,i)}),e[t].forEach(function(n){i.forEach(function(e,t){e.tag===n.tag&&e.kind===n.kind&&r.push(t)}),i.push(n)}),i.filter(function(e,t){return-1===r.indexOf(t)})}function s(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach(function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new r("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=a(this,"implicit",[]),this.compiledExplicit=a(this,"explicit",[]),this.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{}};function i(e){n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;e>16&255),s.push(a>>8&255),s.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return 0==(n=r%4*6)?(s.push(a>>16&255),s.push(a>>8&255),s.push(255&a)):18===n?(s.push(a>>10&255),s.push(a>>2&255)):12===n&&s.push(a>>4&255),c?c.from?c.from(s):new c(s):s},predicate:function(e){return c&&c.isBuffer(e)},represent:function(e){var t,n,i="",r=0,o=e.length,a=u;for(t=0;t>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return 0==(n=o%3)?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}})},{"../type":13}],15:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)},construct:function(e){return"true"===e||"True"===e||"TRUE"===e},predicate:function(e){return"[object Boolean]"===Object.prototype.toString.call(e)},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},{"../type":13}],16:[function(e,t,n){"use strict";var i=e("../common"),r=e("../type"),o=new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");var a=/^[-+]?[0-9]+e/;t.exports=new r("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!o.test(e)||"_"===e[e.length-1])},construct:function(e){var t,n,i,r;return n="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,r=[],0<="+-".indexOf(t[0])&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:0<=t.indexOf(":")?(t.split(":").forEach(function(e){r.unshift(parseFloat(e,10))}),t=0,i=1,r.forEach(function(e){t+=e*i,i*=60}),n*t):n*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||i.isNegativeZero(e))},represent:function(e,t){var n;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(i.isNegativeZero(e))return"-0.0";return n=e.toString(10),a.test(n)?n.replace("e",".e"):n},defaultStyle:"lowercase"})},{"../common":2,"../type":13}],17:[function(e,t,n){"use strict";var i=e("../common"),r=e("../type");t.exports=new r("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i,r,o=e.length,a=0,s=!1;if(!o)return!1;if("-"!==(t=e[a])&&"+"!==t||(t=e[++a]),"0"===t){if(a+1===o)return!0;if("b"===(t=e[++a])){for(a++;a=i.flowLevel;switch(H(r,n,i.indent,t,function(e){return function(e,t){var n,i;for(n=0,i=e.implicitTypes.length;n"+V(r,i.indent)+Z(L(function(t,n){var e,i,r=/(\n+)([^\n]*)/g,o=function(){var e=t.indexOf("\n");return e=-1!==e?e:t.length,r.lastIndex=e,z(t.slice(0,e),n)}(),a="\n"===t[0]||" "===t[0];for(;i=r.exec(t);){var s=i[1],c=i[2];e=" "===c[0],o+=s+(a||e||""===c?"":"\n")+z(c,n),a=e}return o}(r,t),e));case $:return'"'+function(e){for(var t,n,i,r="",o=0;ot&&o tag resolver accepts not "'+c+'" style');i=s.represent[c](t,c)}e.dump=i}return!0}return!1}function Q(e,t,n,i,r,o){e.tag=null,e.dump=n,J(e,n,!1)||J(e,n,!0);var a=p.call(e.dump);i&&(i=e.flowLevel<0||e.flowLevel>t);var s,c,u="[object Object]"===a||"[object Array]"===a;if(u&&(c=-1!==(s=e.duplicates.indexOf(n))),(null!==e.tag&&"?"!==e.tag||c||2!==e.indent&&0 "+e.dump)}return!0}function X(e,t){var n,i,r=[],o=[];for(function e(t,n,i){var r,o,a;if(null!==t&&"object"==typeof t)if(-1!==(o=n.indexOf(t)))-1===i.indexOf(o)&&i.push(o);else if(n.push(t),Array.isArray(t))for(o=0,a=t.length;ot)&&0!==i)N(e,"bad indentation of a sequence entry");else if(e.lineIndentt?d=1:e.lineIndent===t?d=0:e.lineIndentt?d=1:e.lineIndent===t?d=0:e.lineIndentt)&&($(e,t,b,!0,r)&&(m?d=e.result:h=e.result),m||(U(e,l,p,f,d,h,o,a),f=d=h=null),Y(e,!0,-1),s=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==s)N(e,"bad indentation of a mapping entry");else if(e.lineIndentl&&(l=e.lineIndent),j(o))p++;else{if(e.lineIndent>10),56320+(c-65536&1023)),e.position++}else N(e,"unknown escape sequence");n=i=e.position}else j(s)?(L(e,n,i,!0),B(e,Y(e,!1,t)),n=i=e.position):e.position===e.lineStart&&R(e)?N(e,"unexpected end of the document within a double quoted scalar"):(e.position++,i=e.position)}N(e,"unexpected end of the stream within a double quoted scalar")}(e,p)?m=!0:!function(e){var t,n,i;if(42!==(i=e.input.charCodeAt(e.position)))return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!I(i)&&!O(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&N(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),e.anchorMap.hasOwnProperty(n)||N(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],Y(e,!0,-1),!0}(e)?function(e,t,n){var i,r,o,a,s,c,u,l,p=e.kind,f=e.result;if(I(l=e.input.charCodeAt(e.position))||O(l)||35===l||38===l||42===l||33===l||124===l||62===l||39===l||34===l||37===l||64===l||96===l)return!1;if((63===l||45===l)&&(I(i=e.input.charCodeAt(e.position+1))||n&&O(i)))return!1;for(e.kind="scalar",e.result="",r=o=e.position,a=!1;0!==l;){if(58===l){if(I(i=e.input.charCodeAt(e.position+1))||n&&O(i))break}else if(35===l){if(I(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&R(e)||n&&O(l))break;if(j(l)){if(s=e.line,c=e.lineStart,u=e.lineIndent,Y(e,!1,-1),e.lineIndent>=t){a=!0,l=e.input.charCodeAt(e.position);continue}e.position=o,e.line=s,e.lineStart=c,e.lineIndent=u;break}}a&&(L(e,r,o,!1),B(e,e.line-s),r=o=e.position,a=!1),S(l)||(o=e.position+1),l=e.input.charCodeAt(++e.position)}return L(e,r,o,!1),!!e.result||(e.kind=p,e.result=f,!1)}(e,p,x===n)&&(m=!0,null===e.tag&&(e.tag="?")):(m=!0,null===e.tag&&null===e.anchor||N(e,"alias node should not have any properties")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===d&&(m=s&&P(e,f))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(c=0,u=e.implicitTypes.length;c tag; it should be "'+l.kind+'", not "'+e.kind+'"'),l.resolve(e.result)?(e.result=l.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):N(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):N(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||m}function H(e){var t,n,i,r,o=e.position,a=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(r=e.input.charCodeAt(e.position))&&(Y(e,!0,-1),r=e.input.charCodeAt(e.position),!(0t/2-1){n=" ... ",i+=5;break}for(r="",o=this.position;ot/2-1){r=" ... ",o-=5;break}return a=this.buffer.slice(i,o),s.repeat(" ",e)+n+a+r+"\n"+s.repeat(" ",e+this.position-i+n.length)+"^"},i.prototype.toString=function(e){var t,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet())&&(n+=":\n"+t),n},t.exports=i},{"./common":2}],7:[function(e,t,n){"use strict";var i=e("./common"),r=e("./exception"),o=e("./type");function a(e,t,i){var r=[];return e.include.forEach(function(e){i=a(e,t,i)}),e[t].forEach(function(n){i.forEach(function(e,t){e.tag===n.tag&&e.kind===n.kind&&r.push(t)}),i.push(n)}),i.filter(function(e,t){return-1===r.indexOf(t)})}function s(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach(function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new r("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=a(this,"implicit",[]),this.compiledExplicit=a(this,"explicit",[]),this.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{}};function i(e){n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;e>16&255),s.push(a>>8&255),s.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return 0==(n=r%4*6)?(s.push(a>>16&255),s.push(a>>8&255),s.push(255&a)):18==n?(s.push(a>>10&255),s.push(a>>2&255)):12==n&&s.push(a>>4&255),c?c.from?c.from(s):new c(s):s},predicate:function(e){return c&&c.isBuffer(e)},represent:function(e){var t,n,i="",r=0,o=e.length,a=u;for(t=0;t>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return 0==(n=o%3)?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2==n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1==n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}})},{"../type":13}],15:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)},construct:function(e){return"true"===e||"True"===e||"TRUE"===e},predicate:function(e){return"[object Boolean]"===Object.prototype.toString.call(e)},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},{"../type":13}],16:[function(e,t,n){"use strict";var i=e("../common"),r=e("../type"),o=new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");var a=/^[-+]?[0-9]+e/;t.exports=new r("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!o.test(e)||"_"===e[e.length-1])},construct:function(e){var t,n,i,r;return n="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,r=[],0<="+-".indexOf(t[0])&&(t=t.slice(1)),".inf"===t?1==n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:0<=t.indexOf(":")?(t.split(":").forEach(function(e){r.unshift(parseFloat(e,10))}),t=0,i=1,r.forEach(function(e){t+=e*i,i*=60}),n*t):n*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||i.isNegativeZero(e))},represent:function(e,t){var n;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(i.isNegativeZero(e))return"-0.0";return n=e.toString(10),a.test(n)?n.replace("e",".e"):n},defaultStyle:"lowercase"})},{"../common":2,"../type":13}],17:[function(e,t,n){"use strict";var i=e("../common"),r=e("../type");t.exports=new r("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i,r,o=e.length,a=0,s=!1;if(!o)return!1;if("-"!==(t=e[a])&&"+"!==t||(t=e[++a]),"0"===t){if(a+1===o)return!0;if("b"===(t=e[++a])){for(a++;a 0)) ? level - 1 : level; if (block && (state.dump.length !== 0)) { - writeBlockSequence(state, level, state.dump, compact); + writeBlockSequence(state, arrayLevel, state.dump, compact); if (duplicate) { state.dump = '&ref_' + duplicateIndex + state.dump; } } else { - writeFlowSequence(state, level, state.dump); + writeFlowSequence(state, arrayLevel, state.dump); if (duplicate) { state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; } diff --git a/lib/js-yaml/loader.js b/lib/js-yaml/loader.js index fe2cb4d0..2815c955 100644 --- a/lib/js-yaml/loader.js +++ b/lib/js-yaml/loader.js @@ -30,6 +30,8 @@ var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; +function _class(obj) { return Object.prototype.toString.call(obj); } + function is_EOL(c) { return (c === 0x0A/* LF */) || (c === 0x0D/* CR */); } @@ -285,6 +287,31 @@ function mergeMappings(state, destination, source, overridableKeys) { function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) { var index, quantity; + // The output is a plain object here, so keys can only be strings. + // We need to convert keyNode to a string, but doing so can hang the process + // (deeply nested arrays that explode exponentially using aliases). + if (Array.isArray(keyNode)) { + keyNode = Array.prototype.slice.call(keyNode); + + for (index = 0, quantity = keyNode.length; index < quantity; index += 1) { + if (Array.isArray(keyNode[index])) { + throwError(state, 'nested arrays are not supported inside keys'); + } + + if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') { + keyNode[index] = '[object Object]'; + } + } + } + + // Avoid code execution in load() via toString property + // (still use its own toString for arrays, timestamps, + // and whatever user schema extensions happen to have @@toStringTag) + if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') { + keyNode = '[object Object]'; + } + + keyNode = String(keyNode); if (_result === null) { diff --git a/package.json b/package.json index 7d2652c8..4eefe04e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-yaml", - "version": "3.12.0", + "version": "3.13.1", "description": "YAML 1.2 parser and serializer", "keywords": [ "yaml", diff --git a/test/issues/0350.js b/test/issues/0350.js index 891f99e1..5b3f3da5 100644 --- a/test/issues/0350.js +++ b/test/issues/0350.js @@ -6,7 +6,7 @@ var yaml = require('../../'); var readFileSync = require('fs').readFileSync; -test('should allow cast integers as !!float', function () { +test('should return parse docs from loadAll', function () { var data = yaml.safeLoadAll(readFileSync(require('path').join(__dirname, '/0350.yml'), 'utf8')); assert.deepEqual(data, [ { a: 1 }, { b: 2 } ]); diff --git a/test/issues/0432.js b/test/issues/0432.js new file mode 100644 index 00000000..b6cd1620 --- /dev/null +++ b/test/issues/0432.js @@ -0,0 +1,18 @@ +'use strict'; + + +var assert = require('assert'); +var yaml = require('../../'); + + +test('should indent arrays an extra level by default', function () { + var output = yaml.safeDump({ array: [ 'a', 'b' ] }); + var expected = 'array:\n - a\n - b\n'; + assert.strictEqual(output, expected); +}); + +test('should not indent arrays an extra level when disabled', function () { + var output = yaml.safeDump({ array: [ 'a', 'b' ] }, { noArrayIndent: true }); + var expected = 'array:\n- a\n- b\n'; + assert.strictEqual(output, expected); +}); diff --git a/test/issues/0468.js b/test/issues/0468.js new file mode 100644 index 00000000..6337c3f1 --- /dev/null +++ b/test/issues/0468.js @@ -0,0 +1,29 @@ +'use strict'; + + +var assert = require('assert'); +var yaml = require('../..'); + +test('should not indent arrays an extra level when disabled', function () { + /* eslint-disable max-len */ + var output = yaml.dump( + [ + { + a: 'a_val', + b: 'b_val' + }, + { + a: 'a2_val', + items: [ + { + a: 'a_a_val', + b: 'a_b_val' + } + ] + } + ], + { noArrayIndent: true } + ); + var expected = '- a: a_val\n b: b_val\n- a: a2_val\n items:\n - a: a_a_val\n b: a_b_val\n'; + assert.strictEqual(output, expected); +}); diff --git a/test/issues/0475-case1.yml b/test/issues/0475-case1.yml new file mode 100644 index 00000000..29e78476 --- /dev/null +++ b/test/issues/0475-case1.yml @@ -0,0 +1,117 @@ +? - - &id057 + - &id055 + - &id053 + - &id051 + - &id049 + - &id047 + - &id045 + - &id043 + - &id041 + - &id039 + - &id037 + - &id035 + - &id033 + - &id031 + - &id029 + - &id027 + - &id025 + - &id023 + - &id021 + - &id019 + - &id017 + - &id015 + - &id013 + - &id011 + - &id009 + - &id007 + - &id005 + - &id003 + - &id001 [lol] + - &id002 [lol] + - &id004 + - *id001 + - *id002 + - &id006 + - *id003 + - *id004 + - &id008 + - *id005 + - *id006 + - &id010 + - *id007 + - *id008 + - &id012 + - *id009 + - *id010 + - &id014 + - *id011 + - *id012 + - &id016 + - *id013 + - *id014 + - &id018 + - *id015 + - *id016 + - &id020 + - *id017 + - *id018 + - &id022 + - *id019 + - *id020 + - &id024 + - *id021 + - *id022 + - &id026 + - *id023 + - *id024 + - &id028 + - *id025 + - *id026 + - &id030 + - *id027 + - *id028 + - &id032 + - *id029 + - *id030 + - &id034 + - *id031 + - *id032 + - &id036 + - *id033 + - *id034 + - &id038 + - *id035 + - *id036 + - &id040 + - *id037 + - *id038 + - &id042 + - *id039 + - *id040 + - &id044 + - *id041 + - *id042 + - &id046 + - *id043 + - *id044 + - &id048 + - *id045 + - *id046 + - &id050 + - *id047 + - *id048 + - &id052 + - *id049 + - *id050 + - &id054 + - *id051 + - *id052 + - &id056 + - *id053 + - *id054 + - &id058 + - *id055 + - *id056 + - - *id057 + - *id058 +: key diff --git a/test/issues/0475-case2.yml b/test/issues/0475-case2.yml new file mode 100644 index 00000000..f5c69b53 --- /dev/null +++ b/test/issues/0475-case2.yml @@ -0,0 +1,112 @@ + - &id057 + - &id055 + - &id053 + - &id051 + - &id049 + - &id047 + - &id045 + - &id043 + - &id041 + - &id039 + - &id037 + - &id035 + - &id033 + - &id031 + - &id029 + - &id027 + - &id025 + - &id023 + - &id021 + - &id019 + - &id017 + - &id015 + - &id013 + - &id011 + - &id009 + - &id007 + - &id005 + - &id003 + - &id001 [lol] + - &id002 [lol] + - &id004 + - *id001 + - *id002 + - &id006 + - *id003 + - *id004 + - &id008 + - *id005 + - *id006 + - &id010 + - *id007 + - *id008 + - &id012 + - *id009 + - *id010 + - &id014 + - *id011 + - *id012 + - &id016 + - *id013 + - *id014 + - &id018 + - *id015 + - *id016 + - &id020 + - *id017 + - *id018 + - &id022 + - *id019 + - *id020 + - &id024 + - *id021 + - *id022 + - &id026 + - *id023 + - *id024 + - &id028 + - *id025 + - *id026 + - &id030 + - *id027 + - *id028 + - &id032 + - *id029 + - *id030 + - &id034 + - *id031 + - *id032 + - &id036 + - *id033 + - *id034 + - &id038 + - *id035 + - *id036 + - &id040 + - *id037 + - *id038 + - &id042 + - *id039 + - *id040 + - &id044 + - *id041 + - *id042 + - &id046 + - *id043 + - *id044 + - &id048 + - *id045 + - *id046 + - &id050 + - *id047 + - *id048 + - &id052 + - *id049 + - *id050 + - &id054 + - *id051 + - *id052 + - &id056 + - *id053 + - *id054 + - *id057 : 1 diff --git a/test/issues/0475.js b/test/issues/0475.js new file mode 100644 index 00000000..3727560a --- /dev/null +++ b/test/issues/0475.js @@ -0,0 +1,27 @@ +'use strict'; + + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Should not allow nested arrays in map keys (explicit syntax)', function () { + try { + yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0475-case1.yml'), 'utf8')); + } catch (err) { + assert(err.stack.startsWith('YAMLException: nested arrays are not supported inside keys')); + return; + } + assert.fail(null, null, 'Expected an error to be thrown'); +}); + +test('Should not allow nested arrays in map keys (implicit syntax)', function () { + try { + yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0475-case2.yml'), 'utf8')); + } catch (err) { + assert(err.stack.startsWith('YAMLException: nested arrays are not supported inside keys')); + return; + } + assert.fail(null, null, 'Expected an error to be thrown'); +}); diff --git a/test/issues/0480-date.yml b/test/issues/0480-date.yml new file mode 100644 index 00000000..3fcac6e0 --- /dev/null +++ b/test/issues/0480-date.yml @@ -0,0 +1 @@ +{ ! '2019-04-05T12:00:43.467Z': 123 } diff --git a/test/issues/0480-fn-array.yml b/test/issues/0480-fn-array.yml new file mode 100644 index 00000000..2e151bf5 --- /dev/null +++ b/test/issues/0480-fn-array.yml @@ -0,0 +1,4 @@ +? [ + 123, + { toString: ! 'function (){throw new Error("code execution")}' } +] : key diff --git a/test/issues/0480-fn.yml b/test/issues/0480-fn.yml new file mode 100644 index 00000000..68412be5 --- /dev/null +++ b/test/issues/0480-fn.yml @@ -0,0 +1 @@ +{ toString: ! 'function (){throw new Error("code execution")}' } : key diff --git a/test/issues/0480-fn2.yml b/test/issues/0480-fn2.yml new file mode 100644 index 00000000..6efd250d --- /dev/null +++ b/test/issues/0480-fn2.yml @@ -0,0 +1 @@ +{ __proto__: { toString: ! 'function(){throw new Error("code execution")}' } } : key diff --git a/test/issues/0480.js b/test/issues/0480.js new file mode 100644 index 00000000..bd9f4180 --- /dev/null +++ b/test/issues/0480.js @@ -0,0 +1,34 @@ +'use strict'; + + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Should not execute code when object with toString property is used as a key', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-fn.yml'), 'utf8')); + + assert.deepEqual(data, { '[object Object]': 'key' }); +}); + +test('Should not execute code when object with __proto__ property is used as a key', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-fn2.yml'), 'utf8')); + + assert.deepEqual(data, { '[object Object]': 'key' }); +}); + +test('Should not execute code when object inside array is used as a key', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-fn-array.yml'), 'utf8')); + + assert.deepEqual(data, { '123,[object Object]': 'key' }); +}); + +// this test does not guarantee in any way proper handling of date objects, +// it just keeps old behavior whenever possible +test('Should leave non-plain objects as is', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-date.yml'), 'utf8')); + + assert.deepEqual(Object.keys(data).length, 1); + assert(/2019/.test(Object.keys(data)[0])); +}); diff --git a/test/mocha.opts b/test/mocha.opts index 5efaf24d..a8f4aff7 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1 +1,2 @@ --ui tdd +--timeout 10000