Skip to content

Commit

Permalink
Allow use of bash as a script interpreter (#25449)
Browse files Browse the repository at this point in the history
For #24470.

---------

Co-authored-by: dantecatalfamo <[email protected]>
Co-authored-by: Dante Catalfamo <[email protected]>
  • Loading branch information
3 people authored Feb 4, 2025
1 parent 53afcec commit 805a0e9
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 11 deletions.
6 changes: 3 additions & 3 deletions articles/scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

In Fleet you can run custom scripts to remediate an issue on your macOS, Windows, and Linux hosts.

Shell scripts are supported on macOS and Linux. By default, shell scripts will run in the host's (root) shell (`/bin/sh`). We also support `/bin/zsh` interpreter.
Note: To run in `/bin/zsh`, create `.sh` file (only supported extension) and add an interpreter at the first line.
Shell scripts are supported on macOS and Linux. By default, shell scripts will run in the host's (root) shell (`/bin/sh`). We also support `/bin/zsh` and `/bin/bash` interpreters.
Note: To run in `/bin/zsh` or `/bin/bash`, create `.sh` file (only supported extension) and add an interpreter at the first line.

PowerShell scripts are supported on Windows. Other types of scripts are not supported yet.

Expand Down Expand Up @@ -32,7 +32,7 @@ Fleet UI:
2. Head to the **Hosts** page and select the host you want to run the script on.

3. On your target host's host details page, select the **Actions** dropdown and select **Run Script** to view the **Run Script** menu.

4. In the **Run Script** menu, select the **Actions** dropdown for the script you'd like to execute and choose the **Run** option.

Scripts run from the Fleet UI will run the next time your host checks in with Fleet. You can view the status of the script execution as well as the output in the target host's activity feed.
Expand Down
1 change: 1 addition & 0 deletions changes/24470-bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added bash interpreter support for script execution
4 changes: 2 additions & 2 deletions cmd/fleetctl/scripts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ hello world
{
name: "invalid hashbang",
scriptPath: func() string { return writeTmpScriptContents(t, "#! /foo/bar", ".sh") },
expectErrMsg: `Interpreter not supported. Shell scripts must run in "#!/bin/sh" or "#!/bin/zsh."`,
expectErrMsg: `Interpreter not supported. Shell scripts must run in "#!/bin/sh", "#!/bin/bash", or "#!/bin/zsh."`,
},
{
name: "unsupported hashbang",
scriptPath: func() string { return writeTmpScriptContents(t, "#!/bin/ksh", ".sh") },
expectErrMsg: `Interpreter not supported. Shell scripts must run in "#!/bin/sh" or "#!/bin/zsh."`,
expectErrMsg: `Interpreter not supported. Shell scripts must run in "#!/bin/sh", "#!/bin/bash", or "#!/bin/zsh."`,
},
{
name: "posix shell hashbang",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const ScriptPackageUploader = ({
className={baseClass}
graphicName={["file-sh", "file-ps1"]}
message="Shell (.sh) for macOS and Linux or PowerShell (.ps1) for Windows"
additionalInfo="Script will run with “#!/bin/sh” or “#!/bin/zsh” on macOS and Linux."
additionalInfo="Script will run with “#!/bin/sh”, “#!/bin/zsh”, or “#!/bin/bash” on macOS and Linux."
accept=".sh,.ps1"
onFileUpload={onUploadFile}
isLoading={showLoading}
Expand Down
1 change: 1 addition & 0 deletions orbit/changes/24470-bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added bash interpreter support for script execution
7 changes: 7 additions & 0 deletions orbit/pkg/scripts/exec_nonwindows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import (

func TestExecCmdNonWindows(t *testing.T) {
zshPath := "/bin/zsh"
bashPath := "/bin/bash"
if runtime.GOOS == "linux" {
zshPath = "/usr/bin/zsh"
bashPath = "/usr/bin/bash"
}

tests := []struct {
Expand All @@ -40,6 +42,11 @@ func TestExecCmdNonWindows(t *testing.T) {
contents: "#!/bin/sh\n[ -z \"$ZSH_VERSION\" ] && echo 1",
output: "1",
},
{
name: "bash shebang",
contents: "#!" + bashPath + "\n[ -n \"$BASH_VERSION\" ] && echo 1",
output: "1",
},
{
name: "zsh shebang",
contents: "#!" + zshPath + "\n[ -n \"$ZSH_VERSION\" ] && echo 1",
Expand Down
6 changes: 3 additions & 3 deletions server/fleet/scripts.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ const (

// anchored, so that it matches to the end of the line
var (
scriptHashbangValidation = regexp.MustCompile(`^#!\s*(:?/usr)?/bin/z?sh(?:\s*|\s+.*)$`)
ErrUnsupportedInterpreter = errors.New(`Interpreter not supported. Shell scripts must run in "#!/bin/sh" or "#!/bin/zsh."`)
scriptHashbangValidation = regexp.MustCompile(`^#!\s*(:?/usr)?/bin/(ba|z)?sh(?:\s*|\s+.*)$`)
ErrUnsupportedInterpreter = errors.New(`Interpreter not supported. Shell scripts must run in "#!/bin/sh", "#!/bin/bash", or "#!/bin/zsh."`)
)

// ValidateShebang validates if we support a script, and whether we
Expand All @@ -327,7 +327,7 @@ func ValidateShebang(s string) (directExecute bool, err error) {
if strings.HasPrefix(s, "#!") {
// read the first line in a portable way
s := bufio.NewScanner(strings.NewReader(s))
// if a hashbang is present, it can only be `/bin/sh` or `(/usr)/bin/zsh` for now
// if a hashbang is present, it can only be `(/usr)/bin/sh`, `(/usr)/bin/bash`, `(/usr)/bin/zsh` for now
if s.Scan() && !scriptHashbangValidation.MatchString(s.Text()) {
return false, ErrUnsupportedInterpreter
}
Expand Down
17 changes: 16 additions & 1 deletion server/fleet/scripts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ func TestValidateShebang(t *testing.T) {
contents: "#!/bin/zsh\necho hi",
directExecute: true,
},
{
name: "bash shebang",
contents: "#!/bin/bash\necho hi",
directExecute: true,
},
{
name: "zsh shebang with args",
contents: "#!/bin/zsh -x\necho hi",
Expand Down Expand Up @@ -143,14 +148,24 @@ func TestValidateHostScriptContents(t *testing.T) {
},
{
name: "unsupported interpreter",
script: "#!/bin/bash\necho 'hello'",
script: "#!/bin/ksh\necho 'hello'",
wantErr: ErrUnsupportedInterpreter,
},
{
name: "valid script",
script: "#!/bin/sh\necho 'hello'",
wantErr: nil,
},
{
name: "valid bash script",
script: "#!/bin/bash\necho 'hello'",
wantErr: nil,
},
{
name: "valid bash script",
script: "#!/usr/bin/bash\necho 'hello'",
wantErr: nil,
},
{
name: "valid zsh script",
script: "#!/bin/zsh\necho 'hello'",
Expand Down
5 changes: 4 additions & 1 deletion server/service/scripts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,15 @@ func TestHostRunScript(t *testing.T) {
{"invalid utf8", "\xff\xfa", "Wrong data format."},
{"valid without hashbang", "echo 'a'", ""},
{"valid with posix hashbang", "#!/bin/sh\necho 'a'", ""},
{"valid with usr bash hashbang", "#!/usr/bin/bash\necho 'a'", ""},
{"valid with bash hashbang", "#!/bin/bash\necho 'a'", ""},
{"valid with bash hashbang and arguments", "#!/bin/bash -x\necho 'a'", ""},
{"valid with usr zsh hashbang", "#!/usr/bin/zsh\necho 'a'", ""},
{"valid with zsh hashbang", "#!/bin/zsh\necho 'a'", ""},
{"valid with zsh hashbang and arguments", "#!/bin/zsh -x\necho 'a'", ""},
{"valid with hashbang and spacing", "#! /bin/sh \necho 'a'", ""},
{"valid with hashbang and Windows newline", "#! /bin/sh \r\necho 'a'", ""},
{"invalid hashbang", "#!/bin/bash\necho 'a'", "Interpreter not supported."},
{"invalid hashbang", "#!/bin/ksh\necho 'a'", "Interpreter not supported."},
}

ctx = viewer.NewContext(ctx, viewer.Viewer{User: test.UserAdmin})
Expand Down

0 comments on commit 805a0e9

Please sign in to comment.