Merge pull request #5376 from DanHam/ps-escapes
[WIP] Escape of chars special to PowerShell in user data
This commit is contained in:
commit
ba518637d4
|
@ -34,6 +34,7 @@ func init() {
|
||||||
"amazon-shutdown_behavior": new(FixerAmazonShutdownBehavior),
|
"amazon-shutdown_behavior": new(FixerAmazonShutdownBehavior),
|
||||||
"amazon-enhanced-networking": new(FixerAmazonEnhancedNetworking),
|
"amazon-enhanced-networking": new(FixerAmazonEnhancedNetworking),
|
||||||
"docker-email": new(FixerDockerEmail),
|
"docker-email": new(FixerDockerEmail),
|
||||||
|
"powershell-escapes": new(FixerPowerShellEscapes),
|
||||||
}
|
}
|
||||||
|
|
||||||
FixerOrder = []string{
|
FixerOrder = []string{
|
||||||
|
@ -51,5 +52,6 @@ func init() {
|
||||||
"amazon-shutdown_behavior",
|
"amazon-shutdown_behavior",
|
||||||
"amazon-enhanced-networking",
|
"amazon-enhanced-networking",
|
||||||
"docker-email",
|
"docker-email",
|
||||||
|
"powershell-escapes",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package fix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FixerPowerShellEscapes removes the PowerShell escape character from user
|
||||||
|
// environment variables and elevated username and password strings
|
||||||
|
type FixerPowerShellEscapes struct{}
|
||||||
|
|
||||||
|
func (FixerPowerShellEscapes) Fix(input map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
type template struct {
|
||||||
|
Provisioners []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var psUnescape = strings.NewReplacer(
|
||||||
|
"`$", "$",
|
||||||
|
"`\"", "\"",
|
||||||
|
"``", "`",
|
||||||
|
"`'", "'",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode the input into our structure, if we can
|
||||||
|
var tpl template
|
||||||
|
if err := mapstructure.WeakDecode(input, &tpl); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, raw := range tpl.Provisioners {
|
||||||
|
var provisioners map[string]interface{}
|
||||||
|
if err := mapstructure.Decode(raw, &provisioners); err != nil {
|
||||||
|
// Ignore errors, could be a non-map
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := provisioners["type"] == "powershell"; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := provisioners["elevated_user"]; ok {
|
||||||
|
provisioners["elevated_user"] = psUnescape.Replace(provisioners["elevated_user"].(string))
|
||||||
|
}
|
||||||
|
if _, ok := provisioners["elevated_password"]; ok {
|
||||||
|
provisioners["elevated_password"] = psUnescape.Replace(provisioners["elevated_password"].(string))
|
||||||
|
}
|
||||||
|
if raw, ok := provisioners["environment_vars"]; ok {
|
||||||
|
var env_vars []string
|
||||||
|
if err := mapstructure.Decode(raw, &env_vars); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
env_vars_unescaped := make([]interface{}, len(env_vars))
|
||||||
|
for j, env_var := range env_vars {
|
||||||
|
env_vars_unescaped[j] = psUnescape.Replace(env_var)
|
||||||
|
}
|
||||||
|
// Replace with unescaped environment variables
|
||||||
|
provisioners["environment_vars"] = env_vars_unescaped
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write all changes back to template
|
||||||
|
tpl.Provisioners[i] = provisioners
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tpl.Provisioners) > 0 {
|
||||||
|
input["provisioners"] = tpl.Provisioners
|
||||||
|
}
|
||||||
|
|
||||||
|
return input, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (FixerPowerShellEscapes) Synopsis() string {
|
||||||
|
return `Removes PowerShell escapes from user env vars and elevated username and password strings`
|
||||||
|
}
|
|
@ -24,6 +24,13 @@ import (
|
||||||
|
|
||||||
var retryableSleep = 2 * time.Second
|
var retryableSleep = 2 * time.Second
|
||||||
|
|
||||||
|
var psEscape = strings.NewReplacer(
|
||||||
|
"$", "`$",
|
||||||
|
"\"", "`\"",
|
||||||
|
"`", "``",
|
||||||
|
"'", "`'",
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
@ -359,7 +366,13 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) {
|
||||||
// Split vars into key/value components
|
// Split vars into key/value components
|
||||||
for _, envVar := range p.config.Vars {
|
for _, envVar := range p.config.Vars {
|
||||||
keyValue := strings.SplitN(envVar, "=", 2)
|
keyValue := strings.SplitN(envVar, "=", 2)
|
||||||
envVars[keyValue[0]] = keyValue[1]
|
// Escape chars special to PS in each env var value
|
||||||
|
escapedEnvVarValue := psEscape.Replace(keyValue[1])
|
||||||
|
if escapedEnvVarValue != keyValue[1] {
|
||||||
|
log.Printf("Env var %s converted to %s after escaping chars special to PS", keyValue[1],
|
||||||
|
escapedEnvVarValue)
|
||||||
|
}
|
||||||
|
envVars[keyValue[0]] = escapedEnvVarValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a list of env var keys in sorted order
|
// Create a list of env var keys in sorted order
|
||||||
|
@ -480,13 +493,26 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin
|
||||||
}
|
}
|
||||||
escapedCommand := buffer.String()
|
escapedCommand := buffer.String()
|
||||||
log.Printf("Command [%s] converted to [%s] for use in XML string", command, escapedCommand)
|
log.Printf("Command [%s] converted to [%s] for use in XML string", command, escapedCommand)
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
|
|
||||||
|
// Escape chars special to PowerShell in the ElevatedUser string
|
||||||
|
escapedElevatedUser := psEscape.Replace(p.config.ElevatedUser)
|
||||||
|
if escapedElevatedUser != p.config.ElevatedUser {
|
||||||
|
log.Printf("Elevated user %s converted to %s after escaping chars special to PowerShell",
|
||||||
|
p.config.ElevatedUser, escapedElevatedUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape chars special to PowerShell in the ElevatedPassword string
|
||||||
|
escapedElevatedPassword := psEscape.Replace(p.config.ElevatedPassword)
|
||||||
|
if escapedElevatedPassword != p.config.ElevatedPassword {
|
||||||
|
log.Printf("Elevated password %s converted to %s after escaping chars special to PowerShell",
|
||||||
|
p.config.ElevatedPassword, escapedElevatedPassword)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate command
|
// Generate command
|
||||||
err = elevatedTemplate.Execute(&buffer, elevatedOptions{
|
err = elevatedTemplate.Execute(&buffer, elevatedOptions{
|
||||||
User: p.config.ElevatedUser,
|
User: escapedElevatedUser,
|
||||||
Password: p.config.ElevatedPassword,
|
Password: escapedElevatedPassword,
|
||||||
TaskName: taskName,
|
TaskName: taskName,
|
||||||
TaskDescription: "Packer elevated task",
|
TaskDescription: "Packer elevated task",
|
||||||
LogFile: logFile,
|
LogFile: logFile,
|
||||||
|
|
|
@ -518,6 +518,12 @@ func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) {
|
||||||
{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
|
{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
|
||||||
{"FOO=bar=baz"}, // User env var with value containing equals
|
{"FOO=bar=baz"}, // User env var with value containing equals
|
||||||
{"FOO==bar"}, // User env var with value starting with equals
|
{"FOO==bar"}, // User env var with value starting with equals
|
||||||
|
// Test escaping of characters special to PowerShell
|
||||||
|
{"FOO=bar$baz"}, // User env var with value containing dollar
|
||||||
|
{"FOO=bar\"baz"}, // User env var with value containing a double quote
|
||||||
|
{"FOO=bar'baz"}, // User env var with value containing a single quote
|
||||||
|
{"FOO=bar`baz"}, // User env var with value containing a backtick
|
||||||
|
|
||||||
}
|
}
|
||||||
expected := []string{
|
expected := []string{
|
||||||
`$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
|
@ -525,6 +531,10 @@ func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) {
|
||||||
`$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
`$env:FOO="bar=baz"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:FOO="bar=baz"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
`$env:FOO="=bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:FOO="=bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
|
"$env:FOO=\"bar`$baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
|
"$env:FOO=\"bar`\"baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
|
"$env:FOO=\"bar`'baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
|
"$env:FOO=\"bar``baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
}
|
}
|
||||||
|
|
||||||
p := new(Provisioner)
|
p := new(Provisioner)
|
||||||
|
@ -553,6 +563,11 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
|
||||||
{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
|
{"FOO=bar", "BAZ=qux"}, // Multiple user env vars
|
||||||
{"FOO=bar=baz"}, // User env var with value containing equals
|
{"FOO=bar=baz"}, // User env var with value containing equals
|
||||||
{"FOO==bar"}, // User env var with value starting with equals
|
{"FOO==bar"}, // User env var with value starting with equals
|
||||||
|
// Test escaping of characters special to PowerShell
|
||||||
|
{"FOO=bar$baz"}, // User env var with value containing dollar
|
||||||
|
{"FOO=bar\"baz"}, // User env var with value containing a double quote
|
||||||
|
{"FOO=bar'baz"}, // User env var with value containing a single quote
|
||||||
|
{"FOO=bar`baz"}, // User env var with value containing a backtick
|
||||||
}
|
}
|
||||||
expected := []string{
|
expected := []string{
|
||||||
`$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
|
@ -560,6 +575,10 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
|
||||||
`$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
`$env:FOO="bar=baz"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:FOO="bar=baz"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
`$env:FOO="=bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
`$env:FOO="=bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `,
|
||||||
|
"$env:FOO=\"bar`$baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
|
"$env:FOO=\"bar`\"baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
|
"$env:FOO=\"bar`'baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
|
"$env:FOO=\"bar``baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; ",
|
||||||
}
|
}
|
||||||
|
|
||||||
p := new(Provisioner)
|
p := new(Provisioner)
|
||||||
|
|
Loading…
Reference in New Issue