Is PriceBlink - Price Comp & Coupons safe?

Medium risk

PriceBlink runs vendor-served, per-retailer config strings as page-context JavaScript during its auto-coupon flow.

When PriceBlink detects a supported retailer's checkout page, it fetches that retailer's configuration from tb.priceblink.com and passes several of its fields into an eval-style helper that injects them as a script into the page to fill in and submit coupon codes. Separately, every page PriceBlink runs on gets an injected message listener that accepts a postMessage from any origin and, if it starts with "openUrl", tells the extension to open a new background browser tab to that URL with no origin check or validation.

PriceBlinkv8.66Firefox Add-ons
45Risk
Who publishes it

PriceBlink - no other listings under this identity, 5 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
PriceBlink

Shared hosts - 5 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

priceblink.com
Also called by 1 other listing: PriceBlink
tb.priceblink.com
Also called by 1 other listing: PriceBlink
us.norton.com
Also called by 1 other listing: Norton Safe Search
athleta.gap.com
Also called by 4 other listings, including Score App - Compare prices as you shop.
tb.getinvisiblehand.com
Also called by 4 other listings, including PriceBlink, CNET Shopping

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI FOUND

PriceBlink runs vendor-served retailer config as page-context JavaScript

During an auto-coupon flow, PriceBlink runs code fields from its retailer-configuration server directly in the page you're viewing, using a script it injects into the page.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You reach a supported retailer's checkout page and PriceBlink starts its auto-coupon flow.

The extension did this

PriceBlink runs a field from a server-served retailer configuration as JavaScript inside the page.

02EvidenceCODE COMPARE
The code that does this

Config fetch, execution and call sites (source ships readable)

What it actually does
Fetches the retailer configuration from tb.priceblink.combackground.js
function getRetailerByUrl_step2(rid, callback){
    removeExpiredSuppressedRetailers();
   if (!rid) {
    callback(null);
    return;
  }
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
      try {
        var retailer = JSON.parse(xhr.responseText)[0];
        callback(retailer);
      }
      catch (e) {
        callback(null);
      }
    }
  }
  xhr.open("GET", "https://tb.priceblink.com/retailer.php?uid=" + uid + "&browser=" + currentBrowserType + "&ver=" + ver + "&partnerid=0&rid=" + rid + "&auto_coupon_support=1", true);
  xhr.send();
}
runEval() executes a retailer field as page-context scriptjs/autocoupons.js
function runEval(code) {
    var result = document.createElement('div');
    result.id = 'pb-autocoupons-eval-result';
    document.body.appendChild(result);

    var callback = function(mutationsList) {
      for(var mutation of mutationsList) {
        if (mutation.attributeName === 'result') {
          observer.disconnect();
          result.remove();
          script.remove();
          break;
        }
      }
    };

    var observer = new MutationObserver(callback);
    observer.observe(result, { attributes: true });

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.text = "(function () {\n    var result = null;\n    try {\n      result = JSON.stringify(function(){" + code + "}());\n    } catch(e) {\n      e.error = true;\n      result = JSON.stringify(e, ['message', 'name', 'error']);\n    }\n    document.getElementById('" + result.id + "').setAttribute('result', result);\n  }())";
    document.getElementsByTagName('head').item(0).appendChild(script);
  }
Call site: ac_pre_input / ac_pre_submit / coupon_submit_selector fallback / ac_post_submitjs/autocoupons.js
function fillAndSubmitCode(ret, code) {
    if (ret.ac_pre_input) {
      runEval(ret.ac_pre_input);
    }

    $(ret.coupon_input_selector)[0] && $(ret.coupon_input_selector).val(code);

    if (ret.ac_pre_submit) {
      runEval(ret.ac_pre_submit);
    }

    try {
      $(ret.coupon_submit_selector)[0] && $(ret.coupon_submit_selector)[0].click();
    } catch (e) {
      runEval(ret.coupon_submit_selector);
    }

    if (ret.ac_post_submit) {
      runEval(ret.ac_post_submit);
    }
  }
03EvidenceFIELD TABLE
Retailer fields autocoupons.js executes as script (illustrative values)
FieldValueWhy it matters
Pre-input script
document.querySelector('#promo').focus() (illustrative)Runs before PriceBlink types the coupon code into the page.
Pre-submit script
window.scrollTo(0,0) (illustrative)Runs right before PriceBlink clicks the apply button.
Submit-selector fallback
document.forms[0].submit() (illustrative)Runs instead of a click when no element matches the submit selector.
Post-submit script
console.log('submitted') (illustrative)Runs after the coupon form is submitted.
Remove-code script
document.querySelector('#promo').value='' (illustrative)Runs when PriceBlink clears a coupon code it tried.
Pre apply-codes script
localStorage.setItem('pb_test','1') (illustrative)Runs before PriceBlink starts testing its list of coupons.
Post apply-codes script
localStorage.removeItem('pb_test') (illustrative)Runs after PriceBlink finishes testing coupons.
04EvidenceARTIFACT
Reproduce it yourself

Recreates PriceBlink's runEval() mechanism standalone, so you can see a config field run as page-context JavaScript without installing the extension.

RequiresA modern browserNo extension install needed; this isolates the runEval() mechanism
poc-retailer-eval.html · html
<!doctype html>
<div id="target"></div>
<script>
// Recreates PriceBlink autocoupons.js runEval() exactly as shipped
// (js/autocoupons.js:595), to isolate the mechanism without installing
// the extension or touching a live retailer site.
function runEval(code) {
  var result = document.createElement('div');
  result.id = 'pb-autocoupons-eval-result';
  document.body.appendChild(result);

  var callback = function(mutationsList) {
    for (var mutation of mutationsList) {
      if (mutation.attributeName === 'result') {
        observer.disconnect();
        result.remove();
        script.remove();
        break;
      }
    }
  };

  var observer = new MutationObserver(callback);
  observer.observe(result, { attributes: true });

  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.text = "(function () {\n    var result = null;\n    try {\n      result = JSON.stringify(function(){" + code + "}());\n    } catch(e) {\n      e.error = true;\n      result = JSON.stringify(e, ['message', 'name', 'error']);\n    }\n    document.getElementById('" + result.id + "').setAttribute('result', result);\n  }())";
  document.getElementsByTagName('head').item(0).appendChild(script);
}

// Stand-in for one field of a tb.priceblink.com/retailer.php response
// (illustrative; swap in a captured response field to test the real API).
var mockRetailerField = "document.getElementById('target').setAttribute('poc-marker', 'executed')";
runEval(mockRetailerField);
</script>
How to run it
  1. 1
    Save as poc-retailer-eval.html.
  2. 2
    Open it in a browser.
  3. 3
    Check #target: its poc-marker attribute is set, showing the string ran as page JavaScript, the same path a retailer.php field takes.
05EvidenceTHIRD PARTY LIST
Third-party destinations
  • tb.priceblink.com

    Serves the retailer configuration object; fields in this response are executed as JavaScript in the page during the auto-coupon flow.

06EvidencePLAIN NOTE
Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

What it can do

Permissions this extension asks for, as declared in version 8.66. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    http://*/ and 1 more

  • See the address and title of every tab you have open

    tabs

  • Watch every request your browser makes

    webRequest

  • Store data in your browser

    storage

  • Schedule its own background tasks

    alarms

Where it sends data

Destinations our analysis observed PriceBlink - Price Comp & Coupons contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • tb.priceblink.com

    PriceBlink - Price Comp & Coupons sends data to tb.priceblink.com. One other extension we have analysed sends data here.

Updated 30 September 2026amo-61771