Skip to content

wallet: Disallow wallet names that are paths including .. and . elements#34544

Open
achow101 wants to merge 2 commits intobitcoin:masterfrom
achow101:wallet-no-relative-paths
Open

wallet: Disallow wallet names that are paths including .. and . elements#34544
achow101 wants to merge 2 commits intobitcoin:masterfrom
achow101:wallet-no-relative-paths

Conversation

@achow101
Copy link
Member

@achow101 achow101 commented Feb 9, 2026

Wallet names including .. and . are unintuitive and can lead to various issues, see #34497

This disallows creating or loading wallets that have any path elements that are .. or ., including any present in an absolute path. This does not disallow relative paths altogether but rather limits them to only being subdirectories of the wallets directory.

@DrahtBot DrahtBot added the Wallet label Feb 9, 2026
@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 9, 2026

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept ACK davidgumberg, arejula27
Approach ACK w0xlt

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #bitcoin-core/gui/911 (Adds non-mempool wallet balance to overview by ajtowns)
  • #34603 (wallet: Fix detection of symlinks on Windows by achow101)
  • #34418 (qa: Make wallet_multiwallet.py Windows crossbuild-compatible by hodlinator)
  • #34372 (QA: wallet_migration: Test several more weird scenarios by luke-jr)
  • #33671 (wallet: Add separate balance info for non-mempool wallet txs by ajtowns)
  • #26022 (Add util::ResultPtr class by ryanofsky)
  • #25665 (refactor: Add util::Result failure types and ability to merge result values by ryanofsky)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@davidgumberg
Copy link
Contributor

Concept ACK

@fanquake
Copy link
Member

https://github.com/bitcoin/bitcoin/actions/runs/21844979703/job/63075106819?pr=34544#step:12:15955:

 test  2026-02-10T08:24:08.355405Z TestFramework (ERROR): Unexpected exception 
                                   Traceback (most recent call last):
                                     File "D:\a\bitcoin\bitcoin\test\functional\test_framework\test_framework.py", line 142, in main
                                       self.run_test()
                                       ~~~~~~~~~~~~~^^
                                     File "D:\a\bitcoin\bitcoin/test/functional/wallet_migration.py", line 1678, in run_test
                                       self.test_wallet_with_relative_path()
                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                                     File "D:\a\bitcoin\bitcoin/test/functional/wallet_migration.py", line 621, in test_wallet_with_relative_path
                                       migrate_res, wallet = self.migrate_and_get_rpc(relative_name)
                                                             ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
                                     File "D:\a\bitcoin\bitcoin/test/functional/wallet_migration.py", line 139, in migrate_and_get_rpc
                                       migrate_info = self.master_node.migratewallet(wallet_name=wallet_name, **kwargs)
                                     File "D:\a\bitcoin\bitcoin\test\functional\test_framework\coverage.py", line 50, in __call__
                                       return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
                                     File "D:\a\bitcoin\bitcoin\test\functional\test_framework\authproxy.py", line 156, in __call__
                                       raise JSONRPCException(response['error'], status)
                                   test_framework.authproxy.JSONRPCException: Wallet name given as a relative path cannot begin with .. or . (-4)
 test  2026-02-10T08:24:08.357776Z TestFramework (DEBUG): Closing down network thread 

Copy link

@arejula27 arejula27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK [7c993c6]

the funtional test wallet_migration.py fails cuz the test specifically tries to migrate from a relative path using .., see line 621:

wallet_name = "relative"
absolute_path = os.path.abspath(
            os.path.join(common_parent, wallet_name))
# /tmp/bitcoin_func_test_rriidks3/relative
relative_name = os.path.relpath(
            absolute_path, start=self.master_node.wallets_path)
# ../../../relative

wallet = self.create_legacy_wallet(relative_name)

This is done because the nodes use different wallet directories (/tmp/bitcoin_func_test_kfajzcqh/node0/regtest/wallets and /tmp/bitcoin_func_test_kfajzcqh/node1/regtest/wallets), which causes GetWalletPath to fail, as it relies on ../../ to reach the old node’s wallet directory.

To fix this, migrations should only happen from a common ancestor directory to avoid ... However, I suggest using absolute paths for migration and deprecate this test case. Instead, the test should verify that attempting to migrate using a relative path returns an error (e.g., “migration from a relative path is not allowed”).

This change will cause users who load wallets using relative paths to fail. For the loadwallet RPC, it may be useful to improve the error message, for example:

Wallet file verification failed. Wallet names given as relative paths cannot begin with .. or . since v31. Please use an absolute path instead. Your wallet data has not been lost.

@achow101 achow101 force-pushed the wallet-no-relative-paths branch from 7c993c6 to 35161d1 Compare February 10, 2026 22:47
@achow101
Copy link
Member Author

However, I suggest using absolute paths for migration and deprecate this test case. Instead, the test should verify that attempting to migrate using a relative path returns an error (e.g., “migration from a relative path is not allowed”).

Yes, changed the test to check for the error, and removed the other test that used a relative path like this.

it may be useful to improve the error message

I included the suggestion to use an absolute path.

@arejula27
Copy link

When using Bitcoin-Qt, it fails during initialisation, shows an error, and then crashes. Relaunching the program results in the same error, making it impossible to use (and impossible to manually unload the wallet using the GUI).
image
To resolve the issue, users can open DATADIR/settings.json and remove the wallet entry:

{
    "_warning_": "This file is automatically generated and updated by Bitcoin Core. Please do not edit this file while the node is running, as any changes might be ignored or overwritten.",
    "wallet": [
        "../qt-wallet-relative"
    ]
}

For non-technical users, this can be difficult to troubleshoot. It would be nice to update the doc somewhere so they can read how to fix the crash loop or automatically remove all wallets that start with .. or ./ from the auto load configuration.

@achow101
Copy link
Member Author

For non-technical users, this can be difficult to troubleshoot. It would be nice to update the doc somewhere so they can read how to fix the crash loop or automatically remove all wallets that start with .. or ./ from the auto load configuration.

We can write something in the release notes. I would not expect non-technical users to have wallets named like this.

@arejula27
Copy link

Agree

@fanquake
Copy link
Member

I think this will need a rebase, to fix the failing Windows CI.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach ACK


def test_invalid_wallet_names(self):
self.log.info("Test weird paths are not allowed as wallet names")
NON_NORMALIZED = ["bad/./path", "bad/../path", "/bad/./path", "/bad/../path", "../", "./", "./wallet", "../wallets/../wallets/wallet"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-        NON_NORMALIZED = ["bad/./path", "bad/../path", "/bad/./path", "/bad/../path", "../", "./", "./wallet", "../wallets/../wallets/wallet"]
+        NON_NORMALIZED = ["bad/./path", "bad/../path", "/bad/./path", "/bad/../path", "../", "./", "./wallet", "../wallets/../wallets/wallet", "./wallets/wallet"]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's a useful test case as ./wallets does not exist.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was suggested to cover for the patterns satisfying the lexically_normal check. It can be just as easily be replaced with ./bad/path, a pattern that is not explicitly mentioned in the existing test cases.


def test_wallet_with_path(self, wallet_path):
self.log.info("Test migrating a wallet with the following path/name: %s", wallet_path)
# the wallet data is actually inside of path/that/ends/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment can be removed as well. It was originally added in 76fe0e5 for testing a path ending with .., but that specific case no longer exists and the comment probably should have been cleaned up already in 6a7c0d3, so it’s not really specific to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is still accurate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that the comment is technically correct. I understood the original intent of the comment to be that when the path is "path/that/ends/in/..", it actually resolves to path/that/ends/. That’s true, but since the specific test case where the path ends with .. has been removed in 6a7c0d3 and this function is now generic, having only this comment left is a bit confusing when reading the code. In any case, this is just my preference and not meant to be blocking.

{
const fs::path name_path = fs::PathFromString(name);

// 'name' must be a normalized path, i.e. no . or .. except at the root
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. no . or .. except at the root

This gives the impression that ./bad/path would be be allowed but it is not as the tests show.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perharps a graceful fallback can be added to handle the issue mentioned here: #34544 (comment) (which could also occur outside the GUI).

Otherwise, it looks good to me.

Wallet names that are also paths that contain . and .. are unintuitive
and can result in unexpected behavior, particularly in migration.
Therefore we should disallow users from specifying wallet names that
contain . and .. as path elements.
@achow101 achow101 force-pushed the wallet-no-relative-paths branch from 35161d1 to 914ca19 Compare March 14, 2026 00:41
@arejula27
Copy link

The PR changes behaviour from allowing relative paths with ../. elements to rejecting them. I would suggest to consider n alternative approach, which would be to normalise the path to absolute form at the start of the function instead of rejecting it, this way users could pass whatever they want, and internally we'd always work with absolute paths.
It would be more user-friendly, but I am not sure about the trade-offs it can produce

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants