harden: debug mode logs only a request's origin and JSON-RPC method (closes #410)
check / check (push) Failing after 2s
e2e / e2e-chrome (push) Failing after 3s
e2e / e2e-firefox (push) Failing after 3s

With debug mode on, debugFetch logged every request's full URL and body,
so an RPC endpoint with an API key in its path or query string printed
that key to the console on every request. It now logs the HTTP method,
the URL's origin and, for a JSON-RPC body, the method name. The balance
refresh and token lookup, which logged the RPC URL at debug level, log
its origin too. The README's DEBUG Mode Policy says what debug mode logs.

Model: opus-5-5
This commit is contained in:
2026-10-04 17:50:47 +00:00
parent d1751beb32
commit b261bafb03
7 changed files with 91 additions and 8 deletions
+3 -3
View File
@@ -10,7 +10,7 @@ const {
} = require("ethers");
const { ERC20_ABI } = require("./constants");
const { NETWORKS } = require("./networks");
const { log, debugFetch } = require("./log");
const { log, debugFetch, urlOrigin } = require("./log");
const { deriveAddressFromXpub } = require("./wallet");
const { TOKEN_BY_ADDRESS } = require("./tokenList");
const { LOW_HOLDER_THRESHOLD, parseHoldersCount } = require("./holders");
@@ -203,7 +203,7 @@ async function refreshBalances(
trackedTokens,
networkId,
) {
log.debugf("refreshBalances start, rpc:", rpcUrl);
log.debugf("refreshBalances start, rpc:", urlOrigin(rpcUrl));
const provider = getProvider(rpcUrl, networkId);
const updates = [];
@@ -280,7 +280,7 @@ async function refreshBalances(
// Look up token metadata from its contract.
// Calls symbol() and decimals() to verify it implements ERC-20.
async function lookupTokenInfo(contractAddress, rpcUrl, networkId) {
log.debugf("lookupTokenInfo", contractAddress, "rpc:", rpcUrl);
log.debugf("lookupTokenInfo", contractAddress, "rpc:", urlOrigin(rpcUrl));
const provider = getProvider(rpcUrl, networkId);
const contract = new Contract(contractAddress, ERC20_ABI, provider);
+24 -5
View File
@@ -42,14 +42,33 @@ const log = {
},
};
// Fetch wrapper that debug-logs every request and response.
// The origin (scheme, host and port) of a URL, for logging in place of the
// URL: RPC providers put API keys in the path or the query string. A URL that
// does not parse gives "", so logging never stops a request.
function urlOrigin(url) {
try {
return new URL(url).origin;
} catch {
return "";
}
}
// Fetch wrapper that debug-logs every request and response. It logs the
// URL's origin and, for a JSON-RPC body, the method name: never the full URL
// or body, which can carry an API key or a signed transaction.
async function debugFetch(url, opts) {
const method = (opts && opts.method) || "GET";
const body = opts && opts.body;
log.debugf("fetch →", method, url, body || "");
const origin = urlOrigin(url);
let rpcMethod = "";
try {
rpcMethod = JSON.parse(opts.body).method || "";
} catch {
// no body, or a body that is not JSON
}
log.debugf("fetch →", method, origin, rpcMethod);
const resp = await fetch(url, opts);
log.debugf("fetch ←", resp.status, url);
log.debugf("fetch ←", resp.status, origin);
return resp;
}
module.exports = { log, debugFetch, setRuntimeDebug, isDebug };
module.exports = { log, debugFetch, urlOrigin, setRuntimeDebug, isDebug };