Skip to content
Closed
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
19 changes: 13 additions & 6 deletions src/bootstrap/activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ var verifyActivationNode = func(path string) error {

var logActivationBlocked = func(versionDir, nodePath, failureKind, detail string) {
log.ErrorStructured("node.security.activation_blocked", log.StructuredPayload{
"action": "activation_blocked",
"detail": detail,
"failure_kind": failureKind,
"node_path": nodePath,
"source": "link-mode",
"version_path": versionDir,
"action": "activation_blocked",
"detail": detail,
"failure_kind": failureKind,
"node_path": nodePath,
"source": "link-mode",
"version_path": versionDir,
"user": log.Actor(),
"sid": log.ActorSid(),
"hostname": log.Hostname(),
"correlation_id": log.NewCorrelationID(),
"parent_process": log.ParentProcess(),
"project_name": log.ProjectName(),
"project_path": log.ProjectPath(),
}, activationBlockedEventCode)
}

Expand Down
31 changes: 27 additions & 4 deletions src/cmd/layout_warn.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import (
"time"
)

func warnStartupAdvisoriesIfNeeded() {
if license.IsCommunityBuild() {
warnCommunityProgramRootIfNeeded()
return
}
warnCertifiedCommunityFeatureModeIfNeeded()
}

func warnCommunityProgramRootIfNeeded() {
root, err := bootstrap.ProgramRoot()
if err != nil {
Expand All @@ -33,9 +41,24 @@ func warnCommunityProgramRootIfNeeded() {
}
}

func communityEditionWatermark() string {
if license.Edition() != "Community" {
return ""
func warnCertifiedCommunityFeatureModeIfNeeded() {
if !license.InCommunityFeatureMode() {
return
}
msg := license.CommunityFeatureModeWarning()
fmt.Fprintln(os.Stderr, msg)

root, err := bootstrap.ProgramRoot()
if err != nil {
return
}
stamp := filepath.Join(root, ".cache", "community-feature-mode-warn.stamp")
if _, err := eventlog.WriteApplicationWarningThrottled(
uint32(license.FeatureModeWarnEventID),
msg,
stamp,
time.Hour,
); err != nil {
_ = err
}
return "Community (per-user LocalAppData install; see nvm doctor)"
}
100 changes: 87 additions & 13 deletions src/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"fmt"
"nvm/bootstrap"
"nvm/commands"
"nvm/commands/firewall"
"nvm/installer"
"nvm/legacy"
"nvm/log"
Expand All @@ -35,6 +36,16 @@ func main() {
os.Args = append(os.Args, "--help")
}

// Toast / protocol activation (e.g. nvm://firewall?action=trust&...).
if strings.HasPrefix(strings.ToLower(os.Args[1]), "nvm://") {
settings.Load()
if err := firewall.HandleProtocolURI(os.Args[1]); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
return
}

switch os.Args[1] {
case "--register-eventlog":
// Invoked by OSS installer to support event log registration without needing to run the entire CLI installer.
Expand Down Expand Up @@ -64,15 +75,40 @@ func main() {
os.Exit(1)
}
return
case "--sign-installed-versions":
// Installer upgrade from 2.0.0: backfill script trust for every installs\v*.
settings.Load()
if err := verifycache.PrewarmVerifyCache(true); err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
return
case "--sign-version-scripts":
// Invoked by detached reshim after global package installs so proxy
// can trust newly written .cmd/.bat launchers without executing them first.
if len(os.Args) < 3 {
versionDir, wantSignChanged := parseSignVersionScriptsArgs(os.Args[2:])
if versionDir == "" {
fmt.Fprint(os.Stderr, "missing version directory for --sign-version-scripts\n")
os.Exit(1)
}
settings.Load()
if err := verifycache.SignVersionScripts(os.Args[2]); err != nil {
_ = os.Unsetenv("NVM_SIGN_CHANGED_MODULES")
if wantSignChanged && verifycache.ParentIsNvmReshim() {
verifycache.SetAllowSignChanged(true)
}
if err := verifycache.SignVersionScripts(versionDir); err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
return
case "--sign-script":
// Force-resign one launcher after trust prompt (bypass TrustedModules gate).
if len(os.Args) < 3 {
fmt.Fprint(os.Stderr, "missing script path for --sign-script\n")
os.Exit(1)
}
settings.Load()
if err := verifycache.SignScript(os.Args[2]); err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
Expand All @@ -82,10 +118,13 @@ func main() {
// ACL write window (RunWithRuntimeShimWrite); spawning reshim.exe alone
// cannot create hardlinks against the locked directory.
settings.Load()
args := []string{}
if len(os.Args) > 2 {
args = os.Args[2:]
args, readyEvent := splitReshimArgs(os.Args[2:])
_ = os.Unsetenv("NVM_SIGN_CHANGED_MODULES")
if verifycache.AuthorizeSignChangedFromParent() {
verifycache.SetAllowSignChanged(true)
args = append(args, "--sign-changed")
}
system.SignalNamedEvent(readyEvent)
if err := bootstrap.RunReshim(args...); err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
Expand Down Expand Up @@ -205,10 +244,6 @@ func main() {
case "-v", "--version", "version":
settings.Load()
fmt.Printf("v%s\n", version)
if mark := communityEditionWatermark(); mark != "" {
fmt.Println(mark)
}
warnCommunityProgramRootIfNeeded()
return
case "-h", "--help", "help":
// Handled by kong after lightweight init (no shim/reshim/ARP).
Expand All @@ -232,12 +267,12 @@ func main() {
}

settings.Load()
warnCommunityProgramRootIfNeeded()
// Layout / license advisories only on help and env — not every command.
if metaHelp || strings.EqualFold(os.Args[1], "env") {
warnStartupAdvisoriesIfNeeded()
}

desc := fmt.Sprintf("%s\nv%s (%s Edition).", description, version, license.Edition())
if mark := communityEditionWatermark(); mark != "" {
desc = fmt.Sprintf("%s\nv%s (%s Edition).\n%s.", description, version, license.Edition(), mark)
}

cli := kong.Parse(
root,
Expand Down Expand Up @@ -337,3 +372,42 @@ func capitalize(s string) string {
}
return strings.ToUpper(s[:1]) + s[1:]
}

func parseSignVersionScriptsArgs(args []string) (versionDir string, signChanged bool) {
for _, a := range args {
if a == "--sign-changed" {
signChanged = true
continue
}
if strings.HasPrefix(a, "--") {
continue
}
if versionDir == "" {
versionDir = a
}
}
return versionDir, signChanged
}

func splitReshimArgs(args []string) (forward []string, readyEvent string) {
forward = make([]string, 0, len(args))
for i := 0; i < len(args); i++ {
a := args[i]
if a == "--sign-changed" {
continue
}
if a == "--parent-ready-event" {
if i+1 < len(args) {
readyEvent = args[i+1]
i++
}
continue
}
if strings.HasPrefix(a, "--parent-ready-event=") {
readyEvent = strings.TrimPrefix(a, "--parent-ready-event=")
continue
}
forward = append(forward, a)
}
return forward, readyEvent
}
Loading
Loading