Skip to content

Commit

Permalink
Added code-signing capability. Also updated aliases to elevate comman…
Browse files Browse the repository at this point in the history
…d when needed (issue #700)
  • Loading branch information
coreybutler committed Dec 7, 2021
1 parent c4a157c commit 5751e53
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 26 deletions.
16 changes: 13 additions & 3 deletions build.bat
Expand Up @@ -21,7 +21,8 @@ move nvm.exe %GOBIN%
cd ..\

REM Codesign the executable
REM .\buildtools\signtools\x64\signtool.exe sign /debug /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a %GOBIN%\nvm.exe
echo Sign the nvm executable...
buildtools\signtool.exe sign /debug /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a %GOBIN%\nvm.exe

for /f %%i in ('"%GOBIN%\nvm.exe" version') do set AppVersion=%%i
echo nvm.exe v%AppVersion% built.
Expand All @@ -42,26 +43,35 @@ REM Create the "no install" zip version
for %%a in ("%GOBIN%") do (buildtools\zip -j -9 -r "%DIST%\nvm-noinstall.zip" "%CD%\LICENSE" %%a\* -x "%GOBIN%\nodejs.ico")

REM Generate update utility
echo Generating update utility...
cd .\updater
go build nvm-update.go
echo Sign the updater...
buildtools\signtool.exe sign /debug /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a nvm-update.exe
move nvm-update.exe %DIST%
cd ..\

REM Generate the installer (InnoSetup)
echo Generating installer...
buildtools\iscc "%INNOSETUP%" "/o%DIST%"
echo Sign the installer
buildtools\signtool.exe sign /debug /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a %DIST%\nvm-setup.exe
echo Bundle the installer/updater...
buildtools\zip -j -9 -r "%DIST%\nvm-setup.zip" "%DIST%\nvm-setup.exe"
buildtools\zip -j -9 -r "%DIST%\nvm-update.zip" "%DIST%\nvm-update.exe"

del %DIST%\nvm-update.exe
del %DIST%\nvm-setup.exe

REM Generate checksums
echo Generating checksums...
for %%f in (%DIST%\*.*) do (certutil -hashfile "%%f" MD5 | find /i /v "md5" | find /i /v "certutil" >> "%%f.checksum.txt")

echo Cleaning up...
REM Cleanup
del %GOBIN%\nvm.exe
del %GOBIN%\nvm-update.exe
del %GOBIN%\nvm-setup.exe
@REM del %GOBIN%\nvm-update.exe
@REM del %GOBIN%\nvm-setup.exe

echo NVM for Windows v%AppVersion% build completed.
@echo on
100 changes: 77 additions & 23 deletions src/nvm.go
Expand Up @@ -181,7 +181,7 @@ func update() {
*/

func getVersion(version string, cpuarch string) (string, string, error) {
arch := strings.ToLower(cpuarch)
cpuarch = strings.ToLower(cpuarch)

if cpuarch != "" {
if cpuarch != "32" && cpuarch != "64" && cpuarch != "all" {
Expand Down Expand Up @@ -223,11 +223,12 @@ func getVersion(version string, cpuarch string) (string, string, error) {
version = v
}

version = strings.Replace(version, "v", "", 1)

v, err := semver.Make(version)
if err == nil {
err = v.Validate()
}

if err == nil {
// if the user specifies only the major/minor version, identify the latest
// version applicable to what was provided.
Expand All @@ -237,6 +238,8 @@ func getVersion(version string, cpuarch string) (string, string, error) {
} else {
version = cleanVersion(version)
}

version = strings.Replace(version, "v", "", 1)
}

return version, cpuarch, err
Expand Down Expand Up @@ -434,9 +437,11 @@ func uninstall(version string) {
fmt.Printf("Uninstalling node v" + version + "...")
v, _ := node.GetCurrentVersion()
if v == version {
runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`,
filepath.Join(env.root, "elevate.cmd"),
filepath.Clean(env.symlink)))
_, err := runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink)))
if err != nil {
fmt.Println(fmt.Sprint(err))
return
}
}
e := os.RemoveAll(filepath.Join(env.root, "v"+version))
if e != nil {
Expand All @@ -460,13 +465,17 @@ func findLatestSubVersion(version string) string {
return latest
}

func use(version string, cpuarch string) {
v, a, err := getVersion(cersion, cpuarch)
func use(version string, cpuarch string, reload ...bool) {
v, a, err := getVersion(version, cpuarch)
version = v
cpuarch = a

if err != nil {
fmt.Println(err.Error())
if strings.Contains(err.Error(), "No Major.Minor.Patch") {
fmt.Printf("Unrecognized version/alias: \"%v %v-bit\"", v, a)
} else {
fmt.Println(err.Error())
}
return
}

Expand All @@ -489,18 +498,39 @@ func use(version string, cpuarch string) {
// Remove symlink if it already exists
sym, _ := os.Stat(env.symlink)
if sym != nil {
if !runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`,
filepath.Join(env.root, "elevate.cmd"),
filepath.Clean(env.symlink))) {
return
_, err := runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink)))
if err != nil {
fmt.Println(fmt.Sprint(err))
}

// // Return if the symlink already exists
// if ok {
// fmt.Print(err)
// return
// }
}

// Create new symlink
if !runElevated(fmt.Sprintf(`"%s" cmd /C mklink /D "%s" "%s"`,
filepath.Join(env.root, "elevate.cmd"),
filepath.Clean(env.symlink),
filepath.Join(env.root, "v"+version))) {
var ok bool
ok, err = runElevated(fmt.Sprintf(`"%s" cmd /C mklink /D "%s" "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink), filepath.Join(env.root, "v"+version)))
if err != nil {
if strings.Contains(err.Error(), "file already exists") {
ok, err = runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink)))
reloadable := true
if len(reload) > 0 {
reloadable = reload[0]
}
if err != nil {
fmt.Println(fmt.Sprint(err))
} else if reloadable {
use(version, cpuarch, false)
return
}
} else {
fmt.Print(fmt.Sprint(err))
}
}
if !ok {
return
}

Expand Down Expand Up @@ -652,11 +682,13 @@ func enable() {
}

func disable() {
if !runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`,
filepath.Join(env.root, "elevate.cmd"),
filepath.Clean(env.symlink))) {
ok, err := runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink)))
if !ok {
return
}
if err != nil {
fmt.Print(fmt.Sprint(err))
}

fmt.Println("nvm disabled")
}
Expand Down Expand Up @@ -777,7 +809,25 @@ func updateRootDir(path string) {
}
}

func runElevated(command string) bool {
func runElevated(command string, forceUAC ...bool) (bool, error) {
uac := false
if len(forceUAC) > 0 {
uac = forceUAC[0]
}

if uac {
log.Print(command)
cmd := exec.Command(filepath.Join(env.root, "elevate.cmd"), command)
var output bytes.Buffer
var _stderr bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &_stderr
perr := cmd.Run()
if perr != nil {
return false, errors.New(fmt.Sprint(perr) + ": " + _stderr.String())
}
}

c := exec.Command("cmd") // dummy executable that actually needs to exist but we'll overwrite using .SysProcAttr

// Based on the official docs, syscall.SysProcAttr.CmdLine doesn't exist.
Expand All @@ -791,11 +841,15 @@ func runElevated(command string) bool {

err := c.Run()
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
return false
msg := stderr.String()
if strings.Contains(msg, "not have sufficient privilege") && uac {
return runElevated(command, false)
}
// fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
return false, errors.New(fmt.Sprint(err) + ": " + msg)
}

return true
return true, nil
}

func saveSettings() {
Expand Down

0 comments on commit 5751e53

Please sign in to comment.