Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/source/advanced_usage/detailed_operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ When installing a ``noarch: python`` package, the installation process will comp
All installed files are later referenced in the ``$TARGET_PREFIX/conda-meta/mypkg-version-build.json`` file, to facilitate the removal (e.g. when upgrading or removing a package).

If the package contains a ``menu/*.json`` entry that follows the spec introduced by ``menuinst``, a start-menu entry is created on Windows. This is currently not implemented on Linux or macOS but that might change in the future.

Package link scripts
--------------------

Some packages also include ``pre-link``, ``post-link``, ``pre-unlink``,
or ``post-unlink`` scripts that are executed during the linking step.
Because these scripts can contain arbitrary code, they have security
implications. See :ref:`security` for more details.
54 changes: 54 additions & 0 deletions docs/source/advanced_usage/security.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.. _security:

Security
========

Package link scripts
--------------------

Some packages include ``pre-link``, ``post-link``, ``pre-unlink``, or
``post-unlink`` scripts that execute during package installation, update
or removal.

Note that ``post-unlink`` scripts are **deprecated** and therefore
not executed.

These scripts run in a shell subprocess and can perform arbitrary
operations on the system.

``pre-link`` scripts are particularly high-risk: they run from the
package cache and can modify the package itself, affecting all
environments that use that cached package.

By default, Mamba shows a security warning in case a transaction
involves packages with scripts and prompts for confirmation.

Disabling link scripts
^^^^^^^^^^^^^^^^^^^^^^

You can disable execution of all link scripts using the
``--skip-run-link-scripts`` flag:

.. code-block:: bash

mamba install somepackage --skip-run-link-scripts
mamba remove somepackage --skip-run-link-scripts

Or via configuration:

.. code-block:: yaml

# ~/.mambarc
skip_run_link_scripts: true

.. code-block:: bash

export MAMBA_SKIP_RUN_LINK_SCRIPTS=true

When disabled, scripts are silently skipped and the security warning
is suppressed.

.. note::
Some packages rely on link scripts for correct setup.
Disable only when you understand the trade-offs, such as when
installing from untrusted sources.
3 changes: 2 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ You can try Mamba now by visiting the installation for

advanced_usage/more_concepts
advanced_usage/detailed_operations
advanced_usage/artifacts_verification
advanced_usage/package_resolution
advanced_usage/artifacts_verification
advanced_usage/security

.. toctree::
:caption: LIBMAMBA USAGE
Expand Down
8 changes: 4 additions & 4 deletions docs/source/installation/mamba-installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ uninstalling ``mamba`` involves removing the entire Miniforge installation.
Use these paths to adapt the commands below to your specific installation.

1. Remove shell initialization
^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you initialized your shell with ``mamba shell init``, you need to remove the initialization code from your shell configuration files.
Run the following command for each shell you initialized:
Expand All @@ -88,7 +88,7 @@ uninstalling ``mamba`` involves removing the entire Miniforge installation.
This will remove the mamba initialization block from your shell configuration files (``.bashrc``, ``.zshrc``, ``config.fish``, etc.).

2. Remove the Miniforge installation directory and package cache
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The Miniforge installation directory contains ``mamba``, ``conda``, and all installed packages and environments.
Check ``mamba info`` to find the exact location:
Expand Down Expand Up @@ -129,7 +129,7 @@ uninstalling ``mamba`` involves removing the entire Miniforge installation.
Make sure you have backed up any important environments or data before removing this directory.

3. Remove configuration files (optional)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Check ``mamba info`` for the exact paths to your configuration files:

Expand Down Expand Up @@ -157,7 +157,7 @@ uninstalling ``mamba`` involves removing the entire Miniforge installation.
If you also use ``conda`` from another distribution (like Anaconda), be careful not to delete shared configuration files that are still needed.

4. Remove from PATH (if manually added)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you manually added Miniforge to your PATH, remove those entries from your shell configuration files (``.bashrc``, ``.zshrc``, ``.profile``, etc.).

Expand Down
8 changes: 4 additions & 4 deletions docs/source/installation/micromamba-installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ To completely remove ``micromamba`` from your system, follow these steps:
Use these paths to adapt the commands below to your specific installation.

1. Remove shell initialization
^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you initialized your shell with ``micromamba shell init``, you need to remove the initialization code from your shell configuration files.
Run the following command for each shell you initialized:
Expand All @@ -295,7 +295,7 @@ To completely remove ``micromamba`` from your system, follow these steps:
This will remove the mamba initialization block from your shell configuration files (``.bashrc``, ``.zshrc``, ``config.fish``, etc.).

2. Remove the micromamba executable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The location of the ``micromamba`` executable depends on your installation method:

Expand All @@ -311,7 +311,7 @@ To completely remove ``micromamba`` from your system, follow these steps:
Remove the directory where you extracted or installed the ``micromamba`` executable.

3. Remove the root prefix directory and package cache
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``micromamba`` stores all environments, packages, and cache in specific directories.
Check ``micromamba info`` to find the exact locations for your installation:
Expand Down Expand Up @@ -346,7 +346,7 @@ To completely remove ``micromamba`` from your system, follow these steps:
This will delete all environments, installed packages, and cached data. Make sure you have backed up any important environments or data before removing this directory.

4. Remove configuration files (optional)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Check ``micromamba info`` for the exact paths to your configuration files:

Expand Down
1 change: 1 addition & 0 deletions libmamba/include/mamba/core/context_params.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace mamba
bool always_copy = false;
bool always_softlink = false;
bool compile_pyc = true;
bool skip_run_link_scripts = false;
};

struct ThreadsParams
Expand Down
8 changes: 8 additions & 0 deletions libmamba/src/api/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,14 @@ namespace mamba
.set_env_var_names()
.description("Defines if PYC files will be compiled or not"));

insert(
Configurable("skip_run_link_scripts", &m_context.link_params.skip_run_link_scripts)
.group("Extract, Link & Install")
.set_rc_configurable()
.set_env_var_names()
.description("Whether pre/post-un/link scripts will be skipped. Defaults to false.")
);

insert(
Configurable("use_uv", &m_context.use_uv)
.group("Extract, Link & Install")
Expand Down
10 changes: 9 additions & 1 deletion libmamba/src/core/link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string>
#include <string_view>
#include <tuple>
#include <unordered_map>
#include <vector>

#include <fmt/format.h>
Expand Down Expand Up @@ -567,7 +568,14 @@ namespace mamba
return true;
}

std::map<std::string, std::string> envmap;
if (transaction_params.link_params.skip_run_link_scripts)
{
LOG_DEBUG << "Skipping " << action << " script for '" << pkg_info.name
<< "' (disabled via config)";
return true;
}

std::unordered_map<std::string, std::string> envmap;
if (action == "pre-link")
{
LOG_WARNING
Expand Down
9 changes: 6 additions & 3 deletions libmamba/src/core/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1172,9 +1172,12 @@ namespace mamba
return true;
}

LOG_WARNING
<< "Security Warning: This transaction includes executing package scripts (pre/post-link/unlink) if present. "
<< "These scripts can contain arbitrary code. Please ensure you trust the package sources.";
if (!ctx.link_params.skip_run_link_scripts)
{
LOG_WARNING
<< "Security Warning: This transaction includes executing package scripts (pre/post-link/unlink) if present. "
<< "These scripts can contain arbitrary code. Please ensure you trust the package sources.";
}

return Console::prompt("Confirm changes", 'y');
}
Expand Down
4 changes: 4 additions & 0 deletions libmamba/tests/src/core/test_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,10 @@ namespace mamba

TEST_BOOL_CONFIGURABLE(always_copy, ctx.link_params.always_copy);

TEST_BOOL_CONFIGURABLE(compile_pyc, ctx.link_params.compile_pyc);

TEST_BOOL_CONFIGURABLE(skip_run_link_scripts, ctx.link_params.skip_run_link_scripts);

TEST_CASE_METHOD(Configuration, "always_softlink_and_copy")
{
util::set_env("MAMBA_ALWAYS_COPY", "true");
Expand Down
41 changes: 29 additions & 12 deletions libmamba/tests/src/core/test_link_scripts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@ namespace mamba
fs::create_directories(prefix / "bin");
auto conda_path = prefix / "bin" / "conda";
auto out = open_ofstream(conda_path);
out << "#!/bin/sh\n";
out << "if [ \"$1\" = \"shell.posix\" ] && [ \"$2\" = \"hook\" ]; then\n";
out << " echo 'conda() { :; }'\n"; // Define conda as a no-op function
out << "fi\n";
out << R"(#!/bin/sh
if [ "$1" = "shell.posix" ] && [ "$2" = "hook" ]; then
# Define conda as a no-op function
echo 'conda() { :; }'
fi
)";
make_executable(conda_path);
}

const auto make_tx_context = [&]
const auto make_tx_context = [&](const LinkParams& link_params = {})
{
TransactionParams tx_params{
.is_mamba_exe = false,
Expand All @@ -80,7 +82,7 @@ namespace mamba
.conda_prefix = prefix,
.relocate_prefix = prefix,
},
.link_params = {},
.link_params = link_params,
.threads_params = {},
};

Expand All @@ -92,11 +94,11 @@ namespace mamba
);
};

std::string script_ext = util::on_win ? ".bat" : ".sh";
std::string pre_link_name = ".test_pkg-pre-link" + script_ext;
std::string post_link_name = ".test_pkg-post-link" + script_ext;
std::string pre_unlink_name = ".test_pkg-pre-unlink" + script_ext;
std::string post_unlink_name = ".test_pkg-post-unlink" + script_ext;
const std::string script_ext = util::on_win ? ".bat" : ".sh";
const std::string pre_link_name = ".test_pkg-pre-link" + script_ext;
const std::string post_link_name = ".test_pkg-post-link" + script_ext;
const std::string pre_unlink_name = ".test_pkg-pre-unlink" + script_ext;
const std::string post_unlink_name = ".test_pkg-post-unlink" + script_ext;

auto create_script = [](const fs::u8path& p, const fs::u8path& marker_path)
{
Expand Down Expand Up @@ -165,7 +167,22 @@ namespace mamba

REQUIRE(fs::exists(pre_unlink_marker));
// post-unlink script should not be executed as deprecated
REQUIRE(!fs::exists(post_unlink_marker));
REQUIRE_FALSE(fs::exists(post_unlink_marker));
}

SECTION("link scripts disabled")
{
auto tx_context = make_tx_context({ .skip_run_link_scripts = true });
LinkPackage link_pkg(pkg, cache_dir, &tx_context);
UnlinkPackage unlink_pkg(pkg, cache_dir, &tx_context);

REQUIRE(link_pkg.execute());
REQUIRE(unlink_pkg.execute());

REQUIRE_FALSE(fs::exists(pre_link_marker));
REQUIRE_FALSE(fs::exists(post_link_marker));
REQUIRE_FALSE(fs::exists(pre_unlink_marker));
REQUIRE_FALSE(fs::exists(post_unlink_marker));
}
}
}
Expand Down
13 changes: 10 additions & 3 deletions libmambapy/bindings/legacy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1137,25 +1137,32 @@ bind_submodule_impl(pybind11::module_ m)
pyLinkParams
.def(
py::init(
[](bool allow_softlinks, bool always_copy, bool always_softlink, bool compile_pyc) -> LinkParams
[](bool allow_softlinks,
bool always_copy,
bool always_softlink,
bool compile_pyc,
bool skip_run_link_scripts) -> LinkParams
{
return {
.allow_softlinks = allow_softlinks,
.always_copy = always_copy,
.always_softlink = always_softlink,
.compile_pyc = compile_pyc,
.skip_run_link_scripts = skip_run_link_scripts,
};
}
),
py::arg("allow_softlinks") = default_link_params.allow_softlinks,
py::arg("always_copy") = default_link_params.always_copy,
py::arg("always_softlink") = default_link_params.always_softlink,
py::arg("compile_pyc") = default_link_params.compile_pyc
py::arg("compile_pyc") = default_link_params.compile_pyc,
py::arg("skip_run_link_scripts") = default_link_params.skip_run_link_scripts
)
.def_readwrite("allow_softlinks", &LinkParams::allow_softlinks)
.def_readwrite("always_copy", &LinkParams::always_copy)
.def_readwrite("always_softlink", &LinkParams::always_softlink)
.def_readwrite("compile_pyc", &LinkParams::compile_pyc);
.def_readwrite("compile_pyc", &LinkParams::compile_pyc)
.def_readwrite("skip_run_link_scripts", &LinkParams::skip_run_link_scripts);

static const auto default_validation_params = ValidationParams{};
pyValidationParams
Expand Down
12 changes: 12 additions & 0 deletions micromamba/src/common_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ no_channel_priority_hook(Configuration& config, bool&)
}
}

void
init_link_options(CLI::App* subcom, Configuration& config)
{
auto& skip_run_link_scripts = config.at("skip_run_link_scripts");
subcom->add_flag(
"--skip-run-link-scripts,!--run-link-scripts",
skip_run_link_scripts.get_cli_config<bool>(),
skip_run_link_scripts.description()
);
}

void
init_install_options(CLI::App* subcom, Configuration& config)
{
Expand All @@ -354,6 +365,7 @@ init_install_options(CLI::App* subcom, Configuration& config)
init_prefix_options(subcom, config);
init_network_options(subcom, config);
init_channel_parser(subcom, config);
init_link_options(subcom, config);

auto& specs = config.at("specs");
subcom
Expand Down
3 changes: 3 additions & 0 deletions micromamba/src/common_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ init_general_options(CLI::App* subcom, mamba::Configuration& config);
void
init_prefix_options(CLI::App* subcom, mamba::Configuration& config);

void
init_link_options(CLI::App* subcom, mamba::Configuration& config);

void
init_install_options(CLI::App* subcom, mamba::Configuration& config);

Expand Down
2 changes: 2 additions & 0 deletions micromamba/src/env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ set_env_command(CLI::App* com, mamba::Configuration& config)
auto* remove_subcom = com->add_subcommand("remove", "Remove an environment");
init_general_options(remove_subcom, config);
init_prefix_options(remove_subcom, config);
init_link_options(remove_subcom, config);

remove_subcom->callback(
[&config]
Expand Down Expand Up @@ -351,6 +352,7 @@ set_env_command(CLI::App* com, mamba::Configuration& config)

init_general_options(update_subcom, config);
init_prefix_options(update_subcom, config);
init_link_options(update_subcom, config);

auto& file_specs = config.at("file_specs");
update_subcom
Expand Down
1 change: 1 addition & 0 deletions micromamba/src/remove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set_remove_command(CLI::App* subcom, Configuration& config)
using string_list = std::vector<std::string>;
init_general_options(subcom, config);
init_prefix_options(subcom, config);
init_link_options(subcom, config);

auto& specs = config.at("specs");
subcom
Expand Down
Loading