Skip to content

Commit

Permalink
fix(post): avoid swig in code being double escaped (#4362)
Browse files Browse the repository at this point in the history
  • Loading branch information
SukkaW committed Jun 20, 2020
1 parent 05bc34b commit d327789
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 12 deletions.
13 changes: 7 additions & 6 deletions lib/hexo/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ const { slugize, escapeRegExp } = require('hexo-util');
const { copyDir, exists, listDir, mkdirs, readFile, rmdir, unlink, writeFile } = require('hexo-fs');
const yfm = require('hexo-front-matter');

const replaceSwigTag = str => str.replace(/{/g, '\uFFFCleft\uFFFC').replace(/}/g, '\uFFFCright\uFFFC');
const restoreReplacesSwigTag = str => str.replace(/\uFFFCleft\uFFFC/g, '{').replace(/\uFFFCright\uFFFC/g, '}');

const preservedKeys = ['title', 'slug', 'path', 'layout', 'date', 'content'];

const rPlaceholder = /(?:<|&lt;)!--\uFFFC(\d+)--(?:>|&gt;)/g;
Expand All @@ -18,7 +21,7 @@ const rSwigComment = /\{#[\s\S]*?#\}/g;
const rSwigBlock = /\{%[\s\S]*?%\}/g;
const rSwigFullBlock = /\{% *(.+?)(?: *| +.*?)%\}[\s\S]+?\{% *end\1 *%\}/g;
const rSwigRawFullBlock = /{% *raw *%\}[\s\S]+?\{% *endraw *%\}/g;
const rSwigTagInsideInlineCode = /`.*{.*}.*`/g;
const rSwigTagInsideInlineCode = /`.*?{.*?}.*?`/g;

const _escapeContent = (cache, str) => {
const placeholder = '\uFFFC';
Expand Down Expand Up @@ -48,9 +51,7 @@ class PostRenderCache {
escapeAllSwigTags(str) {
const escape = _str => _escapeContent(this.cache, _str);
return str.replace(rSwigRawFullBlock, escape) // Escape {% raw %} first
.replace(rSwigTagInsideInlineCode, str => {
return str.replace(/{/g, '&#123;').replace(/}/g, '&#125;');
})
.replace(rSwigTagInsideInlineCode, replaceSwigTag) // Avoid double escaped by marked renderer
.replace(rSwigFullBlock, escape)
.replace(rSwigBlock, escape)
.replace(rSwigComment, '')
Expand Down Expand Up @@ -256,7 +257,6 @@ class Post {

return promise.then(content => {
data.content = content;

// Run "before_post_render" filters
return ctx.execFilter('before_post_render', data, { context: ctx });
}).then(() => {
Expand All @@ -277,7 +277,7 @@ class Post {
toString: true,
onRenderEnd(content) {
// Replace cache data with real contents
data.content = cacheObj.loadContent(content);
data.content = cacheObj.loadContent(restoreReplacesSwigTag(content));

// Return content after replace the placeholders
if (disableNunjucks) return data.content;
Expand All @@ -287,6 +287,7 @@ class Post {
}
}, options);
}).then(content => {
// restore { and } inside inline code
data.content = content;

// Run "after_post_render" filters
Expand Down
42 changes: 36 additions & 6 deletions test/scripts/hexo/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { highlight, escapeHTML } = require('hexo-util');
const { spy, useFakeTimers } = require('sinon');
const frontMatter = require('hexo-front-matter');
const fixture = require('../../fixtures/post_render');
const escapeSwigTag = str => str.replace(/{/g, '&#123;').replace(/}/g, '&#125;');

describe('Post', () => {
const Hexo = require('../../../lib/hexo');
Expand Down Expand Up @@ -455,8 +456,8 @@ describe('Post', () => {
const paths = [join(hexo.source_dir, '_posts', 'Hello-World-1.md')];

return Promise.all([
post.create({title: 'Hello World', layout: 'draft'}),
post.create({title: 'Hello World'})
post.create({ title: 'Hello World', layout: 'draft' }),
post.create({ title: 'Hello World' })
]).then(data => {
paths.push(data[1].path);

Expand All @@ -473,8 +474,8 @@ describe('Post', () => {
const path = join(hexo.source_dir, '_posts', 'Hello-World.md');

return Promise.all([
post.create({title: 'Hello World', layout: 'draft'}),
post.create({title: 'Hello World'})
post.create({ title: 'Hello World', layout: 'draft' }),
post.create({ title: 'Hello World' })
]).then(data => post.publish({
slug: 'Hello-World'
}, true)).then(data => {
Expand Down Expand Up @@ -1015,7 +1016,7 @@ describe('Post', () => {
engine: 'markdown'
});

data.content.trim().should.eql('<p>In Go’s templates, blocks look like this: <code>&amp;#123;&amp;#123;block &quot;template name&quot; .&amp;#125;&amp;#125; (content) &amp;#123;&amp;#123;end&amp;#125;&amp;#125;</code>.</p>');
data.content.trim().should.eql(`<p>In Go’s templates, blocks look like this: <code>${escapeSwigTag(escapeHTML('{{block "template name" .}} (content) {{end}}'))}</code>.</p>`);
});

// test for https://github.com/hexojs/hexo/issues/3346#issuecomment-595497849
Expand All @@ -1027,7 +1028,7 @@ describe('Post', () => {
engine: 'markdown'
});

data.content.trim().should.eql('<p><code>&amp;#123;&amp;#123; 1 + 1 &amp;#125;&amp;#125;</code> 2</p>');
data.content.trim().should.eql(`<p><code>${escapeSwigTag('{{ 1 + 1 }}')}</code> 2</p>`);
});

// https://github.com/hexojs/hexo/issues/4317
Expand Down Expand Up @@ -1083,6 +1084,35 @@ describe('Post', () => {
data.content.trim().should.contains(highlightedHtml);
});

it('render() - escape & recover muilt {% raw %} and backticks', async () => {
const content = [
'`{{ 1 + 1 }}` {{ 1 + 2 }} `{{ 2 + 2 }}`',
'Text',
'{% raw %}',
'Raw 1',
'{% endraw %}',
'Another Text',
'{% raw %}',
'Raw 2',
'{% endraw %}'
].join('\n');

const data = await post.render(null, {
content,
engine: 'markdown'
});

data.content.trim().should.eql([
`<p><code>${escapeSwigTag('{{ 1 + 1 }}')}</code> 3 <code>${escapeSwigTag('{{ 2 + 2 }}')}</code><br>Text</p>`,
'',
'Raw 1',
'',
'<p>Another Text</p>',
'',
'Raw 2'
].join('\n'));
});

// https://github.com/hexojs/hexo/issues/4087
it('render() - issue #4087', async () => {
// Adopted from https://github.com/hexojs/hexo/issues/4087#issuecomment-596999486
Expand Down

0 comments on commit d327789

Please sign in to comment.