diff --git a/modulefirewall/rules.go b/modulefirewall/rules.go index 0391d5e..94bffc8 100644 --- a/modulefirewall/rules.go +++ b/modulefirewall/rules.go @@ -16,13 +16,21 @@ const ( DefaultApprovedWhenEmpty = "ALL" ) +// DefaultTrustedPackageManagers is the empty-TrustedModules seed: +// deny every module except supported package managers. +var DefaultTrustedPackageManagers = []string{ + "NOT ALL", "npm", "npx", "yarn", "yarnpkg", "corepack", "pnpm", "vlt", +} + // NormalizeTrustedModules applies the empty TrustedModules default: -// deny every module except bundled package managers, so npm/yarn/corepack +// deny every module except bundled package managers, so npm/yarn/corepack/vlt // can re-sign after an upgrade. func NormalizeTrustedModules(entries []string) []string { normalized := NormalizeList(entries, "") if len(normalized) == 0 { - return []string{"NOT ALL", "npm", "npx", "yarn", "yarnpkg", "corepack", "pnpm"} + out := make([]string, len(DefaultTrustedPackageManagers)) + copy(out, DefaultTrustedPackageManagers) + return out } return normalized } diff --git a/modulefirewall/rules_test.go b/modulefirewall/rules_test.go index 87aec03..bed5464 100644 --- a/modulefirewall/rules_test.go +++ b/modulefirewall/rules_test.go @@ -7,7 +7,7 @@ import ( func TestNormalizeTrustedModules_DefaultTrustsNpm(t *testing.T) { got := NormalizeTrustedModules(nil) - want := []string{"NOT ALL", "npm", "npx", "yarn", "yarnpkg", "corepack", "pnpm"} + want := []string{"NOT ALL", "npm", "npx", "yarn", "yarnpkg", "corepack", "pnpm", "vlt"} if len(got) != len(want) { t.Fatalf("got %#v", got) } @@ -37,6 +37,11 @@ func TestNormalizeTrustedModules_DefaultTrustsNpm(t *testing.T) { if err != nil || !ok { t.Fatalf("corepack default trust = %v err=%v", ok, err) } + vlt, _ := ParsePackageToken("vlt") + ok, err = IsPackageTrustedLocal(vlt, nil) + if err != nil || !ok { + t.Fatalf("vlt default trust = %v err=%v", ok, err) + } other, _ := ParsePackageToken("eslint") ok, err = IsPackageTrustedLocal(other, nil) if err != nil || ok { diff --git a/settings/options.go b/settings/options.go index 6c4119c..ed11c09 100644 --- a/settings/options.go +++ b/settings/options.go @@ -36,7 +36,7 @@ type Settings struct { Aliases []string `cfg:"aliases" reg:"Aliases" default:"" help:"Comma-delimited list of version aliases in the format alias=version." hidden:"true"` AllowedSigners []string `cfg:"allowed_signers" reg:"AllowedSigners" help:"Comma-delimited signer organization names (O=) allowed after Authenticode chain verification. Use to restrict vendors (for example OpenJS Foundation vs NodeSource)." hidden:"true"` AllowedThumbprints []string `cfg:"allowed_thumbprints" reg:"AllowedThumbprints" help:"Optional comma-delimited SHA-1 Authenticode leaf thumbprints (hex). When set, node.exe must match one pin after org allowlist. Empty disables pinning." hidden:"true"` - TrustedModules []string `cfg:"trusted_modules" reg:"TrustedModules" help:"Trust firewall: modules allowed to auto-reshim after self-update. NOT/! negation; empty defaults to NOT ALL plus npm, npx, yarn, yarnpkg, corepack, and pnpm. May include an HTTPS policy URL; local entries are checked first and only untrusted modules are POSTed to the URL." hidden:"true"` + TrustedModules []string `cfg:"trusted_modules" reg:"TrustedModules" help:"Trust firewall: modules allowed to auto-reshim after self-update. NOT/! negation; empty defaults to NOT ALL plus npm, npx, yarn, yarnpkg, corepack, pnpm, and vlt. May include an HTTPS policy URL; local entries are checked first and only untrusted modules are POSTed to the URL." hidden:"true"` UntrustedModuleHandlerAction string `cfg:"untrusted_module_handler_action" reg:"UntrustedModuleHandlerAction" default:"prompt" enum:"deny,prompt,allow" help:"Action when a global module changes without TrustedModules hit: prompt (default), allow (auto-reshim + quiet toast), or deny." hidden:"false"` ApprovedModules []string `cfg:"approved_modules" reg:"ApprovedModules" help:"Module firewall (certified): allow list for local installs. Empty defaults to ALL. May be a single HTTPS policy URL." hidden:"true"` ApprovedGlobalModules []string `cfg:"approved_global_modules" reg:"ApprovedGlobalModules" help:"Module firewall (certified): allow list for global installs. Empty defaults to ALL. May be a single HTTPS policy URL." hidden:"true"` diff --git a/verifycache/script_cache.go b/verifycache/script_cache.go index 51d84dd..24bf4cb 100644 --- a/verifycache/script_cache.go +++ b/verifycache/script_cache.go @@ -17,8 +17,9 @@ const scriptCacheBucket = "scripts" var ErrScriptCacheMiss = errors.New("delegated script cache entry missing") // SignVersionScripts records TPM-signed hashes for top-level .cmd/.bat launchers, -// package-manager JS entrypoints (npm-cli.js / npx-cli.js), and Authenticode -// verify-cache entries for top-level .exe helpers. +// package-manager JS entrypoints (npm-cli.js / npx-cli.js), Authenticode +// verify-cache entries for top-level .exe helpers, and Yarn Classic / pnpm +// global bin launchers outside the version directory. func SignVersionScripts(versionDir string) error { dataRoot, err := dataRootFromVersionDir(versionDir) if err != nil {