fix(anti-hotlink): skip base64 image (#14676)
* fix(anti-hotlink): skip base64 image * test: fix parameter complicated_description
This commit is contained in:
parent
94cf0742af
commit
1bdef0171b
|
|
@ -31,7 +31,8 @@ const expects = {
|
|||
<img data-mock="/DIYgod/RSSHub.png" src="https://mock.com/DIYgod/RSSHub.png" referrerpolicy="no-referrer">
|
||||
<img mock="/DIYgod/RSSHub.gif" src="https://mock.com/DIYgod/RSSHub.gif" referrerpolicy="no-referrer">
|
||||
<img src="http://mock.com/DIYgod/DIYgod/RSSHub" referrerpolicy="no-referrer">
|
||||
<img src="https://mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">`,
|
||||
<img src="https://mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" referrerpolicy="no-referrer">`,
|
||||
`<a href="https://mock.com/DIYgod/RSSHub"></a>
|
||||
<img src="https://mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">`,
|
||||
],
|
||||
|
|
@ -48,7 +49,8 @@ const expects = {
|
|||
<img data-mock="/DIYgod/RSSHub.png" src="https://i3.wp.com/mock.com/DIYgod/RSSHub.png" referrerpolicy="no-referrer">
|
||||
<img mock="/DIYgod/RSSHub.gif" src="https://i3.wp.com/mock.com/DIYgod/RSSHub.gif" referrerpolicy="no-referrer">
|
||||
<img src="https://i3.wp.com/mock.com/DIYgod/DIYgod/RSSHub" referrerpolicy="no-referrer">
|
||||
<img src="https://i3.wp.com/mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">`,
|
||||
<img src="https://i3.wp.com/mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" referrerpolicy="no-referrer">`,
|
||||
`<a href="https://mock.com/DIYgod/RSSHub"></a>
|
||||
<img src="https://i3.wp.com/mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">`,
|
||||
],
|
||||
|
|
@ -65,7 +67,8 @@ const expects = {
|
|||
<img data-mock="/DIYgod/RSSHub.png" src="https://images.weserv.nl?url=https%3A%2F%2Fmock.com%2FDIYgod%2FRSSHub.png" referrerpolicy="no-referrer">
|
||||
<img mock="/DIYgod/RSSHub.gif" src="https://images.weserv.nl?url=https%3A%2F%2Fmock.com%2FDIYgod%2FRSSHub.gif" referrerpolicy="no-referrer">
|
||||
<img src="https://images.weserv.nl?url=http%3A%2F%2Fmock.com%2FDIYgod%2FDIYgod%2FRSSHub" referrerpolicy="no-referrer">
|
||||
<img src="https://images.weserv.nl?url=https%3A%2F%2Fmock.com%2FDIYgod%2FRSSHub.jpg" referrerpolicy="no-referrer">`,
|
||||
<img src="https://images.weserv.nl?url=https%3A%2F%2Fmock.com%2FDIYgod%2FRSSHub.jpg" referrerpolicy="no-referrer">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" referrerpolicy="no-referrer">`,
|
||||
`<a href="https://mock.com/DIYgod/RSSHub"></a>
|
||||
<img src="https://images.weserv.nl?url=https%3A%2F%2Fmock.com%2FDIYgod%2FRSSHub.jpg" referrerpolicy="no-referrer">`,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ const parseUrl = (str: string) => {
|
|||
};
|
||||
const replaceUrls = ($: CheerioAPI, selector: string, template: string, attribute = 'src') => {
|
||||
$(selector).each(function () {
|
||||
const old_src = $(this).attr(attribute);
|
||||
if (old_src) {
|
||||
const url = parseUrl(old_src);
|
||||
if (url) {
|
||||
const oldSrc = $(this).attr(attribute);
|
||||
if (oldSrc) {
|
||||
const url = parseUrl(oldSrc);
|
||||
if (url && url.protocol !== 'data:') {
|
||||
// Cheerio will do the right thing to prohibit XSS.
|
||||
$(this).attr(attribute, interpolate(template, url));
|
||||
}
|
||||
|
|
@ -88,8 +88,8 @@ const validateTemplate = (template?: string) => {
|
|||
const middleware: MiddlewareHandler = async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
let image_hotlink_template;
|
||||
let multimedia_hotlink_template;
|
||||
let imageHotlinkTemplate: string | undefined;
|
||||
let multimediaHotlinkTemplate: string | undefined;
|
||||
|
||||
// Read params if enabled
|
||||
if (config.feature.allow_user_hotlink_template) {
|
||||
|
|
@ -98,21 +98,21 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
|
|||
// A risk is that the media URLs will be replaced by user-supplied templates,
|
||||
// so a user could literally take the control of "where are the media from",
|
||||
// but only in their personal-use feed URL.
|
||||
multimedia_hotlink_template = ctx.req.query('multimedia_hotlink_template');
|
||||
image_hotlink_template = ctx.req.query('image_hotlink_template');
|
||||
multimediaHotlinkTemplate = ctx.req.query('multimedia_hotlink_template');
|
||||
imageHotlinkTemplate = ctx.req.query('image_hotlink_template');
|
||||
}
|
||||
|
||||
// Force config hotlink template on conflict
|
||||
if (config.hotlink.template) {
|
||||
image_hotlink_template = filterPath(ctx.req.path) ? config.hotlink.template : undefined;
|
||||
imageHotlinkTemplate = filterPath(ctx.req.path) ? config.hotlink.template : undefined;
|
||||
}
|
||||
|
||||
if (!image_hotlink_template && !multimedia_hotlink_template) {
|
||||
if (!imageHotlinkTemplate && !multimediaHotlinkTemplate) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateTemplate(image_hotlink_template);
|
||||
validateTemplate(multimedia_hotlink_template);
|
||||
validateTemplate(imageHotlinkTemplate);
|
||||
validateTemplate(multimediaHotlinkTemplate);
|
||||
|
||||
// Assume that only description include image link
|
||||
// and here we will only check them in description.
|
||||
|
|
@ -121,13 +121,13 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
|
|||
const data: Data = ctx.get('data');
|
||||
if (data) {
|
||||
if (data.description) {
|
||||
data.description = process(data.description, image_hotlink_template, multimedia_hotlink_template);
|
||||
data.description = process(data.description, imageHotlinkTemplate, multimediaHotlinkTemplate);
|
||||
}
|
||||
|
||||
if (data.item) {
|
||||
for (const item of data.item) {
|
||||
if (item.description) {
|
||||
item.description = process(item.description, image_hotlink_template, multimedia_hotlink_template);
|
||||
item.description = process(item.description, imageHotlinkTemplate, multimediaHotlinkTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -347,7 +347,8 @@ describe('complicated_description', () => {
|
|||
<img data-mock="/DIYgod/RSSHub.png" src="https://mock.com/DIYgod/RSSHub.png" referrerpolicy="no-referrer">
|
||||
<img mock="/DIYgod/RSSHub.gif" src="https://mock.com/DIYgod/RSSHub.gif" referrerpolicy="no-referrer">
|
||||
<img src="http://mock.com/DIYgod/DIYgod/RSSHub" referrerpolicy="no-referrer">
|
||||
<img src="https://mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">`);
|
||||
<img src="https://mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" referrerpolicy="no-referrer">`);
|
||||
expect(parsed.items[1].content).toBe(`<a href="https://mock.com/DIYgod/RSSHub"></a>
|
||||
<img src="https://mock.com/DIYgod/RSSHub.jpg" referrerpolicy="no-referrer">`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { config } from '@/config';
|
|||
import got from '@/utils/got';
|
||||
import wait from '@/utils/wait';
|
||||
import cache from '@/utils/cache';
|
||||
import { DataItem } from '@/types';
|
||||
|
||||
let cacheIndex = 0;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ export default async (ctx) => {
|
|||
url: 'https://httpbingo.org/status/404',
|
||||
});
|
||||
}
|
||||
let item = [];
|
||||
let item: DataItem[] = [];
|
||||
switch (ctx.req.param('id')) {
|
||||
case 'filter':
|
||||
item = [
|
||||
|
|
@ -140,7 +141,8 @@ export default async (ctx) => {
|
|||
<img data-mock="/DIYgod/RSSHub.png">
|
||||
<img mock="/DIYgod/RSSHub.gif">
|
||||
<img src="http://mock.com/DIYgod/DIYgod/RSSHub">
|
||||
<img src="/DIYgod/RSSHub.jpg" onclick="alert(1);" onerror="alert(1);" onload="alert(1);">`,
|
||||
<img src="/DIYgod/RSSHub.jpg" onclick="alert(1);" onerror="alert(1);" onload="alert(1);">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">`,
|
||||
pubDate: new Date(`2019-3-1`).toUTCString(),
|
||||
link: `//mock.com/DIYgod/RSSHub`,
|
||||
author: `DIYgod`,
|
||||
|
|
|
|||
Loading…
Reference in New Issue