Skip to content

Commit

Permalink
Throw an error if block sequence/mapping indent contains a tab
Browse files Browse the repository at this point in the history
fix #80
  • Loading branch information
rlidwka committed Dec 30, 2020
1 parent f0f205b commit aee620a
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(previously usage of custom non-ascii tags may have led to invalid YAML that can't be parsed).
- Anchors now work correctly with empty nodes, #301.
- Fix incorrect parsing of invalid block mapping syntax, #418.
- Throw an error if block sequence/mapping indent contains a tab, #80.


## [3.14.1] - 2020-12-07
Expand Down
25 changes: 25 additions & 0 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ function State(input, options) {
this.lineStart = 0;
this.lineIndent = 0;

// position of first leading tab in the current line,
// used to make sure there are no tabs in the indentation
this.firstTabInLine = -1;

this.documents = [];

/*
Expand Down Expand Up @@ -389,6 +393,7 @@ function readLineBreak(state) {

state.line += 1;
state.lineStart = state.position;
state.firstTabInLine = -1;
}

function skipSeparationSpace(state, allowComments, checkIndent) {
Expand All @@ -397,6 +402,9 @@ function skipSeparationSpace(state, allowComments, checkIndent) {

while (ch !== 0) {
while (is_WHITE_SPACE(ch)) {
if (ch === 0x09/* Tab */ && state.firstTabInLine === -1) {
state.firstTabInLine = state.position;
}
ch = state.input.charCodeAt(++state.position);
}

Expand Down Expand Up @@ -959,13 +967,21 @@ function readBlockSequence(state, nodeIndent) {
detected = false,
ch;

// there is a leading tab before this token, so it can't be a block sequence/mapping;
// it can still be flow sequence/mapping or a scalar
if (state.firstTabInLine !== -1) return false;

if (state.anchor !== null) {
state.anchorMap[state.anchor] = _result;
}

ch = state.input.charCodeAt(state.position);

while (ch !== 0) {
if (state.firstTabInLine !== -1) {
state.position = state.firstTabInLine;
throwError(state, 'tab characters must not be used in indentation');
}

if (ch !== 0x2D/* - */) {
break;
Expand Down Expand Up @@ -1030,13 +1046,22 @@ function readBlockMapping(state, nodeIndent, flowIndent) {
detected = false,
ch;

// there is a leading tab before this token, so it can't be a block sequence/mapping;
// it can still be flow sequence/mapping or a scalar
if (state.firstTabInLine !== -1) return false;

if (state.anchor !== null) {
state.anchorMap[state.anchor] = _result;
}

ch = state.input.charCodeAt(state.position);

while (ch !== 0) {
if (!atExplicitKey && state.firstTabInLine !== -1) {
state.position = state.firstTabInLine;
throwError(state, 'tab characters must not be used in indentation');
}

following = state.input.charCodeAt(state.position + 1);
_line = state.line; // Save the current line.

Expand Down
2 changes: 1 addition & 1 deletion lib/snippet.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function getLine(buffer, lineStart, lineEnd, position, maxLineLength) {
}

return {
str: head + buffer.slice(lineStart, lineEnd) + tail,
str: head + buffer.slice(lineStart, lineEnd).replace(/\t/g, '→') + tail,
pos: position - lineStart + head.length // relative position
};
}
Expand Down
73 changes: 73 additions & 0 deletions test/issues/0080.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use strict';


const assert = require('assert');
const yaml = require('../../');


it('should throw when tabs are used as indentation', function () {
assert.throws(() => yaml.load(`
\tfoo: 1
bar: 2
`), /end of the stream or a document separator is expected/);

assert.throws(() => yaml.load(`
foo: 1
\tbar: 2
`), /tab characters must not be used/);

assert.throws(() => yaml.load(`
\t- foo
- bar
`), /end of the stream or a document separator is expected/);

assert.throws(() => yaml.load(`
- foo
\t- bar
`), /tab characters must not be used/);
});


it('should allow tabs inside separation spaces', function () {
assert.deepStrictEqual(yaml.load(`
foo\t \t:\t \t1\t \t
\t \t \t
bar \t : \t 2 \t
`), { foo: 1, bar: 2 });

assert.deepStrictEqual(yaml.load(`
-\t \tfoo\t \t
\t \t \t
- \t bar \t
`), [ 'foo', 'bar' ]);

assert.deepStrictEqual(yaml.load(`
\t{\tfoo\t:\t1\t,\tbar\t:\t2\t}\t
`), { foo: 1, bar: 2 });

assert.deepStrictEqual(yaml.load(`
\t[\tfoo\t,\tbar\t]\t
`), [ 'foo', 'bar' ]);

assert.deepStrictEqual(yaml.load(`
foo: # string indent = 1
\t \t1
\t 2
\t \t3
`), { foo: '1 2 3' });
});


it('should throw when tabs are used as indentation in strings', function () {
assert.throws(() => yaml.load(`
foo:
bar: |
\tbaz
`), /tab characters must not be used/);

assert.deepStrictEqual(yaml.load(`
foo:
bar: |
\tbaz
`), { foo: { bar: '\tbaz\n' } });
});

0 comments on commit aee620a

Please # to comment.