From 616ad78c881b67303f70ea8de9d948ac8ba75bec Mon Sep 17 00:00:00 2001 From: Rongrong Date: Sun, 1 May 2022 04:14:07 +0800 Subject: [PATCH] fix(core/utils/anti-hotlink): invalid HTML output (#9669) Signed-off-by: Rongrong --- lib/middleware/anti-hotlink.js | 10 ++++- test/middleware/anti-hotlink.js | 75 ++++++++++++++++----------------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/lib/middleware/anti-hotlink.js b/lib/middleware/anti-hotlink.js index ffb53bea5..945f4b1ee 100644 --- a/lib/middleware/anti-hotlink.js +++ b/lib/middleware/anti-hotlink.js @@ -16,7 +16,13 @@ const parseUrl = (str) => { return url; }; const replaceUrls = (body, template) => { - const $ = cheerio.load(body, { decodeEntities: false, xmlMode: true }); + // const $ = cheerio.load(body, { decodeEntities: false, xmlMode: true }); + // `

` => `


` + // so awful... + // "In HTML, using a closing tag on an empty element is usually invalid." + // https://developer.mozilla.org/en-US/docs/Glossary/Empty_element + // I guess it is just a workaround to drop ``, so this is what we exactly need: + const $ = cheerio.load(body, null, false); $('img').each(function () { const old_src = $(this).attr('src'); const url = parseUrl(old_src); @@ -35,7 +41,7 @@ module.exports = async (ctx, next) => { const template = config.hotlink.template; // Assume that only description include image link // and here we will only check them in description. - // Use Cherrio to load the description as html and filter all + // Use Cheerio to load the description as html and filter all // image link if (template) { if (ctx.state.data) { diff --git a/test/middleware/anti-hotlink.js b/test/middleware/anti-hotlink.js index 23a02ff78..6fd54f2e4 100644 --- a/test/middleware/anti-hotlink.js +++ b/test/middleware/anti-hotlink.js @@ -15,6 +15,13 @@ afterEach(() => { }); describe('anti-hotlink', () => { + // First-time require is really, really slow. + // If someone merely runs this test unit instead of the whole suite and this stage does not exist, + // the next one will sometimes time out, so we need to firstly require it once. + it('server-require', () => { + server = require('../../lib/index'); + }); + it('template', async () => { process.env.HOTLINK_TEMPLATE = 'https://i3.wp.com/${host}${pathname}'; server = require('../../lib/index'); @@ -23,51 +30,22 @@ describe('anti-hotlink', () => { const response = await request.get('/test/complicated'); const parsed = await parser.parseString(response.text); expect(parsed.items[0].content).toBe( - ` + ` - + -` +` ); - expect(parsed.items[1].content).toBe(` -`); + expect(parsed.items[1].content).toBe(` +`); }); - it('url', async () => { - process.env.HOTLINK_TEMPLATE = '${protocol}//${host}${pathname}'; - server = require('../../lib/index'); - const request = supertest(server); - const response = await request.get('/test/complicated'); - const parsed = await parser.parseString(response.text); - expect(parsed.items[0].content).toBe( - ` - - - - - - - - -` - ); - expect(parsed.items[1].content).toBe(` -`); - }); - it('no-template', async () => { - process.env.HOTLINK_TEMPLATE = ''; - server = require('../../lib/index'); - const request = supertest(server); - - const response = await request.get('/test/complicated'); - const parsed = await parser.parseString(response.text); - expect(parsed.items[0].content).toBe( - ` + const origin1 = ` @@ -76,9 +54,28 @@ describe('anti-hotlink', () => { -` - ); - expect(parsed.items[1].content).toBe(` -`); +`; + + const origin2 = ` +`; + + const testOrigin = async () => { + server = require('../../lib/index'); + const request = supertest(server); + + const response = await request.get('/test/complicated'); + const parsed = await parser.parseString(response.text); + expect(parsed.items[0].content).toBe(origin1); + expect(parsed.items[1].content).toBe(origin2); + }; + + it('url', async () => { + process.env.HOTLINK_TEMPLATE = '${protocol}//${host}${pathname}'; + await testOrigin(); + }); + + it('no-template', async () => { + process.env.HOTLINK_TEMPLATE = ''; + await testOrigin(); }); });