Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b261bafb03 |
@@ -50,10 +50,8 @@ but the review is broader than any of them.
|
|||||||
every request's full URL and body, so an RPC endpoint with a key in its path
|
every request's full URL and body, so an RPC endpoint with a key in its path
|
||||||
or query string printed that key on every request. It now logs the HTTP
|
or query string printed that key on every request. It now logs the HTTP
|
||||||
method, the URL's origin and, for a JSON-RPC call, the method name. The
|
method, the URL's origin and, for a JSON-RPC call, the method name. The
|
||||||
balance refresh and token lookup log the RPC endpoint by its origin too. A
|
balance refresh and token lookup log the RPC endpoint by its origin too. The
|
||||||
failed RPC call's error line prints the error's short message, which names the
|
README's DEBUG Mode Policy says what debug mode logs.
|
||||||
HTTP status, not its full message, which carries the request URL. The README's
|
|
||||||
DEBUG Mode Policy says what debug mode logs.
|
|
||||||
|
|
||||||
- 2026-10-04: A site has at most one connection prompt and one signature prompt
|
- 2026-10-04: A site has at most one connection prompt and one signature prompt
|
||||||
open at a time ([#405](https://git.eeqj.de/sneak/AutistMask/issues/405)). Each
|
open at a time ([#405](https://git.eeqj.de/sneak/AutistMask/issues/405)). Each
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ async function estimateGas(txInfo) {
|
|||||||
feeWei = gasCostWei;
|
feeWei = gasCostWei;
|
||||||
renderValidation(txInfo);
|
renderValidation(txInfo);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.errorf("gas estimation failed:", e.shortMessage || e.message);
|
log.errorf("gas estimation failed:", e.message);
|
||||||
if (pendingTx !== txInfo) return;
|
if (pendingTx !== txInfo) return;
|
||||||
$("confirm-fee-amount").textContent = "Unable to estimate";
|
$("confirm-fee-amount").textContent = "Unable to estimate";
|
||||||
setVisible("confirm-fee-reserve", false);
|
setVisible("confirm-fee-reserve", false);
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ function startWait(txInfo, txHash, broadcastTime, pollNow) {
|
|||||||
// failed — which matters most on a resumed wait, where the
|
// failed — which matters most on a resumed wait, where the
|
||||||
// first poll is already past the deadline.
|
// first poll is already past the deadline.
|
||||||
answered = false;
|
answered = false;
|
||||||
log.errorf("poll receipt failed:", e.shortMessage || e.message);
|
log.errorf("poll receipt failed:", e.message);
|
||||||
}
|
}
|
||||||
// The lookup is async: the wait may have ended while it was in
|
// The lookup is async: the wait may have ended while it was in
|
||||||
// flight, in which case this result must not touch the view.
|
// flight, in which case this result must not touch the view.
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ async function getFullWarnings(address, provider, options = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.errorf("contract check failed:", e.shortMessage || e.message);
|
log.errorf("contract check failed:", e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip tx count check for contracts — they may legitimately have
|
// Skip tx count check for contracts — they may legitimately have
|
||||||
@@ -92,7 +92,7 @@ async function getFullWarnings(address, provider, options = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.errorf("tx count check failed:", e.shortMessage || e.message);
|
log.errorf("tx count check failed:", e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ async function refreshBalances(
|
|||||||
log.errorf(
|
log.errorf(
|
||||||
"ENS reverse failed",
|
"ENS reverse failed",
|
||||||
addr.address,
|
addr.address,
|
||||||
e.shortMessage || e.message,
|
e.message,
|
||||||
);
|
);
|
||||||
// Keep existing addr.ensName if we had one
|
// Keep existing addr.ensName if we had one
|
||||||
}),
|
}),
|
||||||
@@ -305,10 +305,7 @@ async function lookupTokenInfo(contractAddress, rpcUrl, networkId) {
|
|||||||
name = await contract.name();
|
name = await contract.name();
|
||||||
log.debugf("name() =", name);
|
log.debugf("name() =", name);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warnf(
|
log.warnf("name() failed, using symbol as name:", e.message);
|
||||||
"name() failed, using symbol as name:",
|
|
||||||
e.shortMessage || e.message,
|
|
||||||
);
|
|
||||||
name = symbol;
|
name = symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-5
@@ -42,11 +42,7 @@ async function resolveEnsName(address, rpcUrl, networkId) {
|
|||||||
setCache(address, name);
|
setCache(address, name);
|
||||||
return name;
|
return name;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.errorf(
|
log.errorf("ENS reverse lookup failed", address, e.message);
|
||||||
"ENS reverse lookup failed",
|
|
||||||
address,
|
|
||||||
e.shortMessage || e.message,
|
|
||||||
);
|
|
||||||
// Don't cache failures — let subsequent lookups retry
|
// Don't cache failures — let subsequent lookups retry
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
// What reaches the console when the RPC endpoint answers with an HTTP error.
|
|
||||||
//
|
|
||||||
// RPC providers put the API key in the endpoint URL's path or query string.
|
|
||||||
// When the endpoint answers with an HTTP error (a wrong or expired key, a rate
|
|
||||||
// limit, a server error), the error ethers throws carries the full request URL
|
|
||||||
// in its message, so a line logging that message printed the key
|
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/410). Those lines log the
|
|
||||||
// error's short message, which names the HTTP status and not the URL.
|
|
||||||
//
|
|
||||||
// The real ethers provider runs; only its HTTP transport is replaced, by one
|
|
||||||
// that answers every request with 401 Unauthorized. Debug mode is on, so
|
|
||||||
// every log level is printed.
|
|
||||||
|
|
||||||
const { FetchRequest } = require("ethers");
|
|
||||||
const { getProvider, refreshBalances } = require("../src/shared/balances");
|
|
||||||
const { getFullWarnings } = require("../src/shared/addressWarnings");
|
|
||||||
const { resolveEnsName } = require("../src/shared/ens");
|
|
||||||
const { setRuntimeDebug } = require("../src/shared/log");
|
|
||||||
|
|
||||||
const RPC_URL = "https://rpc.example.invalid/v3/PATHKEY123?token=QUERYTOKEN456";
|
|
||||||
const ADDRESS = "0x1111111111111111111111111111111111111111";
|
|
||||||
|
|
||||||
const realFetch = globalThis.fetch;
|
|
||||||
let printed;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
setRuntimeDebug(true);
|
|
||||||
printed = [];
|
|
||||||
for (const method of ["log", "warn", "error"]) {
|
|
||||||
jest.spyOn(console, method).mockImplementation((...args) => {
|
|
||||||
printed.push(args.map(String).join(" "));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
FetchRequest.registerGetUrl(async () => ({
|
|
||||||
statusCode: 401,
|
|
||||||
statusMessage: "Unauthorized",
|
|
||||||
headers: {},
|
|
||||||
body: new Uint8Array(),
|
|
||||||
}));
|
|
||||||
// The explorer requests the balance refresh makes go nowhere.
|
|
||||||
globalThis.fetch = jest.fn(async () => {
|
|
||||||
throw new Error("tests must not perform network requests");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
FetchRequest.registerGetUrl(FetchRequest.createGetUrlFunc());
|
|
||||||
globalThis.fetch = realFetch;
|
|
||||||
setRuntimeDebug(false);
|
|
||||||
jest.restoreAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
// The line carrying `label` was printed and names the HTTP status, and nothing
|
|
||||||
// printed carries the key.
|
|
||||||
function expectFailureLoggedWithoutKey(label) {
|
|
||||||
const line = printed.find((text) => text.includes(label));
|
|
||||||
expect(line).toContain("401");
|
|
||||||
const all = printed.join("\n");
|
|
||||||
expect(all).not.toContain("PATHKEY123");
|
|
||||||
expect(all).not.toContain("QUERYTOKEN456");
|
|
||||||
}
|
|
||||||
|
|
||||||
test("ethers puts the URL in the error message, not in the short message", async () => {
|
|
||||||
const provider = getProvider(RPC_URL, "mainnet");
|
|
||||||
const error = await provider.getCode(ADDRESS).catch((e) => e);
|
|
||||||
expect(error.message).toContain("PATHKEY123");
|
|
||||||
expect(error.shortMessage).not.toContain("PATHKEY123");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("the recipient checks before a send", async () => {
|
|
||||||
await getFullWarnings(ADDRESS, getProvider(RPC_URL, "mainnet"));
|
|
||||||
expectFailureLoggedWithoutKey("contract check failed");
|
|
||||||
expectFailureLoggedWithoutKey("tx count check failed");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("the ENS reverse lookup", async () => {
|
|
||||||
expect(await resolveEnsName(ADDRESS, RPC_URL, "mainnet")).toBeNull();
|
|
||||||
expectFailureLoggedWithoutKey("ENS reverse lookup failed");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("the balance refresh", async () => {
|
|
||||||
const wallets = [{ addresses: [{ address: ADDRESS }] }];
|
|
||||||
await refreshBalances(
|
|
||||||
wallets,
|
|
||||||
RPC_URL,
|
|
||||||
"https://explorer.example.invalid/api/v2",
|
|
||||||
[],
|
|
||||||
"mainnet",
|
|
||||||
);
|
|
||||||
expectFailureLoggedWithoutKey("ETH balance failed");
|
|
||||||
expectFailureLoggedWithoutKey("ENS reverse failed");
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user