-
Notifications
You must be signed in to change notification settings - Fork 15
Fix #109: Disable extraction outside of allowlisted sites. #138
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,108 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
/* eslint-disable no-unused-vars */ | ||
|
||
/** | ||
* Config values that are shared between files or otherwise useful to have in | ||
* a separate file. Config values can be overridden by setting a pref at the | ||
* subtree [email protected]. | ||
* | ||
* Content scripts cannot access the preference API, and thus cannot use this | ||
* module to get config values. Use commerce/config/content instead to use | ||
* message passing to fetch the config values from the background script. | ||
* @module | ||
*/ | ||
|
||
const DEFAULTS = { | ||
class Value { | ||
constructor(defaultValue) { | ||
this.defaultValue = defaultValue; | ||
} | ||
|
||
async get(name) { | ||
throw new Error('The Value config class cannot be used directly; use a type-specific subclass.'); | ||
} | ||
} | ||
|
||
class StringValue extends Value { | ||
async get(name) { | ||
return browser.shoppingPrefs.getCharPref(name, this.defaultValue); | ||
} | ||
} | ||
|
||
class IntValue extends Value { | ||
async get(name) { | ||
return browser.shoppingPrefs.getIntPref(name, this.defaultValue); | ||
} | ||
} | ||
|
||
class BoolValue extends Value { | ||
async get(name) { | ||
return browser.shoppingPrefs.getBoolPref(name, this.defaultValue); | ||
} | ||
} | ||
|
||
class ListValue extends Value { | ||
async get(name) { | ||
const prefValue = await browser.shoppingPrefs.getCharPref(name, this.defaultValue.join(',')); | ||
return prefValue.split(','); | ||
} | ||
} | ||
|
||
const CONFIG = { | ||
/** Time to wait between price checks for a product */ | ||
priceCheckInterval: 1000 * 60 * 60 * 6, // 6 hours | ||
priceCheckInterval: new IntValue(1000 * 60 * 60 * 6), // 6 hours | ||
|
||
/** Time to wait between checking if we should fetch new prices */ | ||
priceCheckTimeoutInterval: 1000 * 60 * 15, // 15 minutes | ||
priceCheckTimeoutInterval: new IntValue(1000 * 60 * 15), // 15 minutes | ||
|
||
/** Delay before removing iframes created during price checks */ | ||
iframeTimeout: 1000 * 60, // 1 minute | ||
iframeTimeout: new IntValue(1000 * 60), // 1 minute | ||
|
||
// URLs to files within the extension | ||
browserActionUrl: browser.extension.getURL('/browser_action/index.html'), | ||
browserActionUrl: new StringValue(browser.extension.getURL('/browser_action/index.html')), | ||
|
||
// Price alert config | ||
alertPercentThershold: 5, // 5% | ||
alertAbsoluteThreshold: 1000, // $10 | ||
alertPercentThershold: new IntValue(5), // 5% | ||
alertAbsoluteThreshold: new IntValue(1000), // $10 | ||
|
||
/** Color of the toolbar badge for showing active price alerts. */ | ||
badgeAlertBackground: '#00FEFF', | ||
badgeAlertBackground: new StringValue('#00FEFF'), | ||
|
||
/** Color of the toolbar badge when a product on the current page is trackable. */ | ||
badgeDetectBackground: '#33F70C', | ||
badgeDetectBackground: new StringValue('#33F70C'), | ||
|
||
/** URL for the add-on's page on support.mozilla.org */ | ||
supportUrl: 'https://support.mozilla.org', | ||
supportUrl: new StringValue('https://support.mozilla.org'), | ||
|
||
/** URL for the add-on's feedback form */ | ||
feedbackUrl: 'https://www.mozilla.org', | ||
feedbackUrl: new StringValue('https://www.mozilla.org'), | ||
|
||
/** List of domains that extraction is performed on. */ | ||
extractionAllowlist: new ListValue([ | ||
'amazon.com', | ||
'www.amazon.com', | ||
'smile.amazon.com', | ||
'bestbuy.com', | ||
'www.bestbuy.com', | ||
'ebay.com', | ||
'www.ebay.com', | ||
'homedepot.com', | ||
'www.homedepot.com', | ||
'walmart.com', | ||
'www.walmart.com', | ||
'mkelly.me', | ||
'www.mkelly.me', | ||
]), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of this being hardcoded in, can we generate this list from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing that would essentially require fallback extraction support for all sites that we ever support, which isn't really maintainable. Keeping them separate means more typing for sites that are supported, but also allows for sites that we only support via Fathom extraction. |
||
}; | ||
|
||
export default { | ||
async get(configName) { | ||
const defaultValue = DEFAULTS[configName]; | ||
switch (typeof defaultValue) { | ||
case 'string': | ||
return browser.shoppingPrefs.getCharPref(configName, defaultValue); | ||
case 'number': | ||
return browser.shoppingPrefs.getIntPref(configName, defaultValue); | ||
case 'boolean': | ||
return browser.shoppingPrefs.getBoolPref(configName, defaultValue); | ||
default: | ||
throw new Error(`Invalid config type ${typeof defaultValue} for config ${configName}`); | ||
const value = CONFIG[configName]; | ||
if (!value) { | ||
throw new Error(`Invalid config ${configName}`); | ||
} | ||
|
||
return value.get(configName); | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import config from 'commerce/config'; | ||
|
||
/** | ||
* Listener for messages from content scripts to the background page to fetch | ||
* config values. | ||
* @module | ||
*/ | ||
|
||
export async function handleConfigMessage(message) { | ||
return config.get(message.name); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
/** | ||
* Communication from content scripts to the background page for fetching config | ||
* values. | ||
* @module | ||
*/ | ||
|
||
export const CONFIG_MESSAGE_TYPE = 'config'; | ||
|
||
export default { | ||
async get(configName) { | ||
return browser.runtime.sendMessage({type: CONFIG_MESSAGE_TYPE, name: configName}); | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
* "document_idle", which is after all DOM content has been loaded. | ||
*/ | ||
|
||
import config from 'commerce/config/content'; | ||
import extractProductWithFathom from 'commerce/extraction/fathom_extraction'; | ||
import extractProductWithFallback from 'commerce/extraction/fallback_extraction'; | ||
|
||
|
@@ -30,17 +31,29 @@ async function getProductInfo() { | |
}); | ||
} | ||
|
||
(function main() { | ||
(async function main() { | ||
// If we're in an iframe, don't bother extracting a product EXCEPT if we were | ||
// started by the background script for a price check. | ||
const isInIframe = window !== window.top; | ||
const isBackgroundUpdate = window.location.hash === '#moz-commerce-background'; | ||
if (!isInIframe || isBackgroundUpdate) { | ||
// Make sure the page has finished loading, as JS could alter the DOM. | ||
if (document.readyState === 'complete') { | ||
getProductInfo(); | ||
} else { | ||
window.addEventListener('load', getProductInfo); | ||
} | ||
if (isInIframe && !isBackgroundUpdate) { | ||
return; | ||
} | ||
|
||
// Only perform extraction on allowlisted sites. Background updates get a | ||
// pass; we don't want to accidentally freeze updates for products that are | ||
// being tracked no matter what. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by this -- if users can only track products on allowlisted sites in the first place, then how would a background update ever load a page that wasn't an allowlisted site? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If they tracked a product on a webpage that was previously allowed, but is no longer allowed (whether due to an update, testing the demo add-on, or something else). It's a bit of an edge case but could happen between add-on updates. |
||
const url = new URL(document.location.href); | ||
const allowList = await config.get('extractionAllowlist'); | ||
const allowAll = allowList.length === 1 && allowList[0] === '*'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you're building this in for post-MVP where we remove the 5 site limit? Why wouldn't we just remove the allowList check and list from config if we were opening up extraction to all sites? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is more for non-developers who want to test the add-on out on many sites without hardcoding them into the pref and can't make their own build with the config changed. |
||
if (!allowAll && !isBackgroundUpdate && !allowList.includes(url.host)) { | ||
return; | ||
} | ||
|
||
// Make sure the page has finished loading, as JS could alter the DOM. | ||
if (document.readyState === 'complete') { | ||
getProductInfo(); | ||
} else { | ||
window.addEventListener('load', getProductInfo); | ||
} | ||
}()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was very confused by the new
config
folder until I read this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, it's kinda confusing.