Aegis: a WizardConnect request the overlay cannot read is not signed
wc-sign accepted a flat request (tx and sourceOutputs at the top) that buildWcApproval does not read, so any paired dapp could get a signature after an overlay showing only the wallet, an input count and the sighash. The signer now takes only the nested WizardConnect shape, and the overlay refuses, without showing anything, a request whose outputs or spent inputs it cannot decode. Total out now sums every output, not the first 8.
This commit is contained in:
parent
e02d4698ea
commit
2faa3d808a
2 changed files with 17 additions and 8 deletions
|
|
@ -980,6 +980,13 @@ function buildWcApproval(payload) {
|
|||
inner = typeof dec === "string" ? null : dec;
|
||||
} catch { inner = null; }
|
||||
}
|
||||
// Refuse what cannot be shown. The signer only takes this shape (see
|
||||
// wc-sign.js), and an overlay without outputs or spent inputs is no
|
||||
// approval at all.
|
||||
if (!inner || !Array.isArray(inner.inputs) || !Array.isArray(inner.outputs) || !inner.outputs.length
|
||||
|| !Array.isArray(tx.sourceOutputs) || tx.sourceOutputs.length !== inner.inputs.length) {
|
||||
return { unreadable: true, dappName };
|
||||
}
|
||||
const inputCount = Array.isArray(req.inputPaths) ? req.inputPaths.length : (Array.isArray(inner?.inputs) ? inner.inputs.length : "?");
|
||||
const rows = [{ label: "Wallet", value: walletName }, { label: "Inputs", value: String(inputCount) }];
|
||||
// What the wallet is putting IN. The outputs alone never showed that a
|
||||
|
|
@ -1011,6 +1018,7 @@ function buildWcApproval(payload) {
|
|||
try {
|
||||
const outs = inner?.outputs || [];
|
||||
let total = 0n;
|
||||
for (const o of outs) { try { total += BigInt(o.valueSatoshis ?? 0); } catch {} }
|
||||
outs.slice(0, 8).forEach((o, i) => {
|
||||
const lb = o.lockingBytecode;
|
||||
const hexScript = typeof lb === "string" ? lb.toLowerCase() : Buffer.from(lb || []).toString("hex");
|
||||
|
|
@ -1018,7 +1026,6 @@ function buildWcApproval(payload) {
|
|||
if (/^76a914[0-9a-f]{40}88ac$/.test(hexScript)) addr = ctx.d.cashaddr.encode(prefix, 0, ctx.d.tx.fromHex(hexScript.slice(6, 46)));
|
||||
else if (/^a914[0-9a-f]{40}87$/.test(hexScript)) addr = ctx.d.cashaddr.encode(prefix, 1, ctx.d.tx.fromHex(hexScript.slice(4, 44)));
|
||||
const v = BigInt(o.valueSatoshis ?? 0);
|
||||
total += v;
|
||||
const token = tokenText(o.token);
|
||||
rows.push({ label: `Output #${i + 1}`, value: `${fmtBch(Number(v))} BCH${token} → ${addr || (hexScript.startsWith("6a") ? "OP_RETURN data" : "script " + hexScript.slice(0, 24) + "…")}`, mono: true });
|
||||
});
|
||||
|
|
@ -4150,7 +4157,8 @@ module.exports = {
|
|||
// keys fell back to a lone "OK" button whose id never matched, so
|
||||
// every WizardConnect signing request was refused, and the HTML
|
||||
// body was shown as literal markup.
|
||||
const { body, rows, dappName } = buildWcApproval(payload);
|
||||
const { body, rows, dappName, unreadable } = buildWcApproval(payload);
|
||||
if (unreadable) { api.log(`wc sign: refused an unreadable request from ${dappName}`); return { approved: false }; }
|
||||
const pick = await api.approvalModal({
|
||||
title: "Sign a Bitcoin Cash transaction (WizardConnect)?",
|
||||
origin: dappName,
|
||||
|
|
|
|||
|
|
@ -66,12 +66,13 @@ async function signTx({ request, account, branches, libauth, secp256k1 }) {
|
|||
// the outputs) meant `tx.inputs` was undefined and signing threw on the
|
||||
// first real request. index.js already read the nested
|
||||
// request.transaction.userPrompt for the approval dialog, so only this
|
||||
// module had it wrong. The flat shape is still accepted so a caller
|
||||
// that hands us an already-unwrapped payload keeps working.
|
||||
const inner = (request.transaction && (request.transaction.transaction !== undefined
|
||||
|| request.transaction.sourceOutputs !== undefined))
|
||||
? request.transaction
|
||||
: request;
|
||||
// module had it wrong. Only the nested shape is accepted: the approval
|
||||
// overlay reads that shape, and a flat request used to be signed after an
|
||||
// overlay that could show neither its outputs nor what it spends.
|
||||
const inner = request.transaction;
|
||||
if (!inner || typeof inner !== "object" || inner.transaction === undefined || !Array.isArray(inner.sourceOutputs)) {
|
||||
throw new Error("wc-sign: request is not a WizardConnect sign request (request.transaction.{transaction,sourceOutputs})");
|
||||
}
|
||||
const tx = ensureTransaction(inner.transaction, libauth);
|
||||
const sourceOutputs = (inner.sourceOutputs || []).map((o, i) => {
|
||||
if (o.contract) throw new Error(`wc-sign: input ${i} spends a contract — unsupported`);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue