From 9ea29ec6aa5e48d3dff3eacf7582818643a0ab0c Mon Sep 17 00:00:00 2001 From: Vladimir Zapparov Date: Sun, 10 Jan 2016 00:28:36 +0100 Subject: [PATCH] Restrict duplicated mapping keys with exception for merged keys Fixes #166 --- lib/js-yaml/loader.js | 30 ++++++++++++------- .../duplicate-key.js | 0 .../duplicate-key.yml | 0 .../duplicate-value-key.js | 0 .../duplicate-value-key.yml | 0 5 files changed, 20 insertions(+), 10 deletions(-) rename test/{samples-common => samples-load-errors}/duplicate-key.js (100%) rename test/{samples-common => samples-load-errors}/duplicate-key.yml (100%) rename test/{samples-common => samples-load-errors}/duplicate-value-key.js (100%) rename test/{samples-common => samples-load-errors}/duplicate-value-key.yml (100%) diff --git a/lib/js-yaml/loader.js b/lib/js-yaml/loader.js index 960bf453..41f994cd 100644 --- a/lib/js-yaml/loader.js +++ b/lib/js-yaml/loader.js @@ -131,6 +131,7 @@ function State(input, options) { this.schema = options['schema'] || DEFAULT_FULL_SCHEMA; this.onWarning = options['onWarning'] || null; this.legacy = options['legacy'] || false; + this.json = options['json'] || false; this.implicitTypes = this.schema.compiledImplicit; this.typeMap = this.schema.compiledTypeMap; @@ -260,7 +261,7 @@ function captureSegment(state, start, end, checkJson) { } } -function mergeMappings(state, destination, source) { +function mergeMappings(state, destination, source, overridableKeys) { var sourceKeys, key, index, quantity; if (!common.isObject(source)) { @@ -274,11 +275,12 @@ function mergeMappings(state, destination, source) { if (!_hasOwnProperty.call(destination, key)) { destination[key] = source[key]; + overridableKeys[key] = true; } } } -function storeMappingPair(state, _result, keyTag, keyNode, valueNode) { +function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode) { var index, quantity; keyNode = String(keyNode); @@ -290,13 +292,19 @@ function storeMappingPair(state, _result, keyTag, keyNode, valueNode) { if ('tag:yaml.org,2002:merge' === keyTag) { if (Array.isArray(valueNode)) { for (index = 0, quantity = valueNode.length; index < quantity; index += 1) { - mergeMappings(state, _result, valueNode[index]); + mergeMappings(state, _result, valueNode[index], overridableKeys); } } else { - mergeMappings(state, _result, valueNode); + mergeMappings(state, _result, valueNode, overridableKeys); } } else { + if (!state.json && + !_hasOwnProperty.call(overridableKeys, keyNode) && + _hasOwnProperty.call(_result, keyNode)) { + throwError(state, 'duplicated mapping key'); + } _result[keyNode] = valueNode; + delete overridableKeys[keyNode]; } return _result; @@ -636,6 +644,7 @@ function readFlowCollection(state, nodeIndent) { isPair, isExplicitPair, isMapping, + overridableKeys = {}, keyNode, keyTag, valueNode, @@ -707,9 +716,9 @@ function readFlowCollection(state, nodeIndent) { } if (isMapping) { - storeMappingPair(state, _result, keyTag, keyNode, valueNode); + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode); } else if (isPair) { - _result.push(storeMappingPair(state, null, keyTag, keyNode, valueNode)); + _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode)); } else { _result.push(keyNode); } @@ -941,6 +950,7 @@ function readBlockMapping(state, nodeIndent, flowIndent) { _tag = state.tag, _anchor = state.anchor, _result = {}, + overridableKeys = {}, keyTag = null, keyNode = null, valueNode = null, @@ -966,7 +976,7 @@ function readBlockMapping(state, nodeIndent, flowIndent) { if (0x3F/* ? */ === ch) { if (atExplicitKey) { - storeMappingPair(state, _result, keyTag, keyNode, null); + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); keyTag = keyNode = valueNode = null; } @@ -1006,7 +1016,7 @@ function readBlockMapping(state, nodeIndent, flowIndent) { } if (atExplicitKey) { - storeMappingPair(state, _result, keyTag, keyNode, null); + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); keyTag = keyNode = valueNode = null; } @@ -1051,7 +1061,7 @@ function readBlockMapping(state, nodeIndent, flowIndent) { } if (!atExplicitKey) { - storeMappingPair(state, _result, keyTag, keyNode, valueNode); + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode); keyTag = keyNode = valueNode = null; } @@ -1072,7 +1082,7 @@ function readBlockMapping(state, nodeIndent, flowIndent) { // Special case: last mapping's node contains only the key in explicit notation. if (atExplicitKey) { - storeMappingPair(state, _result, keyTag, keyNode, null); + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); } // Expose the resulting mapping. diff --git a/test/samples-common/duplicate-key.js b/test/samples-load-errors/duplicate-key.js similarity index 100% rename from test/samples-common/duplicate-key.js rename to test/samples-load-errors/duplicate-key.js diff --git a/test/samples-common/duplicate-key.yml b/test/samples-load-errors/duplicate-key.yml similarity index 100% rename from test/samples-common/duplicate-key.yml rename to test/samples-load-errors/duplicate-key.yml diff --git a/test/samples-common/duplicate-value-key.js b/test/samples-load-errors/duplicate-value-key.js similarity index 100% rename from test/samples-common/duplicate-value-key.js rename to test/samples-load-errors/duplicate-value-key.js diff --git a/test/samples-common/duplicate-value-key.yml b/test/samples-load-errors/duplicate-value-key.yml similarity index 100% rename from test/samples-common/duplicate-value-key.yml rename to test/samples-load-errors/duplicate-value-key.yml