Base64 encode powershell to avoid any necessary escaping

This commit is contained in:
Taliesin Sisson 2016-07-04 23:44:33 +01:00
parent 52c6cd5fc1
commit 17597b48e1
4 changed files with 83 additions and 40 deletions

View File

@ -101,7 +101,7 @@ func IsPowershellAvailable() (bool, string, error) {
if err != nil {
return false, "", err
} else {
return false, path, err
return true, path, err
}
}

View File

@ -15,3 +15,9 @@ func powershellEncode(buffer []byte) string {
input := []uint8(wideCmd)
return base64.StdEncoding.EncodeToString(input)
}
func powershellDecode(message string) (retour string) {
base64Text := make([]byte, base64.StdEncoding.DecodedLen(len(message)))
base64.StdEncoding.Decode(base64Text, []byte(message))
return string(base64Text)
}

View File

@ -107,24 +107,25 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
},
},
}, raws...)
if err != nil {
return err
}
if p.config.EnvVarFormat == "" {
p.config.EnvVarFormat = `$env:%s=\"%s\"; `
p.config.EnvVarFormat = `$env:%s="%s"; `
}
if p.config.ElevatedEnvVarFormat == "" {
p.config.ElevatedEnvVarFormat = `$env:%s=\"%s\"; `
p.config.ElevatedEnvVarFormat = `$env:%s="%s"; `
}
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; {{.Vars}}{{.Path}}; exit $LastExitCode}'`
p.config.ExecuteCommand = `{{.Vars}}{{.Path}}`
}
if p.config.ElevatedExecuteCommand == "" {
p.config.ElevatedExecuteCommand = `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; {{.Vars}}{{.Path}}; exit $LastExitCode}'`
p.config.ElevatedExecuteCommand = `{{.Vars}}{{.Path}}'`
}
if p.config.Inline != nil && len(p.config.Inline) == 0 {
@ -374,28 +375,41 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string, e
}
func (p *Provisioner) createCommandText() (command string, err error) {
// Return the interpolated command
if p.config.ElevatedUser == "" {
return p.createCommandTextNonPrivileged()
} else {
return p.createCommandTextPrivileged()
}
}
func (p *Provisioner) createCommandTextNonPrivileged() (command string, err error) {
// Create environment variables to set before executing the command
flattenedEnvVars, err := p.createFlattenedEnvVars(false)
if err != nil {
return "", err
}
p.config.ctx.Data = &ExecuteCommandTemplate{
Vars: flattenedEnvVars,
Path: p.config.RemotePath,
}
command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return "", fmt.Errorf("Error processing command: %s", err)
}
// Return the interpolated command
if p.config.ElevatedUser == "" {
return command, nil
}
encodedCommand := "powershell -executionpolicy bypass -encodedCommand " + powershellEncode([]byte("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; "+command+"; exit $LastExitCode"))
return encodedCommand, err
}
func (p *Provisioner) createCommandTextPrivileged() (command string, err error) {
// Can't double escape the env vars, lets create shiny new ones
flattenedEnvVars, err = p.createFlattenedEnvVars(true)
flattenedEnvVars, err := p.createFlattenedEnvVars(true)
if err != nil {
return "", err
}
p.config.ctx.Data = &ExecuteCommandTemplate{
Vars: flattenedEnvVars,
Path: p.config.RemotePath,
@ -412,7 +426,7 @@ func (p *Provisioner) createCommandText() (command string, err error) {
// Return the path to the elevated shell wrapper
command = fmt.Sprintf("powershell -executionpolicy bypass -file \"%s\"", path)
return
return command, err
}
func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath string, err error) {
@ -425,7 +439,7 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin
Password: p.config.ElevatedPassword,
TaskDescription: "Packer elevated task",
TaskName: fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()),
EncodedCommand: powershellEncode([]byte("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; " + command + "; exit $LASTEXITCODE")),
EncodedCommand: powershellEncode([]byte("if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; " + command + "; exit $LastExitCode")),
})
if err != nil {

View File

@ -75,12 +75,12 @@ func TestProvisionerPrepare_Defaults(t *testing.T) {
t.Error("expected elevated_password to be empty")
}
if p.config.ExecuteCommand != `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; {{.Vars}}{{.Path}}; exit $LastExitCode}'` {
t.Fatalf("Default command should be powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; {{.Vars}}{{.Path}}; exit $LastExitCode}', but got %s", p.config.ExecuteCommand)
if p.config.ExecuteCommand != `{{.Vars}}{{.Path}}` {
t.Fatalf("Default command should be '{{.Vars}}{{.Path}}', but got %s", p.config.ExecuteCommand)
}
if p.config.ElevatedExecuteCommand != `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; {{.Vars}}{{.Path}}; exit $LastExitCode}'` {
t.Fatalf("Default command should be powershell powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; {{.Vars}}{{.Path}}; exit $LastExitCode}', but got %s", p.config.ElevatedExecuteCommand)
if p.config.ElevatedExecuteCommand != `{{.Vars}}{{.Path}}'` {
t.Fatalf("Default command should be '{{.Vars}}{{.Path}}', but got %s", p.config.ElevatedExecuteCommand)
}
if p.config.ValidExitCodes == nil {
@ -95,8 +95,8 @@ func TestProvisionerPrepare_Defaults(t *testing.T) {
}
}
if p.config.ElevatedEnvVarFormat != `$env:%s=\"%s\"; ` {
t.Fatalf(`Default command should be powershell '$env:%%s=\"%%s\"; ', but got %s`, p.config.ElevatedEnvVarFormat)
if p.config.ElevatedEnvVarFormat != `$env:%s="%s"; ` {
t.Fatalf(`Default command should be powershell '$env:%%s="%%s"; ', but got %s`, p.config.ElevatedEnvVarFormat)
}
}
@ -389,11 +389,15 @@ func TestProvisionerProvision_Inline(t *testing.T) {
t.Fatal("should not have error")
}
expectedCommand := `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode}'`
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommand {
t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
}
envVars := make([]string, 2)
@ -408,11 +412,15 @@ func TestProvisionerProvision_Inline(t *testing.T) {
t.Fatal("should not have error")
}
expectedCommand = `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; $env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode}'`
expectedCommand = `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:BAR="BAZ"; $env:FOO="BAR"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode`
expectedCommandPrefix = `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded = expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommand {
t.Fatalf("Expect command to be: %s, got: %s", expectedCommand, comm.StartCmd.Command)
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
}
}
@ -434,12 +442,15 @@ func TestProvisionerProvision_Scripts(t *testing.T) {
t.Fatal("should not have error")
}
//powershell -Command "$env:PACKER_BUILDER_TYPE=''"; powershell -Command "$env:PACKER_BUILD_NAME='foobuild'"; powershell -Command c:/Windows/Temp/script.ps1
expectedCommand := `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}'`
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:PACKER_BUILDER_TYPE="footype"; $env:PACKER_BUILD_NAME="foobuild"; c:/Windows/Temp/script.ps1; exit $LastExitCode`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommand {
t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command)
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
}
}
@ -468,11 +479,15 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) {
t.Fatal("should not have error")
}
expectedCommand := `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; $env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}'`
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:BAR="BAZ"; $env:FOO="BAR"; $env:PACKER_BUILDER_TYPE="footype"; $env:PACKER_BUILD_NAME="foobuild"; c:/Windows/Temp/script.ps1; exit $LastExitCode`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
// Should run the command without alteration
if comm.StartCmd.Command != expectedCommand {
t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command)
if comm.StartCmd.Command != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
}
}
@ -500,7 +515,7 @@ func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) {
if err != nil {
t.Fatalf("should not have error creating flattened env vars: %s", err)
}
if flattenedEnvVars != `$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ` {
if flattenedEnvVars != `$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; ` {
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
}
@ -511,7 +526,7 @@ func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) {
if err != nil {
t.Fatalf("should not have error creating flattened env vars: %s", err)
}
if flattenedEnvVars != `$env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ` {
if flattenedEnvVars != `$env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; ` {
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
}
@ -522,7 +537,7 @@ func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) {
if err != nil {
t.Fatalf("should not have error creating flattened env vars: %s", err)
}
if flattenedEnvVars != `$env:BAZ=\"qux\"; $env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ` {
if flattenedEnvVars != `$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; ` {
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
}
}
@ -545,7 +560,7 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
if err != nil {
t.Fatalf("should not have error creating flattened env vars: %s", err)
}
if flattenedEnvVars != `$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ` {
if flattenedEnvVars != `$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; ` {
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
}
@ -556,7 +571,7 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
if err != nil {
t.Fatalf("should not have error creating flattened env vars: %s", err)
}
if flattenedEnvVars != `$env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ` {
if flattenedEnvVars != `$env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; ` {
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
}
@ -567,7 +582,7 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
if err != nil {
t.Fatalf("should not have error creating flattened env vars: %s", err)
}
if flattenedEnvVars != `$env:BAZ=\"qux\"; $env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ` {
if flattenedEnvVars != `$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; ` {
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
}
}
@ -582,8 +597,16 @@ func TestProvision_createCommandText(t *testing.T) {
// Non-elevated
cmd, _ := p.createCommandText()
if cmd != `powershell '& {if (Test-Path variable:global:ProgressPreference){$ProgressPreference=\"SilentlyContinue\"}; $env:PACKER_BUILDER_TYPE=\"\"; $env:PACKER_BUILD_NAME=\"\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}'` {
t.Fatalf("Got unexpected non-elevated command: %s", cmd)
expectedCommand := `if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"}; $env:PACKER_BUILDER_TYPE=""; $env:PACKER_BUILD_NAME=""; c:/Windows/Temp/script.ps1; exit $LastExitCode`
expectedCommandPrefix := `powershell -executionpolicy bypass -encodedCommand `
expectedCommandEncoded := expectedCommandPrefix + powershellEncode([]byte(expectedCommand))
// Should run the command without alteration
if cmd != expectedCommandEncoded {
actualCommandWithoutPrefix := strings.Replace(comm.StartCmd.Command, expectedCommandPrefix, "", -1)
actualCommandDecoded := powershellDecode(actualCommandWithoutPrefix)
t.Fatalf("Expect command to be: %s, got %s. Expected decoded: %s, got %s", expectedCommandEncoded, comm.StartCmd.Command, expectedCommand, actualCommandDecoded)
}
// Elevated