Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/core/render/compiler/image.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getAndRemoveConfig } from '../utils.js';
import { escapeHtml, getAndRemoveConfig } from '../utils.js';
import { isAbsolutePath, getPath, getParentPath } from '../../router/util.js';

export const imageCompiler = ({ renderer, contentBase, router }) =>
Expand All @@ -14,7 +14,7 @@ export const imageCompiler = ({ renderer, contentBase, router }) =>
}

if (title) {
attrs.push(`title="${title}"`);
attrs.push(`title="${escapeHtml(title)}"`);
}

if (config.size) {
Expand Down Expand Up @@ -42,7 +42,7 @@ export const imageCompiler = ({ renderer, contentBase, router }) =>
url = getPath(contentBase, getParentPath(router.getCurrentPath()), href);
}

return /* html */ `<img src="${url}" data-origin="${href}" alt="${text}" ${attrs.join(
return /* html */ `<img src="${escapeHtml(url)}" data-origin="${escapeHtml(href)}" alt="${escapeHtml(text)}" ${attrs.join(
' ',
)} />`;
});
6 changes: 3 additions & 3 deletions src/core/render/compiler/link.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getAndRemoveConfig } from '../utils.js';
import { escapeHtml, getAndRemoveConfig } from '../utils.js';
import { isAbsolutePath } from '../../router/util.js';

export const linkCompiler = ({
Expand Down Expand Up @@ -65,8 +65,8 @@ export const linkCompiler = ({
}

if (title) {
attrs.push(`title="${title}"`);
attrs.push(`title="${escapeHtml(title)}"`);
}

return /* html */ `<a href="${href}" ${attrs.join(' ')}>${text}</a>`;
return /* html */ `<a href="${escapeHtml(href)}" ${attrs.join(' ')}>${text}</a>`;
});
8 changes: 5 additions & 3 deletions src/core/render/compiler/media.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { escapeHtml } from '../utils';

export const compileMedia = {
markdown(url) {
return {
Expand All @@ -11,19 +13,19 @@ export const compileMedia = {
},
iframe(url, title) {
return {
html: `<iframe src="${url}" ${
html: `<iframe src="${escapeHtml(url)}" ${
title || 'width=100% height=400'
}></iframe>`,
};
},
video(url, title) {
return {
html: `<video src="${url}" ${title || 'controls'}>Not Supported</video>`,
html: `<video src="${escapeHtml(url)}" ${title || 'controls'}>Not Supported</video>`,
};
},
audio(url, title) {
return {
html: `<audio src="${url}" ${title || 'controls'}>Not Supported</audio>`,
html: `<audio src="${escapeHtml(url)}" ${title || 'controls'}>Not Supported</audio>`,
};
},
code(url, title) {
Expand Down
26 changes: 24 additions & 2 deletions test/integration/example.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
# Text between

[filename](_media/example3.js ':include :fragment=something_else_not_code')

[filename](_media/example4.js ':include :fragment=demo')

# Text after
`,
},
Expand Down Expand Up @@ -303,4 +303,26 @@ Command | Description | Parameters
expect(mainText).toContain('Something');
expect(mainText).toContain('this is include content');
});

test.each([
{ type: 'iframe', selector: 'iframe' },
{ type: 'video', selector: 'video' },
{ type: 'audio', selector: 'audio' },
])('embed %s escapes URL for XSS safety', async ({ type, selector }) => {
const dangerousUrl = 'https://example.com/?q="><svg/onload=alert(1)>';

await docsifyInit({
markdown: {
homepage: `[media](${dangerousUrl} ':include :type=${type}')`,
},
});

expect(
await waitForFunction(() => !!document.querySelector(selector)),
).toBe(true);

const mediaElm = document.querySelector(selector);
expect(mediaElm.getAttribute('src')).toBe(dangerousUrl);
expect(mediaElm.hasAttribute('onload')).toBe(false);
});
});
19 changes: 19 additions & 0 deletions test/integration/render.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@ Text</p></div>"
'"<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="50" /></p>"',
);
});

test('escapes image alt and title to prevent attribute injection', async function () {
const output = window.marked(
'![alt" onerror="alert(1)](http://imageUrl \'title" onerror="alert(1)\')',
);

expect(output).not.toContain(' onerror="alert(1)"');
expect(output).toContain('alt="alt&quot; onerror=&quot;alert(1)"');
expect(output).toContain('title="title&quot; onerror=&quot;alert(1)"');
});
});

// Headings
Expand Down Expand Up @@ -377,6 +387,15 @@ Text</p></div>"
`"<p><a href="http://url" target="_blank" rel="noopener" id="someCssID">alt text</a></p>"`,
);
});

test('escapes link title to prevent attribute injection', async function () {
const output = window.marked(
`[alt text](http://url 'title" onclick="alert(1)')`,
);

expect(output).not.toContain(' onclick="alert(1)"');
expect(output).toContain('title="title&quot; onclick=&quot;alert(1)"');
});
});

// Skip Link
Expand Down