安装脚本白名单Install-script allowlists
软件包管理器和语言生态系统中的安装脚本白名单机制正变得越来越重要。文章对不同生态系统中的安装脚本白名单机制进行了全面调查。这类机制旨在防止恶意代码在软件包安装过程中未经授权执行。通过限制 `post-install` 等生命周期脚本的运行,可以显著降低供应链攻击的风险。了解这些机制有助于开发者构建更安全的部署流程。
Andrew Nesbitt
在大多数包管理器中,依赖项的安装代码在你安装它时默认就会运行:例如 npm postinstall、Setuptools 的 setup.py、CPAN 的 Makefile.PL、RPM scriptlet、Conda post-link 或 Debian postinst。也有少数包管理器要求在任何此类代码运行之前,必须明确地针对特定包进行手动授权(opt-in),根据工具的不同,这通常被称为 allowlist(允许列表)或 trusted-dependencies list(受信依赖列表)。
按包授权列表指定了哪些依赖项可以运行其安装代码:npm、pnpm、Bun、Deno 和 Composer 插件都采用这种方式。全局沙箱(opam、Swift Package Manager、Nix、Guix、Portage)则采用不同的形式,它们允许执行所有代码,但会严格限制这些代码在执行时能够访问的资源。身份与签名验证机制(RubyGems trust policies、Gradle dependency verification、NuGet trustedSigners、apt-secure)则是通过验证签名者身份来决定最初允许安装哪些构件(artifacts),但这并不干涉这些代码在后续运行时的行为。
npm postinstall、setup.py、Makefile.PL 或 RPM scriptlet 是在获取(fetch)或解压(unpack)阶段触发的。而 Cargo build.rs 或 Zig build.zig 则是在项目编译时运行的,虽然在全新构建(fresh build)中这在功能上相当于紧接其后的下一步,但在结构上却有着本质的区别。JVM 构建文件(Gradle 的 Groovy 或 Kotlin 脚本、Maven 的插件目标调用、SBT 的 Scala 脚本)执行得更早,在任何项目源码进入编译器之前就已经运行了。
JavaScript
npm 在 11.10.0 版本(2026 年 2 月)中通过 package.json 中的 allowScripts 字段引入了按包控制的允许列表。该列表通过 npm approve-scripts 和 npm deny-scripts 命令进行管理,其条目默认会精确锁定到特定版本(例如 [email protected]: true),而拒绝执行(denials)的记录则仅保留包名。
在 11.x 版本中,该行为目前仅起建议作用:脚本依然会执行,但在安装结束的摘要中会列出所有未经审查的包,且官方文档提示在未来的版本中将会进行强制拦截(hard block)。在同版本中添加的名称相似的 npm trust 命令,其实是用于 OIDC 可信发布(trusted publishing)的,与脚本执行无关。
pnpm v10(2025 年 1 月)默认阻止了安装脚本的运行,并通过 package.json 或 pnpm-workspace.yaml 中的 onlyBuiltDependencies / neverBuiltDependencies 字段来读取允许列表。v11 版本将这些配置合并为一个单一的 allowBuilds 映射,并提供 dangerouslyAllowAllBuilds 作为解除限制的兜底选项。配套的 pnpm approve-builds 命令(添加于 10.1.0 版本)是一个交互式选择器,它支持在 CI 环境中使用 --all 参数,并且从 v11 开始支持位置参数,例如 pnpm approve-builds esbuild fsevents !core-js。当 strictDepBuilds 设置为 true(v11 的默认值)时,未在列表中的包会导致安装失败;否则只会发出警告。
Yarn Classic (v1) 没有内置的按包控制机制,仅提供了一个全局的 --ignore-scripts 标志,目前 yarnpkg/yarn#7338 正在追踪该功能的需求。@lavamoat/allow-scripts 项目则为 Yarn v1.22+、Yarn Berry v3+、npm v8+ 以及 pnpm 提供了一种补丁式的解决方案:它先在包管理器级别禁用所有脚本,然后根据 package.json 中的 lavamoat.allowScripts 映射来驱动特定脚本的执行。Yarn Berry (v2+) 则采用声明式配置:在 .yarnrc.yml 中全局设置 enableScripts: false,然后通过配置 dependenciesMeta.<pkg>.built: true 来按需重新启用特定包的脚本。该工具没有提供交互式的审批命令,并且无论全局设置如何,工作区内的包(workspace packages)总是会运行其自身的脚本。
Bun 默认阻止依赖项的安装脚本,并提供了一个内置的默认允许列表,包含 esbuild、fsevents 等知名软件包,但仅当这些包来源于 npm registry 时才会被自动信任。package.json 中的 trustedDependencies 数组会覆盖该列表,因此只要手动指定信任单个包,就会完全替换掉默认的受信任集合。可以通过 `bun pm trust <pkg>` 或 `bun add --trust <pkg>` 按包名添加信任(后者会同时引入该包的传递依赖),而 `bun pm untrusted` 则会列出带有安装脚本但未被授予信任的包。
除非明确批准,否则 Deno 绝不运行 npm 生命周期脚本。该批准通过 `deno install` 和 `deno cache`(Deno 1.45/1.46,2024 年中发布)的 `--allow-scripts=<pkg>` 标志进行配置,该标志接受以逗号分隔的包标识符,例如 `npm:sqlite3,npm:[email protected]`。Deno 2.6(2025 年 12 月发布)新增了 `deno approve-scripts` 命令,它会将针对每个包的授权决定持久化保存在 `deno.json` 中。未经批准的包在安装时会跳过其脚本,并在安装结束时的警告信息中列出,以便在下次运行前进行审查。
PHP
Composer 的顶层 scripts 字段包含了与 `pre-install-cmd` 和 `post-update-cmd` 等事件绑定的生命周期钩子,但在安装时只有根包的脚本会运行:与 npm 的 postinstall 不同,依赖包的脚本绝不会在父项目中执行。插件才是实际存在间接执行风险的入口,而 `allow-plugins` 配置项(Composer 2.2,2021-12-22 引入)使得插件的激活需要按包进行显式授权。
该配置项接受带有通配符支持的 `"vendor/package": true|false` 键值对(例如 `"vendor/*": true`),默认值为 `{}`,遇到未列出的插件时会进行交互式提示,并将选择的结果持久化保存。在非交互式运行环境中(如使用 `--no-interaction` 或在 CI 中),系统会将包安装到 `vendor/` 目录,但会跳过执行其插件代码,因此未列出的插件不会中断安装过程,只是不会被激活。
Python
Python wheels 通常没有安装时的钩子,因此对于 Python 而言,关于安装脚本的问题就变成了:当解析器选择下载 sdist(源码分发包)而不是预构建的 wheel 时,是否允许该包在本地执行 PEP 517 构建后端代码。
Pip 并没有针对此机制的单包允许列表。2012 年提交的 pypa/pip#425,标题为“pip 不应执行来自互联网的任意代码”,体现了其历史立场。目前最接近的控制手段都是全局性的:`pip install --only-binary :all:` 会完全拒绝源码分发包,而 `--no-binary <pkg>` 则可作为针对特定包的例外。《安全安装指南》建议将 `--only-binary :all:` 与 `--require-hashes` 结合使用。反向操作 `--only-binary-except=<pkg>` 则在 pypa/pip#10724 中被跟踪记录。
pypa/pip#13079(已在 pip 25.0 中修复)表明,wheel 在实际使用中并非绝对无害:恶意的 wheel 可能会覆盖 pip 自身的内部模块,并在 `pip install` 运行末期执行恶意代码。
uv 通过一系列结合了全局和单包维度的开关设置,提供了针对单包的源码构建控制:`no-build` 和 `no-build-package` 拒绝使用 sdist,`no-binary` 和 `no-binary-package` 强制进行源码构建,`no-build-isolation` 和 `no-build-isolation-package` 则用于切换 PEP 517 构建隔离。这些配置组合起来,实质上构成了一个单包允许列表,决定了哪些包可以在本地执行构建后端代码。astral-sh/uv#11682 提出希望让 `only-binary` 除了现有的 CLI 标志外,还能支持持久化的项目级配置形式。
Poetry 暴露了 installer.only-binary (Poetry 2.0.0+) 和 installer.no-binary 配置项,接受逗号分隔的包列表或特殊值 :all: / :none:。将 installer.only-binary = ":all:" 与 installer.no-binary = "pkgA" 组合使用,可以通过组合逻辑生成按包划分的源码构建白名单,因为文档指出显式的包名会覆盖 :all:。PDM 提供了用于构建隔离的 --no-isolation,但在 CLI 参考中没有等效的 no-binary-package 选项。Pipenv 原生两者都不支持。文档中记载的替代方法是使用 --extra-pip-args="--only-binary=:all:",或者设置 PIP_NO_BINARY / PIP_ONLY_BINARY 环境变量供 pip 直接读取。
Conda 包可以附带 pre-link、post-link 和 pre-unlink shell 脚本,这些脚本会在安装和卸载期间于用户机器上运行。link-scripts 文档建议开发者避免使用它们,但并没有记录任何白名单、.condarc 开关或 CLI 标志来禁用它们。Conda 的安全配置项(safety_checks、extra_safety_checks、signing_metadata_url_base、频道白名单/黑名单)涵盖的是构件(artifact)完整性和频道来源,而不是针对每个包的脚本执行。Mamba 和 micromamba 重新实现了安装模型,但也继承了同样的空白。
间接的缓解措施是,策略要求 noarch: python 包不得附带链接脚本,因此,如果将使用范围限制在 noarch: python 依赖项,就可以避免纯 Python 工作中的攻击面。
Ruby
RubyGems 和 Bundler 没有针对安装时代码执行的按包(per-gem)白名单。包含 ext/<name>/extconf.rb 的 Gems 会在安装时运行任意 Ruby 代码以配置原生扩展的构建,声明在 gem 的 extensions 列表中的 Rakefile / mkrf_conf 变体也是如此。guides.rubygems.org/security 上的签名和信任策略机制(LowSecurity、MediumSecurity、HighSecurity)检查的是 gem 的发布者,而不是它是否会运行安装时代码。bundle config build.<gem> -- --with-foo 会将参数传递给原生构建过程,但不会限制构建是否发生。
Perl
CPAN 发行版附带 Makefile.PL (ExtUtils::MakeMaker) 或 Build.PL (Module::Build),它们是由 cpan、cpanm 或 cpm 在安装时执行的普通 Perl 脚本。这里没有针对每个发行版的能力限制,没有首次运行提示,也没有等效于 allow-plugins 的机制。CPAN.pm 暴露了 makepl_arg、mbuildpl_arg 和 prerequisites_policy 配置项,用于调整 Makefile.PL 的调用方式和依赖项的解析方式,但它们都不能限制代码是否运行。
系统级语言
在针对受影响 crate 执行每次 cargo build、test、run 和 install 时,Cargo 会将 build.rs 和 proc-macros 作为普通的宿主机原生 Rust 代码运行。Proc-macros 在编译期间在 rustc 进程内部执行,因此任何过程宏依赖项都会在每次构建时运行其代码。目前没有用于禁用 proc-macros 的全局标志,脚本进程周围也没有沙箱。一个 crate 自身的 Cargo.toml 可以设置 build = false 来抑制其自身的构建脚本,但使用者无法禁用依赖项的 build.rs。
长期跟进的追踪议题包括 rust-lang/cargo#5720(沙箱/隔离构建脚本,2018 年 7 月)和 rust-lang/cargo#13681(构建脚本白名单模式,2024 年 4 月),以及 compiler-team 提出的通过 rustup 发布隔离运行时的 MCP,这些都没有落地。cargo-vet 和 cargo-crev 会标记自定义构建的 crate 以引起审查者的注意;但它们都不能阻止代码执行。
Go modules 除了编译之外不会运行下载的代码,在 Russ Cox 的《Command PATH security in Go》一文中,go run、go test 和 go generate 被记录为明确的例外。由于第三方代码根本就不会运行,因此不存在针对每个模块的信任机制。cgo 的 #cgo CFLAGS: 和 LDFLAGS: 指令一直是个突破口。CVE-2018-6574、CVE-2024-24787 和 #42559 均通过扩展工具链中允许的编译器/链接器标志的硬编码白名单来缓解。CVE-2023-39323 通过限制 cgo 生成文件中的 //line 指令,解决了相关攻击面的问题。在上述所有情况中,都没有增加针对每个模块的授权。
Swift Package Manager 在沙盒(macOS 上为 sandbox-exec)内运行 Package.swift 清单评估和包插件,默认情况下没有网络访问权限,且写入操作被限制在每个插件专属的临时目录中。需要更多权限的插件会在其目标定义中使用 PluginPermission: writeToPackageDirectory(reason:) 和 allowNetworkConnections(scope:reason:) 声明权限,其中 scope 可以为 none、local(ports:)、all(ports:)、docker 或 unixDomainSocket。用户会在 TTY 上收到提示(PR #5483),或者必须以非交互方式传递 --allow-writing-to-package-directory / --allow-network-connections 参数,且决策范围限定在每个包。
该权限授予模型涵盖了命令插件,但不包括构建工具插件。构建工具插件默认仍在沙盒内运行,但无法声明或被授予 writeToPackageDirectory / allowNetworkConnections 权限。构建工具沙盒权限提案正在跟进这一攻击面的扩展。
Zig 的 build.zig 是任意的 Zig 代码,会被编译为原生主机二进制文件并由 zig build 执行,包括由包管理器引入的每个传递依赖。这里没有沙盒,也没有针对每个包的关卡。ziglang/zig#14286 处的提案(状态为开放,标记为紧急)尚未合并实现代码。该提案会将每个 build.zig 编译为 wasm32-wasi,并将构建图作为数据输出,供单独的 build_runner 在被授予的任何权限下执行。
JVM
JVM 依赖项是被动的 JAR,在解析或安装时不会执行。构建时插件才是执行面。
Maven 没有内置的允许加载哪些插件的白名单。插件目标(goals)在构建生命周期中作为普通的 Java 代码执行。Maven Enforcer 插件的 bannedPlugins 和 bannedDependencies 规则是带有包含豁免的黑名单,因此必须通过禁用 * 并重新包含特定的 GAV 来表达白名单。在 .mvn/extensions.xml 中声明的核心扩展会在构建开始前加载到 Maven 的核心类加载器中,且没有签名检查或白名单。
Gradle 的 build.gradle(.kts)、settings.gradle(.kts)、约定插件和应用插件都会在配置阶段执行任意的 Kotlin/Groovy 代码,并且没有针对每个插件的代码执行白名单。通过 verification-metadata.xml 进行的依赖验证,通过对构件身份的校验和与 PGP 签名验证,涵盖了常规依赖项和插件。这确立了是谁发布了该构件,而不是其代码可能会做什么。初始化脚本(-I、$GRADLE_USER_HOME/init.gradle(.kts)、init.d/*.init.gradle(.kts))无条件运行,没有签名检查。配置缓存为了性能而序列化已配置的任务图,而不是为了限制插件代码可能会做什么。
在 project/plugins.sbt 中声明的 SBT 插件会在构建配置阶段运行,并拥有完整的 JVM 访问权限。官方文档将插件与构建定义之间的类加载器级别封装描述为一种开发上的便利,而非安全边界。SBT 没有类似于 Gradle 的 verification-metadata.xml 那样的允许列表或签名验证机制,而是继承了底层 Ivy 或 Coursier 解析器所提供的任何制品验证策略。Leiningen 和 Mill 采用了相同的方式,Clojure 中的 project.clj 和 Scala 中的 build.sc 都作为配置阶段的程序运行,并且两者都没有提供针对每个插件的允许列表。
Bazel 位于 JVM 构建工具谱系的另一端。BUILD 文件和 .bzl 扩展使用 Starlark(一种 Python 方言)编写,它无法访问时钟、不能递归、没有可变的全局状态,并且除了声明的输入外,不能进行任何文件系统或网络调用。构建操作在一个沙箱中运行,该沙箱只能看到规则声明的内容。虽然存在一些逃生通道(用于获取的 repository_rule、用于 shell 的 genrule、自定义工具链),但其默认姿态是 BUILD 文件无法观察其宿主环境,并且针对每个操作的沙箱涵盖了原本需要允许列表才能控制的内容。
.NET
根据迁移指南,在 PackageReference(NuGet 4.0+ 以及 SDK 风格项目的默认项)模式下,历史上的 install.ps1 和 uninstall.ps1 PowerShell 脚本在安装或卸载时不再执行。
替代的执行面是 MSBuild 的 build/、buildMultiTargeting/ 和 buildTransitive/ .props 与 .targets 文件,它们通过 NuGet 生成的 {projectName}.nuget.g.props 和 .nuget.g.targets 自动导入到使用者的构建过程中。buildTransitive 允许传递依赖向你的项目提供 targets,而无需你将其显式声明为直接依赖。MSBuild target 导入没有针对每个包的允许列表。nuget.config 中的 <trustedSigners> 配置根据签名者身份控制接受哪些签名的包,但这并不影响其 MSBuild 贡献随后会执行的操作。
其他语言
Hex/Mix (Elixir) 会评估每个依赖项的 mix.exs 并在执行 mix deps.compile 时运行其编译任务,它没有针对每个包的允许列表,在编译之外也没有单独的安装脚本(install-script)字段。Rebar3 (Erlang) 支持 pre_hooks、post_hooks、provider_hooks 以及从 Hex 加载的插件,所有这些都在声明它们的依赖项被构建时执行,同样没有任何允许列表。
Cabal 和 Stack (Haskell) 在历史上会为带有 build-type: Custom 的包运行任意的 Setup.hs 程序。最近在 Cabal 3.14 (2024) 中引入的 build-type: Hooks 用一组固定的命名钩子点取代了全量的 Setup 替换,从而缩小了攻击面,且并未引入允许列表。
Opam (OCaml) 使用 sandbox.sh(opam 2.0,2018年)封装了每个包的 build: 和 install: 命令,在 Linux 上使用 bubblewrap,在 macOS 上使用 sandbox-exec。构建阶段可以写入构建目录和 /tmp,但将 switch 视为只读;安装阶段可以写入 switch。全程拒绝网络访问。该沙箱是全局的而非针对每个包的,并且可以通过 opam init --disable-sandboxing 将其关闭。
Pub (Dart/Flutter) 在历史上解析时不运行任何依赖项代码。hook/build.dart 机制最初作为 Dart 3.2 中的一个实验性功能(在 --enable-experiment=native-assets 之后)启动,并在 Dart 3.10 中稳定下来。该设计被宣称为用于实现可复现性的“半密封”(semi-hermetic),而不是为了对抗性隔离。
LuaRocks 的 rockspecs 可以声明 command、make、cmake 或 builtin 构建后端,其中 command 后端在执行 luarocks install 时会运行任意的 shell 命令,并且没有针对哪些 rocks 可以执行此操作的允许名单(allowlist)。
Nimble (Nim) 在 .nimble NimScript 文件中支持 before 和 after 模板钩子,其中执行外部进程(exec)是官方文档中记载的、用于绕过 NimScript 自身 FFI 限制的途径。zef (Raku) 在构建阶段会无条件运行 Build.rakumod 或在 META6.json 中声明的 builder 模块。--/build 标志可以全局禁用构建阶段;但未记录任何针对单个发行版的控制机制。
Crystal Shards 在 shard.yml 中支持 postinstall 字段,并将全局的 --skip-postinstall 标志作为唯一的退出选项。社区论坛帖子“postinstall considered harmful”探讨了修改这一机制的理由。Julia Pkg 在首次安装每个依赖项时会运行 deps/build.jl,目前的现代替代方案是通过哈希值引用由 BinaryBuilder 生成的 _jll 包,尽管 build.jl 依然受到支持。
CRAN 上的 R 源代码包在执行任何其他操作之前,会运行 configure Bourne shell 脚本(在 Windows 上为 configure.win),外加 R/zzz.R 中 .onLoad 和 .onAttach 里的任意代码。CRAN 的应对措施是进行人工审查,并提供由构建农场预编译的 Windows/macOS 二进制文件,而没有针对单个包的控制机制。
当 Podfile 首次引入带有 script_phase 构建阶段的 pod 时,CocoaPods 会在每次安装时显示警告;如果该 pod 在后续更新中仍包含这些阶段,也会继续显示警告,但系统不会持久化保存任何允许名单。Carthage 会克隆每个依赖项的代码库,并针对其共享 schemes 调用 xcodebuild,这会执行该依赖项 .xcodeproj 中声明的所有 Run Script 构建阶段,期间不会发出任何警告,也没有允许名单机制。
C/C++
Conan 的 recipe 是完整的 Python 模块,其 source()、build()、package() 和 package_info() 方法会在 conan install 和 conan create 期间于宿主 Python 进程中运行。这里没有沙箱或允许名单机制;ConanCenter 索引的人工审查构成了信任边界。
vcpkg 的 port 是由 CMake 脚本模式解释执行的 portfile.cmake 文件,能够调用 execute_process 和 vcpkg_execute_build_process,根据 port 文档说明,这里没有针对单个 port 的允许名单或沙箱。
Spack 的 package.py 文件是包含 install() 方法和构建阶段的任意 Python 代码,它们会在 spack install 期间运行。Spack 的安全框架主要涵盖下载完整性(带校验和的 tar 包、锁定的 git 提交),而不是针对单个 recipe 的权限控制。
操作系统发行版
在 dpkg/apt、RPM/dnf、pacman 以及 Alpine 的 apk 中,安装时的维护者脚本(dpkg 对应 preinst/postinst/prerm/postrm,RPM 对应 %pre/%post/%preun/%postun,pacman 对应 .INSTALL,apk 对应 $pkgname.{pre,post}-install 以及 .{pre,post}-upgrade、.{pre,post}-deinstall 和 .trigger)均以 root 权限运行,没有沙箱、没有 chroot,也没有 seccomp 过滤器。其信任模型在于软件仓库本身,apt-secure(8) 通过仓库的 GPG 签名来把控哪些软件包可以进入安装流程。这里没有针对单个包的允许名单或选择性启用的标志,并且 Debian wiki 的 UntrustedDebs 页面明确指出,安装来自受信任存档之外的 .deb 实际上等同于赋予软件包作者 root 权限。
pacman 的官方软件仓库遵循相同的仓库审查模型。AUR 则向用户公开原始的 PKGBUILD 和 .INSTALL 文件以供审查,而不同的 AUR 助手(如 yay、paru、pikaur 等,在助手对比表中有详细比较)在执行 source 之前是否会提示用户查看 PKGBUILD 的 diff 这一点上表现各异。
Nix 和 Guix 在一个 chroot 环境中运行每个 derivation 的构建器,该环境具有全新的 PID/网络/挂载命名空间、一个非特权构建用户(Nix 的 nixbld 池,Guix 的 guixbuild 池),并且除了预先声明了输出哈希的固定输出 derivation(fixed-output derivations)之外,没有网络访问权限。该模型记录在 Nix 配置参考和 Guix 构建环境设置章节中。每个构建器都在隔离环境内运行,固定输出 derivation 和少数受信任用户(trusted-users)构成了剩余的信任面。CVE-2024-27297 是一个影响 Nix 和 Guix 的固定输出 derivation 沙盒绕过漏洞。
Portage (Gentoo) 默认启用 FEATURES="sandbox",这是一个 LD_PRELOAD 垫片,用于拦截文件系统系统调用并阻止对允许的构建目录之外的写入操作。userpriv 以 portage 用户身份运行 ebuild 阶段,而 usersandbox 则结合了这两者。由于该机制基于 LD_PRELOAD,因此静态二进制文件和直接系统调用可以绕过它,Gentoo wiki 的 Sandbox (Portage) 页面中记录了这一点。信任仍然源自精心策划的 Portage 树中已签名的 Manifest 文件,没有针对单个 ebuild 的权限授予。Overlays 则明确位于该信任边界之外。
用户态包管理器
Homebrew、MacPorts、Scoop 和 Chocolatey 将信任定位在仓库(tap、ports tree、bucket)级别,而不是包级别:添加一个 tap 仓库或 bucket 会赋予其与核心仓库相同的信任级别,而单个 formula(配方)、port 或 manifest(清单)并没有针对单个包的允许列表。Homebrew 的安全策略明确界定了 tap 级别的信任边界。
MacPorts 对 ports tarball 进行签名(GHSA-2j38-pjh8-wfxw,于 2024 年 12 月披露,该漏洞涉及 rsync 过滤器绕过,允许恶意镜像越过签名归档边界传递未签名的 Portfiles,并在 portindex 期间触发 Tcl 执行)。Scoop 将已知 bucket 列表固化到客户端中,并执行逐个 manifest 的哈希验证。Chocolatey 在可选的包签名基础上,对社区提交的内容增加了人工审核,而受信任的包(Trusted Packages)可以根据作者的过往记录绕过人工审核。
winget 的不同之处在于其 YAML manifest 不包含任意的安装时脚本。受支持的 InstallerType 值是真实的安装程序格式(msi、msix、appx、exe、inno、nullsoft、wix、burn、portable、zip、font、msstore),并且 manifest 会声明安装程序二进制文件的 SHA256 哈希值。winget validate 会检查 manifest 格式;winget-pkgs 仓库上的 PR 审查加上 Azure Pipelines 机器人的验证涵盖了提交完整性,此外作者还可以运行一个可选的本地 SandboxTest.ps1 脚本,以便在提交前于 Windows Sandbox 内部测试候选版本。
版本管理器
asdf 插件是由 shell 脚本(bin/install、bin/list-all 等)组成的 Git 仓库,这些脚本在执行 asdf install 期间以当前用户身份运行,没有允许列表或沙盒机制:添加一个插件在功能上等同于运行其 bash 脚本。mise 通过非 shell 后端路由大多数工具来减少插件攻击面:mise discussion #4054 在默认注册表中将大多数工具映射到 aqua、ubi、vfox 或 core,并在 mise-plugins GitHub 组织下分叉了 asdf 插件,从而控制了代码提交权限。
mise trust 和 trusted_config_paths 控制着项目级 mise.toml 文件中 [env]、[hooks] 和 [tasks] 块的执行,在首次 cd 进入包含不受信任配置的目录时会进行提示,并按文件持久化保存该信任决定。
需要完整排版与评论请前往来源站点阅读。