Is Revoke.cash - web3 scam protection safe?

High risk

Revoke.cash - web3 scam protection is high risk. Starting a wallet transaction or signature request, the content script forwards details to the SW, logged as "Message received". Details can include addresses, calldata, signature data, chain ID, hostname, a user ID. Evidence is code-based.

Revoke.cashv1.5.1Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Wallet messages are forwarded to Amplitude analytics

Starting a wallet transaction or signature request, the content script forwards details to the SW, logged as "Message received".

Details can include addresses, calldata, signature data, chain ID, hostname, a user ID.

Evidence is code-based.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You start a protected wallet action on a website.

The covered requests include transactions, typed signatures, personal-sign messages, wallet_sendCalls, and Coinbase wallet signing requests.

The extension did this

The extension forwards the wallet request details to its service worker and labels the full object as analytics.

The service worker calls the tracking helper with a Message received event whose message property is the original wallet-message object.

02EvidenceFIELD TABLE
Fields the source code places in the wallet-message object
FieldValueWhy it matters
Website hostname
app.uniswap.org (illustrative)Shows which site you were using when the wallet request was processed.
Wallet request type
eth_sendTransaction (illustrative)Shows what kind of wallet action you were asked to approve or sign.
Wallet address fields
from=0x742d35Cc6634C0532925a3b844Bc454e4438f44e, to=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 (illustrative)Can identify which wallet address was involved in the request.
Transaction calldata
0x095ea7b30000000000000000000000001111111254eeb25477b68fb85ed929f73a9605820000000000000000000000000000000000000000000000000000000000000000 (illustrative)Can reveal the contract method and parameters behind the action you were asked to approve.
Signature message content
personal_sign message: Sign in with Ethereum to app.uniswap.org (illustrative)Can include the message or typed data you were asked to sign.
Persistent extension user ID
f3a742c1-8e10-44d2-bc6a-9d5e0c2a7814 (illustrative)Lets analytics associate separate wallet-message events with the same extension installation over time.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://api2.amplitude.com/2/httpapi
Dynamic analysis captured Amplitude POST traffic during onboarding, but those captured bodies did not contain the wallet-message indicators.
04EvidenceCODE COMPARE
The code that does this

The content script packages wallet requests with hostname and chain ID

What it actually does
Readable content-script excerptjs/content-scripts/bypass-check.js
const os = (e, t) => {
  const n = as(t);
  e.postMessage({
    requestId: n,
    data: t
  })
};
let is = 0;
const as = e => {
  const t = ++is;
  return e.type === ns.TRANSACTION ? ss()({
    ...e.transaction,
    _nonce: t
  }) : e.type === ns.BATCH_TRANSACTION ? ss()({
    _batch: e.transactions,
    _nonce: t
  }) : e.type === ns.TYPED_SIGNATURE ? ss()({
    ...e.typedData,
    _nonce: t
  }) : e.type === ns.UNTYPED_SIGNATURE ? ss()({
    _msg: e.message,
    _nonce: t
  }) : ss()({
    ...e,
    _nonce: t
  })
};
let cs = 1;
const us = !0;
window.addEventListener("message", e => {
  const {
    target: t
  } = e?.data ?? {}, {
    name: n,
    data: r
  } = e?.data?.data ?? {}, {
    hostname: s
  } = location, o = cs;
  if ("metamask-provider" !== n || !r) return;
  const i = e => {
    Array.isArray(e) ? e.forEach(e => {
      if (e)
        if (ls(e, "eth_sendTransaction")) {
          const [t] = e.params ?? [], n = ns.TRANSACTION, r = es().runtime.connect({
            name: ts
          });
          os(r, {
            type: n,
            bypassed: us,
            hostname: s,
            transaction: t,
            chainId: o
          })
        } else if (ls(e, "eth_signTypedData")) {
        const [t, n] = e.params ?? [], r = JSON.parse(n), i = ns.TYPED_SIGNATURE, a = es().runtime.connect({
          name: ts
        });
        os(a, {
          type: i,
          bypassed: us,
          hostname: s,
          address: t,
          typedData: r,
          chainId: o
        })
      } else if (ls(e, "eth_sign") || ls(e, "personal_sign")) {
        const [t, n] = e.params ?? [], r = 40 === String(t).replace(/0x/, "").length ? n : t, o = ns.UNTYPED_SIGNATURE, i = es().runtime.connect({
          name: ts
        });
        os(i, {
          type: o,
          bypassed: us,
          message: r,
          hostname: s
        })
      } else if (ls(e, "wallet_sendCalls")) {
        const [t] = e.params ?? [], {
          from: n = "0x0000000000000000000000000000000000000000",
          calls: r
        } = t ?? {}, i = ns.TRANSACTION, a = es().runtime.connect({
          name: ts
        });
        for (const e of r) {
          const t = {
            from: n,
            ...e
          };
          os(a, {
            type: i,
            bypassed: us,
            hostname: s,
            transaction: t,
            chainId: o
          })
        }
      }
    }) : i([e])
  };
  "metamask-contentscript" === t && i(r), "metamask-inpage" === t && r?.method?.includes("chainChanged") && (cs = Number(r?.params?.chainId ?? cs))
}), window.addEventListener("message", e => {
  const {
    type: t,
    data: n
  } = e?.data ?? {}, {
    hostname: r
  } = location;
  if ("extensionUIRequest" === t && n) {
    if ("signEthereumTransaction" === n.request?.method) {
      const e = {
          from: n.request.params.fromAddress,
          to: n.request.params.toAddress,
          data: n.request.params.data,
          value: Number.parseInt(n.request.params.weiValue ?? "0").toString(16)
        },
        t = Number(n.request.params.chainId ?? 1),
        s = ns.TRANSACTION,
        o = es().runtime.connect({
          name: ts
        });
      os(o, {
        type: s,
        bypassed: us,
        hostname: r,
        transaction: e,
        chainId: t
      })
    }
    if ("signEthereumMessage" === n.request?.method) {
      const e = n.request.params.typedDataJson,
        t = n.request.params.address;
      if (e) {
        const n = JSON.parse(e),
          s = Number(n?.domain?.chainId ?? 1),
          o = ns.TYPED_SIGNATURE,
          i = es().runtime.connect({
            name: ts
          });
        os(i, {
          type: o,
          bypassed: us,
          hostname: r,
          address: t,
          typedData: n,
          chainId: s
        })
      } else {
        const e = n.request.params.message,
          t = ns.UNTYPED_SIGNATURE,
          s = es().runtime.connect({
            name: ts
          });
        os(s, {
          type: t,
          bypassed: us,
          hostname: r,
          message: e
        })
      }
    }
  }
});
const ls = (e, t) => String(e?.method)?.toLowerCase()?.includes(t?.toLowerCase())
05EvidenceCODE COMPARE
The code that does this

The service worker tracks the whole message object

What it actually does
Readable Amplitude setup and tracking helperjs/background.js
var zo, Do = Bo.init,
  Oo = Bo.track;
const ac = () => Boolean("20f100b65efa90a8e1511c26488a4a83") && !0;
(async () => {
  if (!ac()) return;
  const e = await vs("sync", "user:id");
  Yo = e ?? (() => {
    let e = () => Math.floor(65536 * (1 + Math.random())).toString(16).substring(1);
    return e() + e() + "-" + e() + "-" + e() + "-" + e() + "-" + e() + e() + e()
  })(), e || await (async (e, t, n) => {
    await r().storage.sync.set({
      [t]: n
    })
  })(0, "user:id", Yo), Do("20f100b65efa90a8e1511c26488a4a83", Yo, {
    trackingOptions: {
      ipAddress: !0
    },
    flushQueueSize: 1
  })
})();
const ic = (e, t) => {
  ac() && (Oo(e, t), ec(e, t))
}
Readable Message received wrapperjs/background.js
ry = e => {
  Of.includes(e.requestId) || (Of.push(e.requestId), ic("Message received", {
    message: e
  }))
}
06EvidenceTHIRD PARTY LIST
Remote hosts reached by the analytics paths
  • api2.amplitude.com

    Amplitude HTTP API endpoint embedded in the SDK; dynamic analysis captured POST traffic to this host.

  • api.eu.amplitude.com

    Alternate Amplitude HTTP API endpoint present in the bundled SDK endpoint selector.

  • api.fairside.io

    Extension backend endpoint used by the same tracking helper's queued event flush path.

Updated 17 September 2026nmniboccheadcclilkfkonokbcoceced