From 6fa4b3e0bd41908a3b268625b36ba36a0005a833 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Wed, 15 Jan 2025 08:37:38 -0600 Subject: [PATCH 1/5] Allow use of bash as a script interpreter For #24470. --- changes/24470-bash | 1 + cmd/fleetctl/scripts_test.go | 4 ++-- .../Scripts/components/ScriptUploader/ScriptUploader.tsx | 2 +- orbit/pkg/scripts/exec_nonwindows_test.go | 7 +++++++ server/fleet/scripts.go | 6 +++--- server/fleet/scripts_test.go | 5 +++++ server/service/scripts_test.go | 5 ++++- 7 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 changes/24470-bash diff --git a/changes/24470-bash b/changes/24470-bash new file mode 100644 index 000000000000..7f856b78a8d4 --- /dev/null +++ b/changes/24470-bash @@ -0,0 +1 @@ +* Added bash interpreter support for script execution diff --git a/cmd/fleetctl/scripts_test.go b/cmd/fleetctl/scripts_test.go index 05ef4b39fdac..e20f0b7f8bf8 100644 --- a/cmd/fleetctl/scripts_test.go +++ b/cmd/fleetctl/scripts_test.go @@ -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", diff --git a/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/ScriptUploader.tsx b/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/ScriptUploader.tsx index ea034a7a09e8..607ae6e12abe 100644 --- a/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/ScriptUploader.tsx +++ b/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/ScriptUploader.tsx @@ -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} diff --git a/orbit/pkg/scripts/exec_nonwindows_test.go b/orbit/pkg/scripts/exec_nonwindows_test.go index a7997ebc8578..d795a9e6635e 100644 --- a/orbit/pkg/scripts/exec_nonwindows_test.go +++ b/orbit/pkg/scripts/exec_nonwindows_test.go @@ -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 { @@ -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[ -z \"$ZSH_VERSION\" ] && echo 1", + output: "1", + }, { name: "zsh shebang", contents: "#!" + zshPath + "\n[ -n \"$ZSH_VERSION\" ] && echo 1", diff --git a/server/fleet/scripts.go b/server/fleet/scripts.go index 1f1ebb915788..eddc1b0b193e 100644 --- a/server/fleet/scripts.go +++ b/server/fleet/scripts.go @@ -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 @@ -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 } diff --git a/server/fleet/scripts_test.go b/server/fleet/scripts_test.go index f8e92fdcee60..359098afa91d 100644 --- a/server/fleet/scripts_test.go +++ b/server/fleet/scripts_test.go @@ -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", diff --git a/server/service/scripts_test.go b/server/service/scripts_test.go index 91e711c584ee..505119240040 100644 --- a/server/service/scripts_test.go +++ b/server/service/scripts_test.go @@ -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}) From 41c6668b44845e032cf62cd00621a18528e88601 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Wed, 15 Jan 2025 09:02:13 -0600 Subject: [PATCH 2/5] Fix test --- server/fleet/scripts_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/fleet/scripts_test.go b/server/fleet/scripts_test.go index 359098afa91d..ee062816920f 100644 --- a/server/fleet/scripts_test.go +++ b/server/fleet/scripts_test.go @@ -148,7 +148,7 @@ func TestValidateHostScriptContents(t *testing.T) { }, { name: "unsupported interpreter", - script: "#!/bin/bash\necho 'hello'", + script: "#!/bin/ksh\necho 'hello'", wantErr: ErrUnsupportedInterpreter, }, { @@ -156,6 +156,16 @@ func TestValidateHostScriptContents(t *testing.T) { 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'", From f249cd6ad65853e48e11b010e339df720b46c363 Mon Sep 17 00:00:00 2001 From: dantecatalfamo Date: Wed, 22 Jan 2025 16:13:36 -0500 Subject: [PATCH 3/5] Make sure we're running in bash when we use shebang --- orbit/pkg/scripts/exec_nonwindows_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orbit/pkg/scripts/exec_nonwindows_test.go b/orbit/pkg/scripts/exec_nonwindows_test.go index d795a9e6635e..02657776c342 100644 --- a/orbit/pkg/scripts/exec_nonwindows_test.go +++ b/orbit/pkg/scripts/exec_nonwindows_test.go @@ -44,7 +44,7 @@ func TestExecCmdNonWindows(t *testing.T) { }, { name: "bash shebang", - contents: "#!" + bashPath + "\n[ -z \"$ZSH_VERSION\" ] && echo 1", + contents: "#!" + bashPath + "\n[ -n \"$BASH_VERSION\" ] && echo 1", output: "1", }, { From aced5a58abad5dee8bd6ea9e1002689e86b69684 Mon Sep 17 00:00:00 2001 From: dantecatalfamo Date: Thu, 23 Jan 2025 11:11:29 -0500 Subject: [PATCH 4/5] Update scripts guide --- articles/scripts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/scripts.md b/articles/scripts.md index 066c34abc6d3..82321d44b514 100644 --- a/articles/scripts.md +++ b/articles/scripts.md @@ -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. @@ -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. From 76f2fb93451c68be92e217479b560a11218118d5 Mon Sep 17 00:00:00 2001 From: dantecatalfamo Date: Thu, 23 Jan 2025 11:34:16 -0500 Subject: [PATCH 5/5] Update github action --- .github/workflows/build-orbit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-orbit.yaml b/.github/workflows/build-orbit.yaml index 002d2657f6ed..f0d1d250ab0f 100644 --- a/.github/workflows/build-orbit.yaml +++ b/.github/workflows/build-orbit.yaml @@ -73,7 +73,7 @@ jobs: ORBIT_COMMIT: ${{ github.sha }} - name: Upload orbit - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: orbit path: |