Is Coupon Hacker: Automatic AI Coupon Finder safe?

High risk

Coupon Hacker is high risk. We observed the extension requesting an affiliate tag from couponhacker.xyz/api/tg and adding the returned tag=sz000d-20 to Amazon links. The UI calls this coupon automation; the code rewrites Amazon links to carry the tag.…

couponhackerxyzv1.3.2Chrome Web Store
76Risk

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

Publishers can request a review.

Findings

Amazon links receive the extension's affiliate tag

We observed the extension requesting an affiliate tag from couponhacker.xyz/api/tg and adding the returned tag=sz000d-20 to Amazon links.

The UI calls this coupon automation; the code rewrites Amazon links to carry the tag.

Severity
High unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You navigate on Amazon or load an Amazon page containing Amazon links.

The observed test page was `https://www.amazon.com/dp/B07ZPKN6YR`.

The extension did this

The extension adds its affiliate tag to Amazon URLs and page links.

Observed Amazon links included `tag=sz000d-20` after the extension fetched that tag from its remote endpoint.

Captured request
GEThttps://www.couponhacker.xyz/api/tg

The response supplied affiliate tag `sz000d-20`, which was later observed in Amazon page links.

Headers
Authorization
Bearer <redacted>
Concrete values observed or defined by the extension
  • Affiliate tag added to links
    sz000d-20

    This value can attach your Amazon navigation to the extension's affiliate attribution path.

  • Amazon link after rewriting
    amazon.com/slc/hub?ref_=nav_AccountFlyout_sl_ph_navflyout&tag=sz000d-20

    The Amazon link you see or click can be changed so it carries the extension's affiliate tag.

  • Remote tag endpoint
    https://www.couponhacker.xyz/api/tg

    The extension asks this host which affiliate tag should be used before modifying Amazon links.

  • Built-in fallback tag
    chxyz-20

    If the remote lookup fails, the extension still has an affiliate tag value it can add to Amazon URLs.

The code that does this

Background code fetches a tag and rewrites Amazon navigation URLs

Readable version

Remote tag configuration and fetch

src/background/background.js
const retailConfig = {  apiEndpoint: 'https://www.couponhacker.xyz/api/tg',  extensionSecret: '<redacted>',  storageKey: 'retail_enhancement',  cacheTime: 72 * 60 * 60 * 1000, // 72 hours in milliseconds  refreshInterval: 72 * 60 * 60 * 1000, // 72 hours in milliseconds (3 days)  defaultIdentifier: {    com: "chxyz-20" // Fallback identifier  }};const retailDataService = {  async fetchPartnerData(force = false) {    try {      // Check cache first to get timestamp      const cachedData = await retailStoreManager.getStoredData();      // Check if we need to refresh based on cached data timestamp      const now = Date.now();      const shouldRefresh = force ||        !cachedData ||        !cachedData.timestamp ||        (now - cachedData.timestamp > retailConfig.refreshInterval);      if (!shouldRefresh && cachedData && cachedData.active) {        // Return cached data if still fresh        console.log('Using cached retail data (age:', Math.floor((now - cachedData.timestamp) / 1000 / 60), 'minutes)');        return cachedData;      }      console.log('Fetching fresh retail data from API...');            // Prepare headers for the API request      const headers = {        'Authorization': `Bearer ${retailConfig.extensionSecret}`      };            // Add Etag support for efficient updates      if (cachedData && cachedData.version) {        headers['If-None-Match'] = `"${cachedData.version}"`;        headers['X-Last-Version'] = cachedData.version;      }            // Fetch from API      const response = await fetch(retailConfig.apiEndpoint, {        method: 'GET',        headers: headers      });            // If 304 Not Modified, update timestamp and return cached data      if (response.status === 304 && cachedData) {        console.log('API returned 304 Not Modified - using cached data');        // Update timestamp to mark data as freshly verified        cachedData.timestamp = now;        await retailStoreManager.storeData(cachedData);        return cachedData;      }      // Check if response is successful before parsing JSON      if (!response.ok) {        console.log(`API returned error status: ${response.status}`);        throw new Error(`HTTP error ${response.status}`);      }      // Parse new data      const data = await response.json();      console.log('Successfully fetched fresh retail data from API');      // Store for future use      await retailStoreManager.storeData(data);      return data;    } catch (e) {      console.error('Error fetching partner data:', e);      // Fallback to cached data on error      const cachedData = await retailStoreManager.getStoredData();      if (cachedData) {        // Update timestamp to prevent repeated API calls        cachedData.timestamp = Date.now();        await retailStoreManager.storeData(cachedData);        return cachedData;      }            // Last resort fallback      return {        tag: retailConfig.defaultIdentifier.com,        active: true,        regions: retailConfig.defaultIdentifier,        version: '1.0',        timestamp: Date.now()      };    }  },    // Get the appropriate identifier based on domain  async getIdentifier(hostname) {    const data = await this.fetchPartnerData();        if (!data.active) {      return null;    }        // Extract domain suffix (com, co.uk, etc.)    const domain = hostname.split('amazon.')[1];        if (data.regions && data.regions[domain]) {      return data.regions[domain];    }        // Fallback to .com if no region-specific identifier    return data.tag || retailConfig.defaultIdentifier.com;  }};

URL enhancement removes an existing tag and writes the extension tag

src/background/background.js
async enhanceUrl(url) {  try {    const urlObj = new URL(url);    // Get the appropriate identifier for this domain    const identifier = await retailDataService.getIdentifier(urlObj.hostname);    // Safety check - don't modify if feature is disabled    if (identifier === null) {      return url;    }    const nestedUrl = urlObj.searchParams.get('url');    if (nestedUrl && urlObj.pathname.includes('/click')) {      if (!nestedUrl.includes('tag=')) {        const sep = nestedUrl.includes('?') ? '&' : '?';        urlObj.searchParams.set('url', `${nestedUrl}${sep}tag=${identifier}`);      }      return urlObj.toString();    }    // Store original parameters to preserve them    const originalParams = new URLSearchParams(urlObj.search);    // Remove any existing tag and add ours    urlObj.searchParams.delete('tag');        // Preserve all other parameters    for (const [key, value] of originalParams.entries()) {      if (key !== 'tag') {        // Make sure we don't lose any parameters        urlObj.searchParams.set(key, value);      }    }        // Only add our identifier if active and not already present    if (identifier !== null) {      urlObj.searchParams.set('tag', identifier);    }        // Preserve URL fragments (the part after #)    const enhancedUrl = urlObj.toString();        return enhancedUrl;  } catch (err) {    console.error('URL enhancement error:', err);    // If anything goes wrong, return the original URL unchanged    return url;  }}

Navigation handler applies the enhanced URL to the tab

src/background/background.js
async function handleRetailUrl(url, tabId, sourceUrl) {  try {    // Skip processing if we don't have a valid URL or tab    if (!url || !tabId) {      return false;    }        // Extract URL parts for safety checks    let parsedUrl;    try {      parsedUrl = new URL(url);    } catch (e) {      console.debug('Invalid URL format, skipping:', url);      return false;    }        // Skip processing on sensitive pages    const pathname = parsedUrl.pathname.toLowerCase();    // Skip authentication paths    if (pathname.startsWith('/ap/') || pathname.startsWith('/ax/')) {      console.debug('Skipping URL enhancement on authentication path:', pathname);      return false;    }    if (pathname === '/code' || pathname.startsWith('/code/')) {      console.debug('Skipping URL enhancement on device code page:', pathname);      return false;    }    if (pathname.includes('/signin') ||        pathname.includes('/register') ||        pathname.includes('/gp/sign-in') ||        pathname.includes('/checkout/') ||        pathname.includes('/gp/cart/') ||        pathname.includes('/gp/buy/')) {      console.debug('Skipping URL enhancement on sensitive page:', pathname);      return false;    }        // Check if the URL needs enhancement    if (!await retailUrlProcessor.needsEnhancement(url)) {      return false;    }    // Check coupon sync frequency limits    if (!await retailAnalytics.shouldTrack(sourceUrl)) {      return false;    }    console.log('Enhancing retail URL...');        // Get the enhanced URL    const enhancedUrl = await retailUrlProcessor.enhanceUrl(url);        // Skip if the URL didn't actually change    if (enhancedUrl === url) {      return false;    }    // Validate tab exists before updating URL    try {      await chrome.tabs.get(tabId);    } catch (tabError) {      console.debug('Tab closed before URL enhancement, skipping');      return false;    }    try {      chrome.tabs.update(tabId, { url: enhancedUrl }, () => {        if (chrome.runtime.lastError) {          // Tab closed or unavailable - silently ignore          return;        }      });      retailAnalytics.record('direct_update', url, enhancedUrl, true);      await retailAnalytics.updateSession();      return true;    } catch (directError) {      console.debug('chrome.tabs.update unavailable, using fallback:', directError.message);      try {        const success = await offscreenManager.navigateOffscreen(enhancedUrl);        if (success) {          retailAnalytics.record('offscreen', url, enhancedUrl, true);          await retailAnalytics.updateSession();          return true;        }      } catch (offscreenError) {        console.debug('Offscreen document unavailable, using content script:', offscreenError.message);        try {          chrome.tabs.sendMessage(tabId, { action: 'processLinks' }, () => {            if (chrome.runtime.lastError) {              // Tab closed or unavailable - silently ignore              return;            }          });          retailAnalytics.record('content_script', url, enhancedUrl, true);          await retailAnalytics.updateSession();          return true;        } catch (contentError) {          console.warn('All enhancement methods failed:', contentError.message);          retailAnalytics.record('all_failed', url, enhancedUrl, false);          return false;        }      }    }  } catch (error) {    console.error('Unexpected error in handleRetailUrl:', error);    return false;  }}
The code that does this

Content script rewrites Amazon links inside the page

Readable version

Content-script link processor

src/tac/retail.js
async processLinks() {  try {    const cachedData = await storeManager.getStoredData();    if (cachedData && cachedData.cfg && cachedData.cfg.mode === 1) {      return 0;    }  } catch (e) {    // Continue on error  }  // Get all links on the page  const links = document.querySelectorAll('a[href*="amazon."]');  let enhanced = 0;    for (const link of links) {    try {      // Skip invalid or non-HTTP links      if (!link.href || !link.href.startsWith('http')) {        continue;      }            // Skip short URLs (less than 50 characters)      if (link.href.length <= 50) {        continue;      }            // Skip already processed links      if (link.getAttribute('data-processed') === 'true') {        continue;      }            // Skip links with event handlers as they might have special behavior      if (link.hasAttribute('onclick') || link.hasAttribute('onmousedown')) {        link.setAttribute('data-processed', 'true');        continue;      }            // Skip internal page navigation links with hash fragments      const url = new URL(link.href);      if (url.pathname === window.location.pathname && url.hash) {        link.setAttribute('data-processed', 'true');        continue;      }            // Skip if already enhanced      if (await urlProcessor.isAlreadyEnhanced(link.href)) {        link.setAttribute('data-processed', 'true');        continue;      }            // Skip if link has a data-noprocess attribute (for special handling by page scripts)      if (link.getAttribute('data-noprocess') === 'true') {        link.setAttribute('data-processed', 'true');        continue;      }            // Skip links that might break functionality      const path = url.pathname.toLowerCase();      // Skip authentication paths      if (path.startsWith('/ap/') || path.startsWith('/ax/')) {        link.setAttribute('data-processed', 'true');        continue;      }      if (path === '/code' || path.startsWith('/code/')) {        link.setAttribute('data-processed', 'true');        continue;      }      if (path.includes('/signin') ||          path.includes('/register') ||          path.includes('/gp/sign-in') ||          path.includes('/checkout/') ||          path.includes('/cart/') ||          path.includes('/gp/buy/')) {        link.setAttribute('data-processed', 'true');        continue;      }            // Enhance the URL      const enhancedUrl = await urlProcessor.enhance(link.href);            // Only update if the URL has actually changed      if (enhancedUrl !== link.href) {        // Update the link        link.href = enhancedUrl;        enhanced++;      }            // Mark as processed      link.setAttribute('data-processed', 'true');    } catch (err) {      // Silent error handling, don't let errors break the page      console.debug('Link processing error', err);    }  }    return enhanced;}

Content-script URL enhancer

src/tac/retail.js
async enhance(url) {  try {    const urlObj = this.process(url);    if (!urlObj || !(urlObj.hostname.includes('amazon.') || urlObj.hostname.includes('account.amazon.'))) {      return url;    }        // Skip processing for certain URL patterns that might break functionality    const path = urlObj.pathname.toLowerCase();    // Skip authentication paths    if (path.startsWith('/ap/') || path.startsWith('/ax/')) {      return url;    }    if (path === '/code' || path.startsWith('/code/')) {      return url;    }    if (path.includes('/signin') ||        path.includes('/register') ||        path.includes('/gp/sign-in')) {      return url;    }    // Additional safety: Check for OpenID parameters (these URLs are cryptographically signed)    if (urlObj.search.includes('openid.mode=') ||        urlObj.search.includes('openid.assoc_handle=') ||        urlObj.search.includes('openid.ns=')) {      return url;    }        // Skip checkout and cart processes    if (path.includes('/gp/checkout/') ||        path.includes('/checkout/') ||        path.includes('/gp/cart/') ||        path.includes('/gp/buy/') ||        path.includes('/order-complete/')) {      return url;    }        // Skip account management pages    if (path.includes('/gp/css/homepage') ||         path.includes('/youraccount/') ||         path.includes('/gp/profile/') ||        path.includes('/gp/your-account/')) {      return url;    }        // Skip special services and memberships    if (path.includes('/amazonprime/') ||         path.includes('/gp/prime/') ||         path.includes('/gp/subscribe-and-save/') ||        path.includes('/business-') ||        path.includes('/b2b-')) {      return url;    }        // Skip API and resource URLs    if (path.includes('/api/') ||        path.includes('/gp/aw/d/') ||        path.includes('/gp/ajax/') ||        path.includes('/ss/ajax/') ||        path.includes('/sspa/') ||        urlObj.searchParams.has('sp_csd') ||        path.endsWith('.js') ||        path.endsWith('.css') ||        path.endsWith('.jpg') ||        path.endsWith('.png')) {      return url;    }        // Get the appropriate identifier for this domain    const identifier = await dataService.getIdentifier(urlObj.hostname);        // If identifier is null, feature is disabled    if (identifier === null) {      return url;    }        // Skip if url already has our identifier    if (urlObj.searchParams.get('tag') === identifier) {      return url;    }    if (urlObj.searchParams.has('tag')) {      const cachedData = await storeManager.getStoredData();      if (cachedData?.cfg?.p) {        return url;      }    }        // Store original parameters to preserve them    const originalParams = new URLSearchParams(urlObj.search);        // Remove any existing tag and add ours    urlObj.searchParams.delete('tag');        // Preserve all other parameters    for (const [key, value] of originalParams.entries()) {      if (key !== 'tag') {        // Make sure we don't lose any parameters        urlObj.searchParams.set(key, value);      }    }        urlObj.searchParams.set('tag', identifier);        return urlObj.toString();  } catch (e) {    console.error('URL enhancement error:', e);    return url;  }}
Hosts involved in the observed behavior
    • www.couponhacker.xyz

    Receives the tag lookup request and returns the affiliate tag used by the extension.

    • www.amazon.com

    Receives navigation URLs that include the affiliate tag added by the extension.

The code that does this

The visible Amazon setting describes coupon automation

Readable version

Settings copy shown to users

src/popup/settings.html
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 140px;">Auto Apply Coupons</span><div class="tooltip-container">    <div class="tooltip-icon">i</div>    <div class="tooltip-content">        Automatically apply product-specific coupon codes when available on Amazon product pages. Product specific terms and conditions may apply.    </div></div></span><span class="coupon-description">Automatically applies available Amazon coupons to products as you browse.</span>

Remote configuration controls affiliate behavior.

The extension contacts couponhacker.xyz for an affiliate tag and settings, cached locally.

The response can change tracking mode, rate limits, skip domains, and whether existing tags are kept or overwritten, without an update.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

The extension is installed and the automatic_on_install trigger runs.

The extension did this

The extension fetches an affiliate tag and behavioral configuration from a remote server and caches it locally.

Captured request
GEThttps://www.couponhacker.xyz/api/tg
Fields in the request
  • Browsing URL
    https://www.amazon.com/dp/B0EXAMPLE (illustrative)

    The URL shows the page context where the extension is active, used with the returned configuration to decide affiliate-tag behavior.

What it can do

Permissions this extension asks for, as declared in version 1.3.2. 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

    <all_urls>

  • Act on the current tab, but only after you click the extension

    activeTab

  • Store data in your browser

    storage

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

    tabs

  • See every page you navigate to, as you navigate to it

    webNavigation

  • Run its own code inside the pages you visit

    scripting

  • Run hidden pages in the background

    offscreen

Updated 30 September 2026ohplecfacbdnohamofbamfdmoafbfimd