diff --git a/package.json b/package.json index 0387969e..55f566b8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.39.4", + "version": "3.39.5", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/camo.js b/src/camo.js index 73fa894c..61a8bb1e 100644 --- a/src/camo.js +++ b/src/camo.js @@ -6,9 +6,9 @@ import { CamoConfig } from './configuration/camoconfig'; const LOGGER = require('@calzoneman/jsli')('camo'); function isWhitelisted(camoConfig: CamoConfig, url: string): boolean { - const whitelistedDomains = camoConfig.getWhitelistedDomains(); + const whitelistedDomains = camoConfig.getWhitelistedDomainsRegexp(); const parsed = urlparse.parse(url); - return whitelistedDomains.includes(parsed.hostname); + return whitelistedDomains.test('.' + parsed.hostname); } export function camoify(camoConfig: CamoConfig, url: string): string { diff --git a/src/configuration/camoconfig.js b/src/configuration/camoconfig.js index a33728fb..518ab57e 100644 --- a/src/configuration/camoconfig.js +++ b/src/configuration/camoconfig.js @@ -1,3 +1,5 @@ +const SPECIALCHARS = /([\\\.\?\+\*\$\^\|\(\)\[\]\{\}])/g; + class CamoConfig { constructor(config = { camo: { enabled: false } }) { this.config = config.camo; @@ -30,6 +32,17 @@ class CamoConfig { return this.config['whitelisted-domains'] || []; } + getWhitelistedDomainsRegexp() { + const domains = this.getWhitelistedDomains() + .map(d => '\\.' + d.replace(SPECIALCHARS, '\\$1') + '$'); + if (domains.length === 0) { + // If no whitelist, match nothing + return new RegExp('$^'); + } + + return new RegExp(domains.join('|'), 'i'); + } + getEncoding() { return this.config.encoding || 'url'; } diff --git a/test/camo.js b/test/camo.js index ee157551..48bf1d02 100644 --- a/test/camo.js +++ b/test/camo.js @@ -7,7 +7,7 @@ describe('Camo', () => { camo: { server: 'http://localhost:8081', key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', - 'whitelisted-domains': ['def.xyz'], + 'whitelisted-domains': ['def.xyz', 'tii.kzz.qqq'], encoding: 'hex' } }); @@ -36,6 +36,28 @@ describe('Camo', () => { const result = Camo.camoify(config, 'http://def.xyz/image.jpeg'); assert.strictEqual(result, 'https://def.xyz/image.jpeg'); }); + + it('bypasses camo for whitelisted domains subdomains', () => { + const result = Camo.camoify(config, 'http://abc.def.xyz/image.jpeg'); + assert.strictEqual(result, 'https://abc.def.xyz/image.jpeg'); + }); + + it('does not bypass camo for a non-subdomain match', () => { + const result = Camo.camoify(config, 'http://abcdef.xyz/image.jpeg'); + assert.strictEqual(result, 'http://localhost:8081/19f53f65e8081a064cff54fbd665e8bb08612aa6/687474703a2f2f6162636465662e78797a2f696d6167652e6a706567'); + }); + + it('does not bypass camo when no whitelist is configured', () => { + const config = new CamoConfig({ + camo: { + server: 'http://localhost:8081', + key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', + encoding: 'hex' + } + }); + const result = Camo.camoify(config, 'http://abcdef.xyz/image.jpeg'); + assert.strictEqual(result, 'http://localhost:8081/19f53f65e8081a064cff54fbd665e8bb08612aa6/687474703a2f2f6162636465662e78797a2f696d6167652e6a706567'); + }); }); describe('#transformImgTags', () => { diff --git a/test/configuration/camoconfig.js b/test/configuration/camoconfig.js index acf62333..b676d1cb 100644 --- a/test/configuration/camoconfig.js +++ b/test/configuration/camoconfig.js @@ -39,4 +39,18 @@ describe('CamoConfig', () => { assert.deepStrictEqual(new CamoConfig().getEncoding(), 'url'); }); }); + + describe('#getWhitelistedDomainsRegexp', () => { + it('generates a regex based on the whitelisted domains', () => { + const config = new CamoConfig({ + camo: { + server: 'localhost:8081', + 'whitelisted-domains': ['abc.xyz', 'tii.kzz.qqq'] + } + }); + + const re = config.getWhitelistedDomainsRegexp(); + assert.deepStrictEqual(re, /\.abc\.xyz$|\.tii\.kzz\.qqq$/i); + }); + }); });