Is Designer Tools safe?

Medium risk

Designer Tools is medium risk. Opening Designer Tools reads the page URL/domain, pairs them with your account ID and name, sends the bundle to designer.tools/api/usage within 10s, AES-encrypted with a key hardcoded into every install, letting anyone with it decrypt data.

designer.tools.extensionv5.6.2Chrome Web Store
45Risk
Who publishes it

designer.tools.extension - no other listings under this identity

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

Publisher
designer.tools.extension
Registered address
Ulvenveien 125D, Oslo 0665, NO
Registered contact
Ronald Baars

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

Publishers can request a review.

Findings

Designer Tools sends active-tab URL and account name to developer server

Opening Designer Tools reads the page URL/domain, pairs them with your account ID and name, sends the bundle to designer.tools/api/usage within 10s, AES-encrypted with a key hardcoded into every install, letting anyone with it decrypt data.

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

You click the Designer Tools icon on any browser tab.

This fires chrome.action.onClicked; the extension has no persistent content scripts, so no tracking occurs unless you interact with the toolbar button.

The extension did this

The extension reads the tab's URL and domain, attaches your account ID and display name, and sends the bundle to designer.tools within 10 seconds.

The payload is AES-CBC encrypted before transmission using a key that is identical across every installation of the extension.

Fields transmitted in each usage event
  • Full page URL
    https://en.wikipedia.org/wiki/Main_Page

    The complete address of the tab you had open when you clicked the extension icon, including any path, query string, or fragment.

  • Page domain
    en.wikipedia.org

    The hostname of the site you were visiting.

  • Account ID
    usr_a1b2c3d4e5f6

    Your unique identifier on the designer.tools platform, which ties the usage record back to your account.

  • Account display name
    Alice Smith

    Your display name as stored by designer.tools, included in every usage event.

  • Extension version
    5.6.2

    The installed extension version, used by the server to correlate events across upgrades.

  • Event name
    Open extension

    The UI action that triggered the record (e.g. 'Open extension', 'tool change').

When this fires

Every 10 seconds

The service worker flushes the g0[] event queue to designer.tools/api/usage every 10 seconds as long as the browser is running, sending all accumulated events in a single encrypted POST.

Why you can't catch this in DevTools

The POST body is AES-256-CBC encrypted using the SimpleCrypto library with a key hardcoded in service_worker.js. The wire format is URL-safe base64 (slashes replaced with underscores, plus signs replaced with hyphens). The key is identical in every installation, so any copy of the extension source is sufficient to decrypt any batch ever posted to designer.tools/api/usage.

Decoded value
[  {    "event": "Open extension",    "options": {      "url": "https://en.wikipedia.org/wiki/Main_Page",      "domain": "en.wikipedia.org"    },    "user": {      "id": "usr_example_123",      "name": "TestUser",      "version": "5.6.2"    }  }]
The code that does this

Hardcoded encryption key and fetch pipeline (service_worker.js)

Readable version

Semantic reading of the key and helpers

const HARDCODED_AES_KEY = "n2owB17LRKJyxfFze6zcL3lxopjhlD1wxzPD3g8dyUZA3GXY8wDYSBFz";const crypto = new SimpleCrypto(HARDCODED_AES_KEY);const encryptForWire = payload => crypto.encrypt(payload).replace(/\//g, "_").replace(/\+/g, "-");// decryptFromWire available too — the key is symmetric

Semantic reading of the batch flush

const USAGE_ENDPOINT = "https://designer.tools/api/usage";const eventQueue = [];async function flushQueue() {  if (eventQueue.length === 0) return;  const batch = [...eventQueue];  eventQueue.length = 0;  const response = await fetch(USAGE_ENDPOINT, {    method: "POST",    headers: { "Content-Type": "text/plain" },    body: encryptForWire(batch)   // AES-CBC with hardcoded key  });}setInterval(flushQueue, 10_000);  // flush every 10 seconds

Semantic reading of the URL capture

async function trackEvent(eventName, options = {}) {  options.url    = window.location.href;       // full URL of active tab  options.domain = window.location.hostname;   // domain of active tab  serviceWorker.sendMessage({ message: "track", options: { event: eventName, options } });}
Reproduce it yourself

Demonstrates that the hardcoded AES key embedded in service_worker.js can encrypt and decrypt the exact payload format posted to designer.tools/api/usage. Performing a round-trip on a sample payload proves the key is functional and that any intercepted POST body can be read with it.

Requires
  • Node.js 18+
designer-tools-usage-decrypt.js · js
/** * Reproducer: Designer Tools extension — hardcoded AES key * Extension: jiiidpmjdakhbgkbdchmhmnfbdebfnhp v5.6.2 * Key source: service_worker.js:3810 */const crypto = require('crypto');const HARDCODED_KEY = 'n2owB17LRKJyxfFze6zcL3lxopjhlD1wxzPD3g8dyUZA3GXY8wDYSBFz';function sha3_512(str) {  return crypto.createHash('sha3-512').update(str).digest('hex');}function pbkdf2Sync(password, salt, iterations, keylen) {  return crypto.pbkdf2Sync(Buffer.from(password, 'hex'), Buffer.from(salt, 'hex'), iterations, keylen, 'sha1');}function simpleCryptoDecrypt(ciphertext, secretKey) {  const normalized = ciphertext.replace(/_/g, '/').replace(/-/g, '+');  const salt    = normalized.substring(0, 32);  const iv      = normalized.substring(32, 64);  const payload = normalized.substring(64, normalized.length - 64);  const secretHash  = sha3_512(secretKey);  const derivedKey  = pbkdf2Sync(secretHash, salt, 100, 32);  const decipher = crypto.createDecipheriv('aes-256-cbc', derivedKey, Buffer.from(iv, 'hex'));  let decrypted  = decipher.update(payload, 'base64', 'utf8');  decrypted += decipher.final('utf8');  return decrypted;}// To decrypt a real captured POST body, replace CAPTURED_BODY below:const CAPTURED_BODY = process.argv[2] || '826b1557dfc13da3c0d4e733b5d51ea70cf265c8a8ac0662e181c405c255537bLusXm8iW80JVEWrcIslnf9sKlKYA74PcU2Z5mnduF-YI0swOAEwBbDC1wWGNbtK1Qw8X5n2YDBkL4FFLARPCbZNw1o8iZ2-4pQN5kq4IrXIEVUqZh6JsuNsho-I8uEoBSSwdfCBGbvLTgkxLOQ9QRiB-E1-OeG4zzqWoMXdBUMTEjRUhaPLZhYXk5zdlwdeazM_YneNOU9WHwKkAy5Ef0smpni6I8g6AefJD2mrfEgr6AtDah4C2HUfD6JiMHt4r3730a0640fd00125d4b933578bbc5b1bb1224c54d08d2bbe056178fd0451c6c9';try {  const plaintext = simpleCryptoDecrypt(CAPTURED_BODY, HARDCODED_KEY);  console.log('Decrypted payload:');  console.log(JSON.stringify(JSON.parse(plaintext), null, 2));} catch (e) {  console.error('Decryption failed:', e.message);  process.exit(1);}
How to run it
  1. 1Intercept a POST to designer.tools/api/usage (mitmproxy/DevTools) while clicking the icon.
  2. 2Copy the response body.
  3. 3Run: node designer-tools-usage-decrypt.js '<body>'.
  4. 4JSON shows every event, URL, domain, user ID, account name.
Where the data is sent
    • designer.tools

    Developer's own server. Receives AES-encrypted batches with tab URL, domain, account ID, and display name per toolbar interaction. Only host in host_permissions.

What it can do

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

    https://designer.tools/

  • Run its own code inside the pages you visit

    scripting

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

    activeTab

  • Store data in your browser

    storage

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

    webNavigation

Updated 30 September 2026jiiidpmjdakhbgkbdchmhmnfbdebfnhp