Browse Source

Guard against custom rule not incrementing pos

pull/879/head
Alex Kocharin 3 years ago
parent
commit
1529ff4944
  1. 6
      CHANGELOG.md
  2. 13
      lib/parser_block.js
  3. 13
      lib/parser_inline.js
  4. 39
      test/misc.js

6
CHANGELOG.md

@ -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). 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 ## [13.0.1] - 2022-05-03
### Fixed ### Fixed
- Bumped `linkify-it` to 4.0.1. That should fix some hangs, caused by wrong - Bumped `linkify-it` to 4.0.1. That should fix some hangs, caused by wrong
@ -616,6 +621,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Renamed presets folder (configs -> presets). - 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.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 [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 [12.3.2]: https://github.com/markdown-it/markdown-it/compare/12.3.1...12.3.2

13
lib/parser_block.js

@ -46,7 +46,7 @@ function ParserBlock() {
// Generate tokens for input range // Generate tokens for input range
// //
ParserBlock.prototype.tokenize = function (state, startLine, endLine) { ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
var ok, i, var ok, i, prevLine,
rules = this.ruler.getRules(''), rules = this.ruler.getRules(''),
len = rules.length, len = rules.length,
line = startLine, line = startLine,
@ -74,11 +74,20 @@ ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
// - update `state.line` // - update `state.line`
// - update `state.tokens` // - update `state.tokens`
// - return true // - return true
prevLine = state.line;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
ok = rules[i](state, line, endLine, false); 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 // set state.tight if we had an empty line before current tag
// i.e. latest empty line should not count // i.e. latest empty line should not count

13
lib/parser_inline.js

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

39
test/misc.js

@ -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 () { describe('Misc', function () {
it('Should replace NULL characters', function () { it('Should replace NULL characters', function () {

Loading…
Cancel
Save