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
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
members: read
steps:
- name: Validate Team Membership For Sensitive Changes
env:
Expand Down
61 changes: 61 additions & 0 deletions src/commands/cfg/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,67 @@ func TestResetAll_PreservesRoot(t *testing.T) {
}
}

func captureStderr(t *testing.T, fn func() error) (string, error) {
t.Helper()

oldStderr := os.Stderr
reader, writer, err := os.Pipe()
if err != nil {
t.Fatalf("os.Pipe() failed: %v", err)
}
os.Stderr = writer

errCh := make(chan error, 1)
var buffer bytes.Buffer
go func() {
_, copyErr := io.Copy(&buffer, reader)
errCh <- copyErr
}()

runErr := fn()
writer.Close()
os.Stderr = oldStderr
copyErr := <-errCh
reader.Close()
if copyErr != nil {
t.Fatalf("failed capturing stderr: %v", copyErr)
}

return buffer.String(), runErr
}

func TestSet_SkipMalwareChecksWarns(t *testing.T) {
bindTestRegistry(t)

out, err := captureStderr(t, func() error {
return runSetCfg(t, "skip_malware_checks=true")
})
if err != nil {
t.Fatalf("skip_malware_checks=true: %v", err)
}
if got := getSetting(t, "skip_malware_checks"); got != true {
t.Fatalf("skip_malware_checks: expected true, got %v", got)
}
wantOff := "Your system is no longer attempting to block malicious package installations"
if !strings.Contains(out, wantOff) {
t.Fatalf("skip on stderr = %q, want %q", out, wantOff)
}

out, err = captureStderr(t, func() error {
return runSetCfg(t, "skip_malware_checks=false")
})
if err != nil {
t.Fatalf("skip_malware_checks=false: %v", err)
}
if got := getSetting(t, "skip_malware_checks"); got != false {
t.Fatalf("skip_malware_checks: expected false, got %v", got)
}
wantOn := "Your system is now attempting to block malicious package installations"
if !strings.Contains(out, wantOn) {
t.Fatalf("skip off stderr = %q, want %q", out, wantOn)
}
}

func TestResetAll_SkipsBlockedKeys(t *testing.T) {
bindTestRegistry(t)
root := filepath.Join(os.TempDir(), "nvm_config_reset_all_blocked")
Expand Down
21 changes: 21 additions & 0 deletions src/commands/cfg/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ func (s *Set) Run() error {
}

log.LogConfigurationChanged(key, newValue, oldValue, log.OutcomeSucceeded, "")
if key == "skip_malware_checks" {
warnSkipMalwareChecks(value)
}
}

if len(errs) > 0 {
Expand Down Expand Up @@ -152,6 +155,24 @@ func displayValue(name string, value interface{}) string {
}
}

func warnSkipMalwareChecks(value string) {
skip := false
switch strings.ToLower(strings.TrimSpace(value)) {
case "1", "true", "yes", "on":
skip = true
case "0", "false", "no", "off":
skip = false
default:
return
}
msg := "Your system is now attempting to block malicious package installations"
if skip {
msg = "Your system is no longer attempting to block malicious package installations"
}
log.Warn(msg)
fmt.Fprintln(os.Stderr, msg)
}

func setAnnouncements(enabled bool) error {
if enabled {
log.Log("Project and release announcements have been enabled.")
Expand Down
3 changes: 3 additions & 0 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ replace common/mirrorauth v1.0.0 => ../../common/mirrorauth

replace common/token v1.0.0 => ../../common/token

replace common/malwaredb v1.0.0 => ../../common/malwaredb

replace common/cose v1.0.0 => ../../common/cose

replace common/license v1.0.0 => ../../common/licensing
Expand All @@ -54,6 +56,7 @@ require (
common/http v1.0.0
common/inspect v1.0.0
common/license v1.0.0
common/malwaredb v1.0.0
common/mirrorauth v1.0.0
common/modulefirewall v1.0.0
common/notify v1.0.0
Expand Down
36 changes: 36 additions & 0 deletions src/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import (
"golang.org/x/sys/windows/registry"

"common/acl"
"common/malwaredb"
"common/notify"
"nvm/bootstrap"
"nvm/log"
"nvm/prompt"
)

type InstallConfig struct {
Expand Down Expand Up @@ -144,6 +147,10 @@ func Install(cfg InstallConfig) error {
}
}

if err := seedMalwareDefinitions(status); err != nil {
return err
}

status.Done()
if failures.Load() > 0 {
return fmt.Errorf("%d version(s) failed to install", failures.Load())
Expand Down Expand Up @@ -627,6 +634,35 @@ func allowInsecureDownloads(cfg InstallConfig) bool {
return cfg.AllowInsecure
}

func seedMalwareDefinitions(status *Status) error {
root, err := bootstrap.DataRoot()
if err != nil || root == "" {
return nil
}
if _, err := os.Stat(filepath.Join(root, "malware.db")); err == nil {
return nil
}
_, err = malwaredb.Update(malwaredb.UpdateOptions{
Root: root,
License: malwaredb.LicenseHeader(),
})
if err == nil {
return nil
}
status.Alert(fmt.Errorf("malware definitions not downloaded: %v", err))
if !system.IsAppInForeground() {
return nil
}
ok, confirmErr := prompt.Confirm("Malicious package definitions could not be acquired. Package installs will be blocked until definitions download. Continue?", "n")
if confirmErr != nil {
return confirmErr
}
if !ok {
return fmt.Errorf("malware definitions not downloaded: %v", err)
}
return nil
}

func getRoot(version string) string {
target := expand(settings.Global().Root)
return filepath.Join(target, fmt.Sprintf("v%s", version))
Expand Down
Loading