Repository navigation
fix: stop fish completion from re-running the typed dash-cli command - #7783
Conversation
|
🕓 Review not started yet because the new head is waiting for the 30-minute push debounce.
Commit 5339d4f. Normal review starts when eligible; priority review starts as soon as a slot is available. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review. WalkthroughThe Fish completion helper builds command-help arguments from the first command-line token and recognized connection options that appear before the first non-option argument. Option completion invokes Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The completion change avoids rerunning typed RPC commands while preserving supported connection options; no actionable merge risk is established. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change substantially reduces accidental command execution during completion. A limited uncertainty remains: removing explicit safety overrides could allow configuration-enabled stateful behavior during command-help lookup. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 2 only (queue backlog)
Static verification at head 6442f5c confirms that the completion no longer forwards the typed RPC or execution-changing options, addressing the unintended-command execution. One non-blocking compatibility regression remains: supported double-dash connection options are discarded. The supplied CI snapshot reports successful lint and several builds, with additional jobs pending and PR-title validation failing; no local builds or tests were run.
🟡 1 suggestion(s)
Review provenance
Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
lowbygpt-6.1-sol(effort low) — The diff is a small, contained fish-completion fix that filters arguments used for help queries without changing funds-movement implementation or other critical surfaces. - Phase 1 reviewers: not run (skipped for throughput: 22 PRs queued, above the 10 limit)
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `contrib/completions/fish/dash-cli.fish`:
- [SUGGESTION] contrib/completions/fish/dash-cli.fish:18: Preserve supported double-dash connection options
ArgsManager::ParseParameters explicitly normalizes `--foo` to `-foo` in src/util/system.cpp:279–281, so double-dash connection options are valid dash-cli input. This new allowlist silently drops those spellings. For example, completing `dash-cli --regtest --datadir=/path/to/regtest getblockc` now requests help using the default configuration and network instead of the selected regtest node; `--rpcwallet=w1` is likewise discarded rather than selecting that wallet's endpoint. This can return suggestions from the wrong node or no RPC suggestions when the default connection fails. Accept either dash spelling for the same allowlisted names; this preserves connection selection without forwarding the typed RPC or execution-changing options.
| set --local base $cmd[1] | ||
| for arg in $cmd[2..-1] | ||
| string match --quiet --regex -- '^-' $arg; or break | ||
| if string match --quiet --regex -- '^-(conf|datadir|testnet|regtest|devnet|chain|rpcconnect|rpcport|rpcuser|rpcpassword|rpccookiefile|rpcwallet|rpcclienttimeout)(=|$)' $arg |
There was a problem hiding this comment.
✅ Resolved at
ff98899d; see the replies below.
🟡 Suggestion: Preserve supported double-dash connection options
ArgsManager::ParseParameters explicitly normalizes --foo to -foo in src/util/system.cpp:279–281, so double-dash connection options are valid dash-cli input. This new allowlist silently drops those spellings. For example, completing dash-cli --regtest --datadir=/path/to/regtest getblockc now requests help using the default configuration and network instead of the selected regtest node; --rpcwallet=w1 is likewise discarded rather than selecting that wallet's endpoint. This can return suggestions from the wrong node or no RPC suggestions when the default connection fails. Accept either dash spelling for the same allowlisted names; this preserves connection selection without forwarding the typed RPC or execution-changing options.
| if string match --quiet --regex -- '^-(conf|datadir|testnet|regtest|devnet|chain|rpcconnect|rpcport|rpcuser|rpcpassword|rpccookiefile|rpcwallet|rpcclienttimeout)(=|$)' $arg | |
| if string match --quiet --regex -- '^--?(conf|datadir|testnet|regtest|devnet|chain|rpcconnect|rpcport|rpcuser|rpcpassword|rpccookiefile|rpcwallet|rpcclienttimeout)(=|$)' $arg |
source: gpt-6.1-sol (phase2-reviewer: general)
There was a problem hiding this comment.
Valid, thanks. Fixed in ff98899: the allowlist pattern is now ^--?(conf|datadir|...), so --regtest, --datadir=… and --rpcwallet=… are passed on just like their single-dash forms. Rechecked with fish: dash-cli --regtest --datadir=/tmp/x getblockc<Tab> now runs dash-cli --regtest --datadir=/tmp/x help, and --stdin is still dropped.
🤖 Posted autonomously by Claude on behalf of pasta.
There was a problem hiding this comment.
Resolved (re-reviewed at ff98899d): Confirmed your fix at ff98899: line 18 now uses ^--?, preserving both supported dash spellings for the allowlisted connection options, consistent with ArgsManager's normalization. The allowlist still excludes --stdin and execution-changing options, and the loop stops before the typed RPC and its arguments.
… command The dash-cli fish completion fetched the RPC command list by running the whole typed command line with `help` appended, and the option list by running it with `-help` appended. Everything after the first non-dash token is passed to the RPC as a parameter, so pressing Tab after a complete line such as `sendtoaddress <address> <amount>` executed it, several times per Tab, with "help" as the comment. A leading `-generate` likewise reserved a new address on each Tab. Fetch the command list using only the executable and the connection options typed before the RPC command, and fetch the option list from the bare executable, which does not depend on the rest of the line. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6442f5c to
ff98899
Compare
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
Independently reviewed the complete diff at ff98899 and the surrounding CLI parsing and RPC dispatch paths; no actionable in-scope defects were found. Completion now excludes the typed RPC and execution-changing CLI options, and the prior double-dash connection-option finding is fixed. Validation was static only: the supplied CI snapshot shows successful container builds and ClangFormat, with lint and the full-container manifest job still pending; the author's completion reproductions were not independently rerun.
🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)
Review provenance
Source: reviewer 1: glm-5.3-flash (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
lowbygpt-6.1-sol(effort low) — The diff is a small, contained fish-completion fix that filters arguments passed to help queries rather than changing funds-movement logic or another critical surface. - Phase 1 reviewers:
glm-5.3-flash— general (completed, effort high); agentphase1-reviewer - Phase 1 model:
glm-5.3-flash— zai quota: 5h 99% left, weekly 67% left; passed overgemini-3.8-flash-high(antigravity below 15% reserve: weekly 15% left, 5h 100% left) - Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort medium); agentphase2-reviewer,gpt-6.1-sol— dash-core-commit-history (completed, effort medium); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.
No unresolved findings remain from the prior review on this head.
…h completion Keep -conf, -datadir, the network selectors and every -rpc* option except -rpcwait*, the same prefix approach the dash-cli bash completion uses. This drops the per-option list, so new -rpc* connection options are kept without further edits, while -rpcwait* stays excluded so Tab does not block while the node is down. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PastaPastaPasta
left a comment
There was a problem hiding this comment.
approved; this is technically an upstream issue; but they rarely change these files so backport conflict isn't much concern; I'll try to upstream this, but idk if it'll have any movement; let's fix it now and then complete a backport later if we can.
…-cli command 5339d4f refactor(contrib): match dash-cli connection options by prefix in fish completion (pasta) ff98899 fix(contrib): stop fish completion from re-running the typed dash-cli command (pasta) Pull request description: ## Issue being fixed or feature implemented The fish completion for `dash-cli` (`contrib/completions/fish/dash-cli.fish`, imported from bitcoin#24611) builds its suggestions by running the command line the user has typed so far, with `help` or `-help` appended. Once an RPC command has been typed, everything after it is passed to the node as RPC parameters, so the appended `help` does not ask for help: it becomes one more parameter of the typed command, and the command runs. For anyone who has installed this completion, pressing Tab after a complete line such as `dash-cli sendtoaddress <address> 1 ` sends the coins before Enter is pressed, with `help` as the wallet comment. Each Tab runs the line several times, because every `complete` rule's `--condition` calls the helper again. Pressing Enter afterwards sends once more. The same applies to any RPC that accepts a trailing string parameter. A leading `-generate` reserves a new wallet address on each Tab. ### Why this is a real problem Code path at c14104b: - [`dash-cli.fish#L5`](https://github.com/dashpay/dash/blob/c14104b172a2/contrib/completions/fish/dash-cli.fish#L5) takes every completed token with `commandline -oc`. [L13-L16](https://github.com/dashpay/dash/blob/c14104b172a2/contrib/completions/fish/dash-cli.fish#L13-L16) strip only a bare `help` and `-stdin*`, and [L19](https://github.com/dashpay/dash/blob/c14104b172a2/contrib/completions/fish/dash-cli.fish#L19) runs `$cmd help`. [L63](https://github.com/dashpay/dash/blob/c14104b172a2/contrib/completions/fish/dash-cli.fish#L63) and [L65](https://github.com/dashpay/dash/blob/c14104b172a2/contrib/completions/fish/dash-cli.fish#L65) run `$cmd -help` the same way. - All three rules ([L75-L99](https://github.com/dashpay/dash/blob/c14104b172a2/contrib/completions/fish/dash-cli.fish#L75-L99)) call the helper from `--condition`, which fish evaluates on every Tab. - [`src/util/system.cpp#L262-L276`](https://github.com/dashpay/dash/blob/c14104b172a2/src/util/system.cpp#L262-L276): the first non-dash argument is the command. All later argv entries, including `help` and `-help`, are copied into the command arguments without being parsed. [`src/bitcoin-cli.cpp#L1207-L1212`](https://github.com/dashpay/dash/blob/c14104b172a2/src/bitcoin-cli.cpp#L1207-L1212) sends `args[0]` as the method. - [`src/wallet/rpc/spend.cpp#L238`](https://github.com/dashpay/dash/blob/c14104b172a2/src/wallet/rpc/spend.cpp#L238): the third `sendtoaddress` parameter is an optional string `comment`, so `help` is accepted and the send goes ahead. Reproduction against the unfixed tree. A fresh regtest `dashd` is built from this branch, wallet `w1` is funded, and `w2` provides the destination address. One Tab is simulated with fish 4.9.3 `complete -C '<line>'`, which evaluates the completion rules exactly as an interactive Tab does. Enter is never pressed. Script: `regtest-repro.sh` (included below). <details><summary>Unfixed: one Tab after a typed <code>sendtoaddress</code> line sends 5 DASH</summary> ``` $ git rev-parse --short HEAD c14104b $ regtest-repro.sh $PWD == completion output == Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 getblockc': getblockchaininfo getblockcount Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 sendtoa': sendtoaddress "address" amount ( "comment" "comment_to" subt Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 -rpcw': -rpcwait -rpcwaittimeout= -rpcwallet= Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 help getblockc': getblockchaininfo getblockcount == sendtoaddress == w1 txs before Tab: 0 sends w2 balance before Tab (minconf 0): 0.00000000 $ fish -c "source dash-cli.fish; complete -C 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 sendtoaddress yYY9kJyUeiJJRU9nAEsx5Nu2wXZEjK4CnD 1 '" # one Tab press, Enter never pressed w1 txs after Tab: 5 sends w2 balance after Tab (minconf 0): 5.00000000 mempool size: "size": 5, comments on the w1 sends: 2 "comment": "-help", 3 "comment": "help", == -generate == w1 before Tab: "keypoolsize": 1000,, blocks: 110 $ fish -c "source dash-cli.fish; complete -C 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 -generate '" # one Tab press, Enter never pressed w1 after Tab: "keypoolsize": 999,, blocks: 110 ``` </details> To show exactly what the completion executes, the same line was also completed with a stand-in `dash-cli` on `PATH` that only logs its arguments: <details><summary>Unfixed: commands run by the completion for one Tab</summary> ``` $ git rev-parse --short HEAD c14104b $ stub-repro.sh $PWD ### Tab after: 'dash-cli -regtest ' ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest -help ran: dash-cli -regtest -help ### Tab after: 'dash-cli -regtest -rpcwallet=w1 sendtoaddress yNmxLr8sgqWjj8qEcvH7bD3b4G4Bu4PZpq 1 ' ran: dash-cli -regtest -rpcwallet=w1 sendtoaddress yNmxLr8sgqWjj8qEcvH7bD3b4G4Bu4PZpq 1 help ran: dash-cli -regtest -rpcwallet=w1 sendtoaddress yNmxLr8sgqWjj8qEcvH7bD3b4G4Bu4PZpq 1 help ### Tab after: 'dash-cli -regtest -generate ' ran: dash-cli -regtest -generate help ran: dash-cli -regtest -generate help ran: dash-cli -regtest -generate help ran: dash-cli -regtest -generate -help ran: dash-cli -regtest -generate -help ### Tab after: 'dash-cli -regtest -stdin ' ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest -stdin -help ran: dash-cli -regtest -stdin -help ``` </details> <details><summary>regtest-repro.sh</summary> ```sh #!/bin/sh # Usage: regtest-repro.sh <worktree> # Starts a fresh regtest dashd from <worktree>/src, funds wallet w1, then asks # fish for completions (exactly what one Tab press does) of a fully typed but # not yet submitted `sendtoaddress` line and of `-generate `, and reports what # changed on the node. Enter is never pressed. WT=$1 P=$(cd "$(dirname "$0")" && pwd) FISH=fish D=$P/regtest-datadir rm -rf "$D"; mkdir -p "$D" export PATH=$WT/src:$PATH CLI="dash-cli -regtest -datadir=$D -rpcport=19876" "$WT/src/dashd" -regtest -datadir="$D" -rpcport=19876 -port=19877 -listen=0 -fallbackfee=0.0001 -daemon >/dev/null $CLI -rpcwait getblockcount >/dev/null $CLI createwallet w1 >/dev/null $CLI createwallet w2 >/dev/null $CLI -rpcwallet=w1 generatetoaddress 110 "$($CLI -rpcwallet=w1 getnewaddress)" >/dev/null ADDR=$($CLI -rpcwallet=w2 getnewaddress) tab() { echo "\$ fish -c \"source dash-cli.fish; complete -C '$1'\" # one Tab press, Enter never pressed" "$FISH" --no-config -c "source $WT/contrib/completions/fish/dash-cli.fish; complete -C '$1' >/dev/null" } echo "== completion output ==" for l in "$CLI getblockc" "$CLI -rpcwallet=w1 sendtoa" "$CLI -rpcwallet=w1 -rpcw" "$CLI help getblockc"; do echo "Tab after '$(echo "$l" | sed "s|$D|DATADIR|")':" "$FISH" --no-config -c "source $WT/contrib/completions/fish/dash-cli.fish; complete -C '$l'" | cut -c1-60 | sed 's/^/ /' done echo "== sendtoaddress ==" echo "w1 txs before Tab: $($CLI -rpcwallet=w1 listtransactions '*' 1000 | grep -c '"category": "send"') sends" echo "w2 balance before Tab (minconf 0): $($CLI -rpcwallet=w2 getreceivedbyaddress $ADDR 0)" tab "$CLI -rpcwallet=w1 sendtoaddress $ADDR 1 " echo "w1 txs after Tab: $($CLI -rpcwallet=w1 listtransactions '*' 1000 | grep -c '"category": "send"') sends" echo "w2 balance after Tab (minconf 0): $($CLI -rpcwallet=w2 getreceivedbyaddress $ADDR 0)" echo "mempool size: $($CLI getmempoolinfo | grep '"size"')" echo "comments on the w1 sends:" $CLI -rpcwallet=w1 listtransactions '*' 1000 | grep '"comment"' | sort | uniq -c echo "== -generate ==" echo "w1 before Tab: $($CLI -rpcwallet=w1 getwalletinfo | grep '"keypoolsize"'), blocks: $($CLI getblockcount)" tab "$CLI -rpcwallet=w1 -generate " echo "w1 after Tab: $($CLI -rpcwallet=w1 getwalletinfo | grep '"keypoolsize"'), blocks: $($CLI getblockcount)" $CLI stop >/dev/null sleep 3 ``` </details> ### Why it matters Pressing Tab is expected to be harmless. With this completion installed, Tab can broadcast a payment the user has not confirmed. It does so several times per keypress, so a later Enter produces a duplicate payment. Any other state-changing RPC that tolerates the extra parameter runs early in the same way. The scope is limited: the completion is opt-in and not installed by the build, it needs working RPC credentials and an unlocked wallet, and the user must already have typed the full command. Nothing is reachable remotely. The effect is still an unintended spend of the user's own funds. ## What was done? Only `contrib/completions/fish/dash-cli.fish` changes. The first commit is the fix; the second replaces the explicit option list with prefix matching. - `__fish_dash_cli_get_commands_helper` now runs `help` with only the executable and the connection options typed before the RPC command, matched by prefix: `-conf`, `-datadir`, `-testnet`, `-regtest`, `-devnet`, `-chain`, and every `-rpc*` option except `-rpcwait*` (which would make Tab hang while the node is down). This is the same prefix approach as the dash-cli bash completion, so new `-rpc*` connection options need no edits here. It stops at the first non-dash token, so the RPC command and its arguments are never passed on. Other leading options are dropped, including `-generate`, `-getinfo`, `-netinfo`, `-addrinfo`, `-named`, `-rpcwait*` and `-stdin*`. This replaces the old ad hoc stripping of `help` and `-stdin*`, which the allowlist covers. - `__fish_dash_cli_get_options` runs `-help` on the bare executable (`$cmd[1]`). The option list does not depend on the rest of the line, and the other Dash completion scripts (`dash-tx.fish`, `dash-wallet.fish`, `dash-util.fish`, `dashd.fish`) already do this. ### Why this is the correct minimal fix The root cause is that the completion re-executes user input. The fix stops passing that input on, and keeps only what is needed to reach the same node and wallet (datadir, conf, network, RPC endpoint and credentials, wallet). An allowlist is used because a denylist would have to keep up with every dash-cli option that changes what gets executed. Some leading options are themselves commands: `-generate` calls `getnewaddress` before it validates its arguments. Stripping only `help` would not help, because any typed RPC still runs. Excluding specific RPCs would always miss some. Left unchanged on purpose: the completion rules, the output parsing, and the `-help`/`-?` early returns. The helper is still called once per rule on each Tab, which now only issues the read-only `help` RPC. Caching it, or adding a default client timeout, would be separate improvements. Bitcoin Core's `bitcoin-cli.fish` has the same pattern. ## How Has This Been Tested? No automated test was added. The repository has no harness for shell completion scripts, and the behaviour was checked end to end against a real node instead. Environment: macOS arm64, `dashd`/`dash-cli` built from this branch, fish 4.9.3. - `regtest-repro.sh` before the fix: one Tab after `sendtoaddress <w2 address> 1 ` produced 5 sends from `w1` (5 DASH received by `w2`, 5 transactions in the mempool, comments `help` ×3 and `-help` ×2). The `-generate ` Tab reduced the keypool from 1000 to 999. Output is above. - `regtest-repro.sh` after the fix: no sends, `w2` balance 0, empty mempool, keypool unchanged. Command, `help <command>` and option completion return the same suggestions as before. <details><summary>Fixed: same script</summary> ``` $ git diff --stat contrib/completions/fish/dash-cli.fish | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) $ regtest-repro.sh $PWD == completion output == Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 getblockc': getblockchaininfo getblockcount Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 sendtoa': sendtoaddress "address" amount ( "comment" "comment_to" subt Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 -rpcw': -rpcwait -rpcwaittimeout= -rpcwallet= Tab after 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 help getblockc': getblockchaininfo getblockcount == sendtoaddress == w1 txs before Tab: 0 sends w2 balance before Tab (minconf 0): 0.00000000 $ fish -c "source dash-cli.fish; complete -C 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 sendtoaddress ya4Gcphm7nFvTRuHjLqnmqh6W6tkhjmvfL 1 '" # one Tab press, Enter never pressed w1 txs after Tab: 0 sends w2 balance after Tab (minconf 0): 0.00000000 mempool size: "size": 0, comments on the w1 sends: == -generate == w1 before Tab: "keypoolsize": 1000,, blocks: 110 $ fish -c "source dash-cli.fish; complete -C 'dash-cli -regtest -datadir=DATADIR -rpcport=19876 -rpcwallet=w1 -generate '" # one Tab press, Enter never pressed w1 after Tab: "keypoolsize": 1000,, blocks: 110 ``` </details> <details><summary>Fixed: commands run by the completion for one Tab (logging stand-in)</summary> ``` $ git diff --stat contrib/completions/fish/dash-cli.fish | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) $ stub-repro.sh $PWD ### Tab after: 'dash-cli -regtest ' ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -help ran: dash-cli -help ### Tab after: 'dash-cli -regtest -rpcwallet=w1 sendtoaddress yNmxLr8sgqWjj8qEcvH7bD3b4G4Bu4PZpq 1 ' ran: dash-cli -regtest -rpcwallet=w1 help ran: dash-cli -regtest -rpcwallet=w1 help ### Tab after: 'dash-cli -regtest -generate ' ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -help ran: dash-cli -help ### Tab after: 'dash-cli -regtest -stdin ' ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -regtest help ran: dash-cli -help ran: dash-cli -help ``` </details> - After switching to prefix matching (second commit), the regtest check was repeated: command, `help <command>`, option and `-rpcwait` line completions return the expected suggestions; Tab after the `sendtoaddress` line and after `-generate ` leaves w1 with 0 sends, keypool 1000 and an empty mempool; with the node stopped, Tab after `-rpcwait getblockc` returns immediately. A logging stand-in confirmed that every connection option (`-conf`, `-datadir`, `-testnet`, `-regtest`, `-devnet`, `-chain`, `-rpcconnect`, `-rpcport`, `-rpcuser`, `-rpcpassword`, `-rpccookiefile`, `-rpcwallet`, `-rpcclienttimeout`) is passed on and `-rpcwait`, `-rpcwaittimeout`, `-stdin*`, `-named`, `-generate`, `-getinfo`, `-netinfo` are not. - `fish --no-execute contrib/completions/fish/dash-cli.fish` succeeds (syntax check). `fish_indent` reports no changes in the edited hunks. - `test/lint/lint-whitespace.py` passes. ## Breaking Changes None. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone 🤖 Generated with [Claude Code](https://claude.com/claude-code) Top commit has no ACKs. Tree-SHA512: 233c7ab06389315b9ca8aed82b36438b55f482e6fc167c4cecaaba135ff0363d84c1b593904bfdb0ac529c2f2b1722f29eeb12c6616d9da75bb1c1934adce9e9 (cherry picked from commit 3789ec0)
fbe6526 Merge #7807: feat(consensus): hold EvoNode shares and multiple payouts behind a new evo_shares deployment (pasta) 57fd2d9 Merge #7776: fix: keep a pending ProUpServTx across a registrar update that keeps the operator key (pasta) ca69801 Merge #7803: fix: return non-zero amount of accounts after sethdseed (pasta) 57a8f07 Merge #7796: backport: bitcoin#26720 (remaining), partial bitcoin#29523: weight-check the knapsack exact-match selection (pasta) 4eb82f6 Merge #7786: fix(net): keep a dedicated onion listener when -bind is given and correct the bind release note (pasta) 70626a0 Merge #7793: backport: partial bitcoin#28868 (wallet: reload wallet if migration exited early, keep mixed watchonly txs) (pasta) 250a3cd Merge #7790: fix: guard cached governance object flags with the object lock (pasta) 700c195 Merge #7787: fix: only sign EHF signals inside the deployment's start/timeout window (pasta) a3b0f3c Merge #7785: fix: restore the txindex requirement when governance validation is enabled (pasta) 2d1735d Merge #7794: fix(wallet): floor the denomination gap threshold so rebalancing cannot oscillate (pasta) d9f6592 Merge #7783: fix: stop fish completion from re-running the typed dash-cli command (pasta) 89d44c8 Merge #7795: backport: bitcoin#34272, partial bitcoin#31650 (pass PSBT by const reference in PSBTInputSignedAndVerified) (pasta) a1d7411 Merge #7779: fix!: report legacy evonode Platform addresses when listdiff changes its address (pasta) 73f6ea2 Merge #7789: fix(qt): report malformed lang/font settings instead of aborting at startup (pasta) 40de493 Merge #7781: fix(rpc): use a consistent tip height in quorum dkgstatus (pasta) 7d1a90d Merge #7791: fix(net): request object votes from nPeersPerHashMax peers (pasta) 51b145c Merge #7778: test: use single-member in functional tests: feature_mnehf, feature_notifications, p2p_instantsend (pasta) Pull request description: ## Issue being fixed or feature implemented Backports for v24.0.0-rc.3. `v24.0.x` was cut at the v24.0.0-rc.2 tag (1e239d4), and develop has since moved to 24.1.0 (#7775), so rc.3 is built from this branch instead of being tagged on develop like rc.1 and rc.2. ## What was done? Cherry-picked (`git cherry-pick -m1 -x`) every develop merge since rc.2 that carries `backport-candidate-24.0.x`, plus #7778, which #7807's test needs. They are in develop merge order: - #7778 test: use single-member in functional tests: feature_mnehf, feature_notifications, p2p_instantsend (test-only. Needed because #7807's `feature_evo_shares_activation.py` depends on its `mine_quorum_single_member()` fix and fails without it.) - #7791 fix(net): request object votes from nPeersPerHashMax peers - #7781 fix(rpc): use a consistent tip height in quorum dkgstatus - #7789 fix(qt): report malformed lang/font settings instead of aborting at startup - #7779 fix!: report legacy evonode Platform addresses when listdiff changes its address - #7795 backport: bitcoin#34272, partial bitcoin#31650 (pass PSBT by const reference in PSBTInputSignedAndVerified) - #7783 fix: stop fish completion from re-running the typed dash-cli command - #7794 fix(wallet): floor the denomination gap threshold so rebalancing cannot oscillate - #7785 fix: restore the txindex requirement when governance validation is enabled - #7787 fix: only sign EHF signals inside the deployment's start/timeout window - #7790 fix: guard cached governance object flags with the object lock - #7793 backport: partial bitcoin#28868 (wallet: reload wallet if migration exited early, keep mixed watchonly txs) - #7786 fix(net): keep a dedicated onion listener when -bind is given and correct the bind release note. Includes partial bitcoin#36170 (5f40d56). Upstream's dedicated-onion-bind changes to `feature_torcontrol.py` and `p2p_private_broadcast.py` are not included, because those tests depend on upstream commits (5693833, e74d54e) that Dash does not have. - #7796 backport: bitcoin#26720 (remaining), partial bitcoin#29523: weight-check the knapsack exact-match selection - #7803 fix: return non-zero amount of accounts after sethdseed - #7776 fix: keep a pending ProUpServTx across a registrar update that keeps the operator key - #7807 feat(consensus): hold EvoNode shares and multiple payouts behind a new evo_shares deployment One pick needed conflict resolution, in a test only, described in its commit message: #7793, `test/functional/wallet_migration.py`. `test_conflict_txs()` comes from bitcoin#28542 (#7762), which is not on this branch, so it is left out. The C++ part of #7793 is identical to develop. Merged since rc.2 but intentionally not included: #7775 (24.1 version bump), #7260 and #7804 (new Qt feature and its CI fix), #7584 (refactor), routine upstream backports #7257, #7759, #7762, #7768, #7788, #7801, and test/CI-only changes #7777 and #7797. No version change is needed. `configure.ac` is already 24.0.0 with `CLIENT_VERSION_IS_RELEASE` false, so the build takes its version string from the `v24.0.0-rc.3` tag, as it did for rc.2. ## How Has This Been Tested? Built on macOS arm64 against depends with `--enable-debug --enable-werror` (dashd, dash-qt, tests). - `make check`: all unit test suites pass, including `test_dash-qt`. - Functional tests, all passing: `feature_evo_shares_activation.py`, `feature_masternode_shares.py`, `feature_mnehf.py`, `feature_governance.py --descriptors`, `feature_governance_objects.py`, `feature_governance_txindex_devnet.py`, `feature_proxy.py`, `feature_notifications.py`, `feature_llmq_connections.py`, `feature_llmq_chainlocks_automatic.py`, `feature_llmq_singlenode.py`, `p2p_instantsend.py`, `rpc_blockchain.py` (v1 and v2 transport), `rpc_netinfo.py`, `rpc_quorum.py`, `rpc_verifychainlock.py`, `wallet_hd.py` (legacy and descriptors), `wallet_migration.py`. - `feature_bind_extra.py` (from #7786) only runs on Linux and was skipped locally, so CI covers it. Every touched file was diffed against develop. The remaining differences come only from develop PRs that are not part of this backport. ## Breaking Changes Carried over from the picked PRs: - #7807: consensus change behind the new `evo_shares` deployment (bit 14). Mainnet and testnet are unaffected until it activates. Devnets with `v24` already active and an EvoNode carrying several owner payouts need to check or reset. New RPC `protx shared_register_prepare_evo`. - #7785: outside regtest, a node with `-txindex=0` and without `-disablegovernance` refuses to start again, as before v23.1.0. - #7786: carries a partial bitcoin#36170. A node started with `-bind` but no specific `-bind=<addr:port>=onion` now refuses to start while `-listenonion` is enabled. That is the default when listening, even without Tor configured. A wildcard onion bind is also refused. Such nodes need `-bind=127.0.0.1:<port>=onion` or `-listenonion=0`. Nodes without `-bind`, including those using only `-whitebind`, keep the default `127.0.0.1:9996` (`19996` testnet) onion target. See `doc/release-notes-7300.md`. - #7779: `protx listdiff` now includes `platform_p2p` and `platform_https` for a legacy-address evonode whose core P2P address changes. ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone 🤖 Generated with [Claude Code](https://claude.com/claude-code) Top commit has no ACKs. Tree-SHA512: 8cb01bdb0234f31b5dd2c105b69673de148922991a7523627395c0e0ccfa22b94be5ffbecc842398d443bc90fd9baf2eee6a9cf6fcdbbd1626eb00b93726a55a
Issue being fixed or feature implemented
The fish completion for
dash-cli(contrib/completions/fish/dash-cli.fish, imported from bitcoin#24611) builds its suggestions by running the command line the user has typed so far, withhelpor-helpappended. Once an RPC command has been typed, everything after it is passed to the node as RPC parameters, so the appendedhelpdoes not ask for help: it becomes one more parameter of the typed command, and the command runs.For anyone who has installed this completion, pressing Tab after a complete line such as
dash-cli sendtoaddress <address> 1sends the coins before Enter is pressed, withhelpas the wallet comment. Each Tab runs the line several times, because everycompleterule's--conditioncalls the helper again. Pressing Enter afterwards sends once more. The same applies to any RPC that accepts a trailing string parameter. A leading-generatereserves a new wallet address on each Tab.Why this is a real problem
Code path at c14104b:
dash-cli.fish#L5takes every completed token withcommandline -oc. L13-L16 strip only a barehelpand-stdin*, and L19 runs$cmd help. L63 and L65 run$cmd -helpthe same way.--condition, which fish evaluates on every Tab.src/util/system.cpp#L262-L276: the first non-dash argument is the command. All later argv entries, includinghelpand-help, are copied into the command arguments without being parsed.src/bitcoin-cli.cpp#L1207-L1212sendsargs[0]as the method.src/wallet/rpc/spend.cpp#L238: the thirdsendtoaddressparameter is an optional stringcomment, sohelpis accepted and the send goes ahead.Reproduction against the unfixed tree. A fresh regtest
dashdis built from this branch, walletw1is funded, andw2provides the destination address. One Tab is simulated with fish 4.9.3complete -C '<line>', which evaluates the completion rules exactly as an interactive Tab does. Enter is never pressed. Script:regtest-repro.sh(included below).Unfixed: one Tab after a typed
sendtoaddressline sends 5 DASHTo show exactly what the completion executes, the same line was also completed with a stand-in
dash-clionPATHthat only logs its arguments:Unfixed: commands run by the completion for one Tab
regtest-repro.sh
Why it matters
Pressing Tab is expected to be harmless. With this completion installed, Tab can broadcast a payment the user has not confirmed. It does so several times per keypress, so a later Enter produces a duplicate payment. Any other state-changing RPC that tolerates the extra parameter runs early in the same way. The scope is limited: the completion is opt-in and not installed by the build, it needs working RPC credentials and an unlocked wallet, and the user must already have typed the full command. Nothing is reachable remotely. The effect is still an unintended spend of the user's own funds.
What was done?
Only
contrib/completions/fish/dash-cli.fishchanges. The first commit is the fix; the second replaces the explicit option list with prefix matching.__fish_dash_cli_get_commands_helpernow runshelpwith only the executable and the connection options typed before the RPC command, matched by prefix:-conf,-datadir,-testnet,-regtest,-devnet,-chain, and every-rpc*option except-rpcwait*(which would make Tab hang while the node is down). This is the same prefix approach as the dash-cli bash completion, so new-rpc*connection options need no edits here. It stops at the first non-dash token, so the RPC command and its arguments are never passed on. Other leading options are dropped, including-generate,-getinfo,-netinfo,-addrinfo,-named,-rpcwait*and-stdin*. This replaces the old ad hoc stripping ofhelpand-stdin*, which the allowlist covers.__fish_dash_cli_get_optionsruns-helpon the bare executable ($cmd[1]). The option list does not depend on the rest of the line, and the other Dash completion scripts (dash-tx.fish,dash-wallet.fish,dash-util.fish,dashd.fish) already do this.Why this is the correct minimal fix
The root cause is that the completion re-executes user input. The fix stops passing that input on, and keeps only what is needed to reach the same node and wallet (datadir, conf, network, RPC endpoint and credentials, wallet). An allowlist is used because a denylist would have to keep up with every dash-cli option that changes what gets executed. Some leading options are themselves commands:
-generatecallsgetnewaddressbefore it validates its arguments. Stripping onlyhelpwould not help, because any typed RPC still runs. Excluding specific RPCs would always miss some.Left unchanged on purpose: the completion rules, the output parsing, and the
-help/-?early returns. The helper is still called once per rule on each Tab, which now only issues the read-onlyhelpRPC. Caching it, or adding a default client timeout, would be separate improvements. Bitcoin Core'sbitcoin-cli.fishhas the same pattern.How Has This Been Tested?
No automated test was added. The repository has no harness for shell completion scripts, and the behaviour was checked end to end against a real node instead. Environment: macOS arm64,
dashd/dash-clibuilt from this branch, fish 4.9.3.regtest-repro.shbefore the fix: one Tab aftersendtoaddress <w2 address> 1produced 5 sends fromw1(5 DASH received byw2, 5 transactions in the mempool, commentshelp×3 and-help×2). The-generateTab reduced the keypool from 1000 to 999. Output is above.regtest-repro.shafter the fix: no sends,w2balance 0, empty mempool, keypool unchanged. Command,help <command>and option completion return the same suggestions as before.Fixed: same script
Fixed: commands run by the completion for one Tab (logging stand-in)
help <command>, option and-rpcwaitline completions return the expected suggestions; Tab after thesendtoaddressline and after-generateleaves w1 with 0 sends, keypool 1000 and an empty mempool; with the node stopped, Tab after-rpcwait getblockcreturns immediately. A logging stand-in confirmed that every connection option (-conf,-datadir,-testnet,-regtest,-devnet,-chain,-rpcconnect,-rpcport,-rpcuser,-rpcpassword,-rpccookiefile,-rpcwallet,-rpcclienttimeout) is passed on and-rpcwait,-rpcwaittimeout,-stdin*,-named,-generate,-getinfo,-netinfoare not.fish --no-execute contrib/completions/fish/dash-cli.fishsucceeds (syntax check).fish_indentreports no changes in the edited hunks.test/lint/lint-whitespace.pypasses.Breaking Changes
None.
Checklist:
🤖 Generated with Claude Code