From ff6e91511a4ba5d945d16ac2c6b0013501ed28b6 Mon Sep 17 00:00:00 2001 From: faze-geek Date: Fri, 24 Jul 2026 15:50:56 +0530 Subject: [PATCH] Add auth flag to list explicit output --- libmamba/src/api/list.cpp | 23 +++++++++++++-- micromamba/src/list.cpp | 9 ++++++ micromamba/tests/test_list.py | 53 +++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/libmamba/src/api/list.cpp b/libmamba/src/api/list.cpp index aeb7192769..dad6ca50b2 100644 --- a/libmamba/src/api/list.cpp +++ b/libmamba/src/api/list.cpp @@ -13,6 +13,7 @@ #include "mamba/core/channel_context.hpp" #include "mamba/core/context.hpp" #include "mamba/core/prefix_data.hpp" +#include "mamba/specs/conda_url.hpp" #include "mamba/util/string.hpp" namespace mamba @@ -32,6 +33,7 @@ namespace mamba bool canonical = false; bool export_ = false; bool revisions = false; + bool auth = false; }; struct formatted_pkg @@ -141,6 +143,11 @@ namespace mamba } PrefixData& prefix_data = sprefix_data.value(); + if (options.auth && !options.explicit_) + { + LOG_WARNING << "Option --auth ignored because --explicit was not provided."; + } + if (options.full_name) { regex = '^' + regex + '$'; @@ -288,6 +295,15 @@ namespace mamba LOG_WARNING << "Option --export ignored because --explicit was also provided."; } + // By default, strip authentication (token, username/password) from the URL. + // So explicit list can be shared safely. If --auth is specified, keep it. + const auto credentials = options.auth ? specs::CondaURL::Credentials::Show + : specs::CondaURL::Credentials::Remove; + auto format_url = [&](const std::string& url) -> std::string + { + auto parsed = specs::CondaURL::parse(url); + return parsed ? parsed->str(credentials) : url; + }; for (auto p : packages) { if (options.md5 && options.sha256) @@ -299,15 +315,15 @@ namespace mamba } if (options.md5) { - std::cout << p.url << "#" << p.md5 << std::endl; + std::cout << format_url(p.url) << "#" << p.md5 << std::endl; } else if (options.sha256) { - std::cout << p.url << "#" << p.sha256 << std::endl; + std::cout << format_url(p.url) << "#" << p.sha256 << std::endl; } else { - std::cout << p.url << std::endl; + std::cout << format_url(p.url) << std::endl; } } } @@ -381,6 +397,7 @@ namespace mamba options.canonical = config.at("canonical").value(); options.export_ = config.at("export").value(); options.revisions = config.at("revisions").value(); + options.auth = config.at("auth").value(); auto channel_context = ChannelContext::make_conda_compatible(config.context()); detail::list_packages(config.context(), regex, channel_context, std::move(options)); diff --git a/micromamba/src/list.cpp b/micromamba/src/list.cpp index 613dd4ff78..2690a17935 100644 --- a/micromamba/src/list.cpp +++ b/micromamba/src/list.cpp @@ -80,6 +80,15 @@ init_list_parser(CLI::App* subcom, Configuration& config) Configurable("revisions", false).group("cli").description("List the revision history.") ); subcom->add_flag("--revisions", revisions.get_cli_config(), revisions.description()); + + auto& auth = config.insert( + Configurable("auth", false) + .group("cli") + .description( + "Keep authentication (token, username/password) in the URL when using --explicit. Ignored if --revisions is also provided." + ) + ); + subcom->add_flag("--auth", auth.get_cli_config(), auth.description()); } void diff --git a/micromamba/tests/test_list.py b/micromamba/tests/test_list.py index 217387c627..28ff97cfb3 100644 --- a/micromamba/tests/test_list.py +++ b/micromamba/tests/test_list.py @@ -141,6 +141,59 @@ def test_list_subcommands( assert len(output.split("=")) == 3 +def _inject_token_into_conda_meta(prefix, token): + import json + import pathlib + + conda_meta = pathlib.Path(prefix) / "conda-meta" + count = 0 + for record in conda_meta.glob("*.json"): + data = json.loads(record.read_text()) + url = data.get("url", "") + if "://" not in url or "/t/" in url: + continue + scheme, rest = url.split("://", 1) + host, path = rest.split("/", 1) + data["url"] = f"{scheme}://{host}/t/{token}/{path}" + record.write_text(json.dumps(data)) + count += 1 + return count + + +@pytest.mark.parametrize("hash_flag", ["", "--md5", "--sha256"]) +@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True) +def test_list_explicit_auth(tmp_home, tmp_root_prefix, tmp_xtensor_env, hash_flag): + token = "sometoken123abc" + assert _inject_token_into_conda_meta(tmp_xtensor_env, token) > 0 + token_fragment = f"/t/{token}/" + + # Default: Authentication stripped from URLs. + default_res = helpers.umamba_list("-p", tmp_xtensor_env, "--explicit", hash_flag) + default_urls = [line for line in default_res.splitlines() if "://" in line] + assert len(default_urls) > 0 + for line in default_urls: + assert token_fragment not in line + assert line.startswith("https://") + if hash_flag == "--md5": + assert len(line.split("#")[-1].strip()) == 32 + elif hash_flag == "--sha256": + assert len(line.split("#")[-1].strip()) == 64 + + # With --auth: Authentication preserved in URLs. + auth_res = helpers.umamba_list("-p", tmp_xtensor_env, "--explicit", "--auth", hash_flag) + auth_urls = [line for line in auth_res.splitlines() if "://" in line] + assert len(auth_urls) > 0 + for line in auth_urls: + assert token_fragment in line + + +@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True) +def test_list_auth_without_explicit_is_ignored(tmp_home, tmp_root_prefix, tmp_xtensor_env): + res = helpers.umamba_list("-p", tmp_xtensor_env, "--auth") + assert "xtensor" in res + assert "xtl" in res + + @pytest.mark.parametrize("quiet_flag", ["", "-q", "--quiet"]) @pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True) def test_list_name(tmp_home, tmp_root_prefix, tmp_xtensor_env, quiet_flag):