fix(anti-hotlink): skip base64 image (#14676)

* fix(anti-hotlink): skip base64 image

* test: fix parameter complicated_description
This commit is contained in:
Tony 2024-03-06 02:35:30 +08:00 committed by GitHub
parent 94cf0742af
commit 1bdef0171b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 26 additions and 20 deletions

View File

@ -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">`,
],

View File

@ -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);
}
}
}

View File

@ -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">`);
});

View File

@ -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`,