Skip to content

Commit b5b63ca

Browse files
stevenjoezhanguioleeyoshinorin
authored
fix(tag/include_code): prevent path traversal (#5251)
Co-authored-by: uiolee <[email protected]> Co-authored-by: yoshinorin <[email protected]>
1 parent cefee92 commit b5b63ca

File tree

2 files changed

+33
-30
lines changed

2 files changed

+33
-30
lines changed

lib/plugins/tag/include_code.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { exists, readFile } from 'hexo-fs';
2-
import { basename, extname, join, posix } from 'path';
1+
import { basename, extname, join } from 'path';
2+
import { url_for } from 'hexo-util';
33
import type Hexo from '../../hexo';
44

55
const rCaptionTitleFile = /(.*)?(?:\s+|^)(\/*\S+)/;
@@ -47,32 +47,31 @@ export = (ctx: Hexo) => function includeCodeTag(args: string[]) {
4747
// If the language is not defined, use file extension instead
4848
lang = lang || extname(path).substring(1);
4949

50-
const src = join(ctx.source_dir, codeDir, path);
50+
const source = join(codeDir, path).replace(/\\/g, '/');
5151

52-
// If the title is not defined, use file name instead
53-
const title = match[1] || basename(path);
54-
const caption = `<span>${title}</span><a href="${posix.join(ctx.config.root, codeDir, path)}">view raw</a>`;
55-
56-
return exists(src).then(exist => {
57-
if (exist) return readFile(src);
58-
}).then(code => {
59-
if (!code) return;
52+
// Prevent path traversal: https://github.com/hexojs/hexo/issues/5250
53+
const Page = ctx.model('Page');
54+
const doc = Page.findOne({ source });
55+
if (!doc) return;
6056

61-
const lines = code.split('\n');
62-
code = lines.slice(from, to).join('\n').trim();
57+
let code = doc.content;
58+
const lines = code.split('\n');
59+
code = lines.slice(from, to).join('\n').trim();
6360

64-
if (ctx.extend.highlight.query(ctx.config.syntax_highlighter)) {
65-
const options = {
66-
lang,
67-
caption,
68-
lines_length: lines.length
69-
};
70-
return ctx.extend.highlight.exec(ctx.config.syntax_highlighter, {
71-
context: ctx,
72-
args: [code, options]
73-
});
74-
}
61+
// If the title is not defined, use file name instead
62+
const title = match[1] || basename(path);
63+
const caption = `<span>${title}</span><a href="${url_for.call(ctx, doc.path)}">view raw</a>`;
7564

76-
return `<pre><code>${code}</code></pre>`;
77-
});
65+
if (ctx.extend.highlight.query(ctx.config.syntax_highlighter)) {
66+
const options = {
67+
lang,
68+
caption,
69+
lines_length: lines.length
70+
};
71+
return ctx.extend.highlight.exec(ctx.config.syntax_highlighter, {
72+
context: ctx,
73+
args: [code, options]
74+
});
75+
}
76+
return `<pre><code>${code}</code></pre>`;
7877
};

test/scripts/tags/include_code.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ describe('include_code', () => {
1616
const defaultCfg = JSON.parse(JSON.stringify(hexo.config));
1717

1818
const fixture = [
19-
'if (tired && night){',
19+
'if (tired && night) {',
2020
' sleep();',
2121
'}'
2222
].join('\n');
2323

2424
const code = args => includeCode(args.split(' '));
2525

26-
before(() => writeFile(path, fixture));
26+
before(async () => {
27+
await writeFile(path, fixture);
28+
await hexo.init();
29+
await hexo.load();
30+
});
2731

2832
beforeEach(() => {
2933
hexo.config = JSON.parse(JSON.stringify(defaultCfg));
@@ -95,7 +99,7 @@ describe('include_code', () => {
9599

96100
it('to', async () => {
97101
const fixture = [
98-
'if (tired && night){',
102+
'if (tired && night) {',
99103
' sleep();'
100104
].join('\n');
101105
const expected = highlight(fixture, {
@@ -173,7 +177,7 @@ describe('include_code', () => {
173177

174178
it('to', async () => {
175179
const fixture = [
176-
'if (tired && night){',
180+
'if (tired && night) {',
177181
' sleep();'
178182
].join('\n');
179183
const expected = prismHighlight(fixture, {

0 commit comments

Comments
 (0)