Skip to content

Commit

Permalink
Guard against custom rule not incrementing pos
Browse files Browse the repository at this point in the history
  • Loading branch information
rlidwka committed May 25, 2022
1 parent 6325878 commit 1529ff4
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [13.1.0] - WIP
### Changed
- Throw an error if 3rd party plugin doesn't increment `line` or `pos` counters
(previously, markdown-it would likely go into infinite loop instead), #847.

## [13.0.1] - 2022-05-03
### Fixed
- Bumped `linkify-it` to 4.0.1. That should fix some hangs, caused by wrong
Expand Down Expand Up @@ -616,6 +621,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Renamed presets folder (configs -> presets).


[13.1.0]: https://github.com/markdown-it/markdown-it/compare/13.0.1...13.1.0
[13.0.1]: https://github.com/markdown-it/markdown-it/compare/13.0.0...13.0.1
[13.0.0]: https://github.com/markdown-it/markdown-it/compare/12.3.2...13.0.0
[12.3.2]: https://github.com/markdown-it/markdown-it/compare/12.3.1...12.3.2
Expand Down
13 changes: 11 additions & 2 deletions lib/parser_block.js
Expand Up @@ -46,7 +46,7 @@ function ParserBlock() {
// Generate tokens for input range
//
ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
var ok, i,
var ok, i, prevLine,
rules = this.ruler.getRules(''),
len = rules.length,
line = startLine,
Expand Down Expand Up @@ -74,12 +74,21 @@ ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
// - update `state.line`
// - update `state.tokens`
// - return true
prevLine = state.line;

for (i = 0; i < len; i++) {
ok = rules[i](state, line, endLine, false);
if (ok) { break; }
if (ok) {
if (prevLine >= state.line) {
throw new Error("block rule didn't increment state.line");
}
break;
}
}

// this can only happen if user disables paragraph rule
if (!ok) throw new Error('none of the block rules matched');

// set state.tight if we had an empty line before current tag
// i.e. latest empty line should not count
state.tight = !hasEmptyLines;
Expand Down
13 changes: 10 additions & 3 deletions lib/parser_inline.js
Expand Up @@ -99,7 +99,10 @@ ParserInline.prototype.skipToken = function (state) {
ok = rules[i](state, true);
state.level--;

if (ok) { break; }
if (ok) {
if (pos >= state.pos) { throw new Error("inline rule didn't increment state.pos"); }
break;
}
}
} else {
// Too much nesting, just skip until the end of the paragraph.
Expand All @@ -124,7 +127,7 @@ ParserInline.prototype.skipToken = function (state) {
// Generate tokens for input range
//
ParserInline.prototype.tokenize = function (state) {
var ok, i,
var ok, i, prevPos,
rules = this.ruler.getRules(''),
len = rules.length,
end = state.posMax,
Expand All @@ -137,11 +140,15 @@ ParserInline.prototype.tokenize = function (state) {
// - update `state.pos`
// - update `state.tokens`
// - return true
prevPos = state.pos;

if (state.level < maxNesting) {
for (i = 0; i < len; i++) {
ok = rules[i](state, false);
if (ok) { break; }
if (ok) {
if (prevPos >= state.pos) { throw new Error("inline rule didn't increment state.pos"); }
break;
}
}
}

Expand Down
39 changes: 39 additions & 0 deletions test/misc.js
Expand Up @@ -169,6 +169,45 @@ describe('API', function () {
});


describe('Plugins', function () {

it('should not loop infinitely if all rules are disabled', function () {
var md = markdownit();

md.inline.ruler.enableOnly([]);
md.inline.ruler2.enableOnly([]);
md.block.ruler.enableOnly([]);

assert.throws(() => md.render(' - *foo*\n - `bar`'), /none of the block rules matched/);
});

it('should not loop infinitely if inline rule doesn\'t increment pos', function () {
var md = markdownit();

md.inline.ruler.after('text', 'custom', function (state/*, silent*/) {
if (state.src.charCodeAt(state.pos) !== 0x40/* @ */) return false;
return true;
});

assert.throws(() => md.render('foo@bar'), /inline rule didn't increment state.pos/);
assert.throws(() => md.render('[foo@bar]()'), /inline rule didn't increment state.pos/);
});

it('should not loop infinitely if block rule doesn\'t increment pos', function () {
var md = markdownit();

md.block.ruler.before('paragraph', 'custom', function (state, startLine/*, endLine, silent*/) {
var pos = state.bMarks[startLine] + state.tShift[startLine];
if (state.src.charCodeAt(pos) !== 0x40/* @ */) return false;
return true;
}, { alt: [ 'paragraph' ] });

assert.throws(() => md.render('foo\n@bar\nbaz'), /block rule didn't increment state.line/);
assert.throws(() => md.render('foo\n\n@bar\n\nbaz'), /block rule didn't increment state.line/);
});
});


describe('Misc', function () {

it('Should replace NULL characters', function () {
Expand Down

0 comments on commit 1529ff4

Please sign in to comment.