xpay: handle pay ignoring unknown arguments
What changed, and why it matters
This change makes the 'xpay' payment plugin more willing to take over payments that were originally sent to the older 'pay' command. Previously, if 'pay' received an argument that xpay did not understand, xpay would refuse to handle the payment and the older 'pay' logic would run instead. Now xpay logs that it does not recognize the argument, ignores it, and still redirects the payment through xpay. This is a behavior change, not a clear-cut security fix, and could in theory cause payments to be processed with options that xpay silently drops.
Review whether any legacy 'pay' arguments that are now ignored by xpay affect payment safety, fees, routing, or metadata. If the intent is purely compatibility, document which arguments are intentionally ignored and ensure users are not surprised by changed behavior. No immediate emergency action is indicated by the diff alone.
Security signals we found
Behavior change in RPC command interception: unknown arguments are now silently ignored instead of causing fallback to legacy 'pay'.
Potential silent dropping of payment options that the legacy 'pay' command would have honored differently.
No input validation, memory safety, or cryptographic change is visible in the diff.
Evidence from the diff
In plugins/xpay/xpay.c, the handle_rpc_command hook that intercepts ‘pay’ RPC calls used to bail out (‘goto dont_redirect’) when it encountered any JSON argument other than ‘bolt11’, ‘amount_msat’, or ‘maxdelay’. The patch removes that bailout, so xpay now redirects the call to itself even when unknown arguments are present. The log message changes from ‘Not redirecting pay (unknown arg …)’ to ‘Unknown arg …, xpay will ignore it.’ The test file is updated to expect redirection instead of non-redirection for ‘label’ and ‘riskfactor’ arguments.
Changed components
plugins/xpay/xpay.ctests/test_xpay.pyInspect captured patch +6 / −5
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index 955f098d..e05e2df3 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -2457,10 +2457,9 @@ static struct command_result *handle_rpc_command(struct command *cmd,
maxdelay = t + 1;
else {
plugin_log(cmd->plugin, LOG_INFORM,
- "Not redirecting pay (unknown arg %.*s)",
+ "Unknown arg %.*s, xpay will ignore it.",
json_tok_full_len(t),
json_tok_full(buf, t));
- goto dont_redirect;
}
}
} else {
diff --git a/tests/test_xpay.py b/tests/test_xpay.py
index 90d191b1..c5ad9160 100644
--- a/tests/test_xpay.py
+++ b/tests/test_xpay.py
@@ -450,14 +450,16 @@ def test_xpay_takeover(node_factory, executor):
inv, "10000", 'label'])
l1.daemon.wait_for_log(r'Not redirecting pay \(only handle 1 or 2 args\): ')
- # Other args fail.
+ # Other args are ignored.
inv = l3.rpc.invoice('any', "test_xpay_takeover7", "test_xpay_takeover7")
l1.rpc.pay(inv['bolt11'], amount_msat=10000, label='test_xpay_takeover7')
- l1.daemon.wait_for_log(r'Not redirecting pay \(unknown arg "label"\)')
+ l1.daemon.wait_for_log('Unknown arg "label", xpay will ignore it.')
+ l1.daemon.wait_for_log('Redirecting pay->xpay')
inv = l3.rpc.invoice('any', "test_xpay_takeover8", "test_xpay_takeover8")
l1.rpc.pay(inv['bolt11'], amount_msat=10000, riskfactor=1)
- l1.daemon.wait_for_log(r'Not redirecting pay \(unknown arg "riskfactor"\)')
+ l1.daemon.wait_for_log('Unknown arg "riskfactor", xpay will ignore it.')
+ l1.daemon.wait_for_log('Redirecting pay->xpay')
# Test that it's really dynamic.
l1.rpc.setconfig('xpay-handle-pay', False)
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.