Is Image downloader - Imageye safe?

Medium risk

Imageye is medium risk. Imageye rewrites values saved to Chrome local storage with an `imageye` prefix, string reversal, and a character shift. DA observed it writing `displayMode` as `imageye%ohqdShglv%`, decoding to `"sidePanel"`.

meaganamrachv5.23.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

Imageye encodes Chrome local storage values

Imageye rewrites values saved to Chrome local storage with an `imageye` prefix, string reversal, and a character shift.

DA observed it writing `displayMode` as `imageye%ohqdShglv%`, decoding to `"sidePanel"`.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install or use the extension and it saves local preferences.

The extension did this

The extension stores those values with an `imageye` prefix and a reversible character transformation.

02EvidenceFIELD TABLE
Local storage fields shown by code and observation
FieldValueWhy it matters
Display preference
displayMode = imageye%ohqdShglv%This records how the extension opens its interface for you. Dynamic analysis observed it being saved in encoded form.
Scraping conditions key
SCRAPING_CONDITIONSThis key names extension configuration used by the image-collection logic. The same storage adapter handles it.
Last replacement time
LRTThis timing value helps the extension remember recent activity between browsing sessions.
03EvidenceSTORAGE DUMP
What's stored on your device

A direct browser-storage inspection shows an encoded value instead of the readable setting name.

Locationchrome.storage.local key `displayMode`
Contents (JSON)
{
  "displayMode": "imageye%ohqdShglv%"
}
04EvidenceOPAQUE REVEAL
Why you can't catch this in DevTools

The stored value is reversible using the adapter logic shipped with the extension.

What's actually being sent
"sidePanel"
05EvidenceCODE COMPARE
The code that does this

The bundled adapter encodes values before writing to Chrome local storage

What it actually does
Readable equivalent of the shared storage adapterdeobfuscated/background.js and deobfuscated/inject.js
const STORAGE_PREFIX = "imageye";

const storageAdapter = {
  async getItem(key) {
    const values = await chrome.storage.local.get([key]);
    const stored = values[key];

    if (typeof stored === "string" && stored.startsWith(STORAGE_PREFIX)) {
      try {
        const shiftedBack = stored
          .slice(7)
          .split("")
          .map((char) => String.fromCharCode(char.charCodeAt(0) - 3))
          .join("");
        return JSON.parse(shiftedBack.split("").reverse().join(""));
      } catch (error) {
        return null;
      }
    }

    return stored;
  },

  async setItem(key, value) {
    try {
      if (typeof value === "string" && value.startsWith(STORAGE_PREFIX)) {
        return chrome.storage.local.set({ [key]: value });
      }

      const json = JSON.stringify(value);
      const reversed = json.split("").reverse().join("");
      const shifted = reversed
        .split("")
        .map((char) => String.fromCharCode(char.charCodeAt(0) + 3))
        .join("");
      return chrome.storage.local.set({ [key]: STORAGE_PREFIX + shifted });
    } catch (error) {
      return Promise.reject(new Error("Value must be JSON-serializable"));
    }
  },

  removeItem(key) {
    return chrome.storage.local.remove(key);
  },
};
Readable equivalent of the displayMode writedeobfuscated/background.js
const STORAGE_KEYS = {
  SCRAPING_CONDITIONS: "SCRAPING_CONDITIONS",
  LAST_REPLACEMENT_AT: "LRT",
  DISPLAY_MODE: "displayMode",
};

function setDisplayMode(tabId, requestedMode) {
  let mode = requestedMode;
  if (!globalThis.canDisplayInSidePanel) {
    mode = "popup";
  }

  storageAdapter.setItem(STORAGE_KEYS.DISPLAY_MODE, mode);

  if (mode === "sidePanel") {
    chrome.action.setPopup({ popup: "" });
    chrome.sidePanel.setOptions({ tabId, path: "popup.html", enabled: true });
  } else {
    chrome.action.setPopup({ popup: "popup.html" });
    if (chrome.sidePanel) {
      chrome.sidePanel.setOptions({ tabId, enabled: false });
    }
  }
}
06EvidenceARTIFACT
Reproduce it yourself

Decodes Imageye local-storage values that start with the `imageye` prefix so you can inspect the stored setting.

RequiresNode.js 18+
decode-imageye-storage.js · js
const value = process.argv[2];

if (!value) {
  console.error("Usage: node decode-imageye-storage.js 'imageye%ohqdShglv%'");
  process.exit(1);
}

const prefix = "imageye";
if (!value.startsWith(prefix)) {
  console.error("Input does not start with the imageye prefix.");
  process.exit(1);
}

const decodedJson = value
  .slice(prefix.length)
  .split("")
  .map((char) => String.fromCharCode(char.charCodeAt(0) - 3))
  .join("")
  .split("")
  .reverse()
  .join("");

console.log(JSON.parse(decodedJson));
How to run it
  1. 1
    node decode-imageye-storage.js 'imageye%ohqdShglv%'
Updated 30 September 2026agionbommeaifngbhincahgmoflcikhm