check script existence on server side (#28647)

For #28558 

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [x] Added/updated automated tests
- [x] Manual QA for all new/changed functionality
- [x] For unreleased bug fixes in a release candidate, confirmed that
the fix is not expected to adversely impact load test results or alerted
the release DRI if additional load testing is needed.
This commit is contained in:
Jahziel Villasana-Espinoza 2025-04-29 17:27:08 -04:00 committed by GitHub
parent b17da22190
commit 968d33c0df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 7 deletions

View file

@ -1719,6 +1719,15 @@ func (svc *Service) softwareBatchUpload(
case ok:
// Perfect match: existing installer on the same team
installer.StorageID = p.SHA256
if foundInstaller.Extension == "exe" {
if p.InstallScript == "" {
return errors.New("Couldn't edit. Install script is required for .exe packages.")
}
if p.UninstallScript == "" {
return errors.New("Couldn't edit. Uninstall script is required for .exe packages.")
}
}
installer.Extension = foundInstaller.Extension
installer.Filename = foundInstaller.Filename
installer.Version = foundInstaller.Version

View file

@ -17075,16 +17075,26 @@ func (s *integrationEnterpriseTestSuite) TestBatchSoftwareUploadWithSHAs() {
var hitInstallerURL bool
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hitInstallerURL = true
if r.URL.Path != "/ruby.deb" && r.URL.Path != "/updated/ruby.deb" {
switch r.URL.Path {
case "/ruby.deb", "/updated/ruby.deb":
file, err := os.Open(filepath.Join("testdata", "software-installers", "ruby.deb"))
require.NoError(t, err)
defer file.Close()
w.Header().Set("Content-Type", "application/vnd.debian.binary-package")
_, err = io.Copy(w, file)
require.NoError(t, err)
case "/app.exe":
file, err := os.Open(filepath.Join("..", "..", "pkg", "file", "testdata", "software-installers", "hello-world-installer.exe"))
require.NoError(t, err)
defer file.Close()
w.Header().Set("Content-Type", "application/vnd.microsoft.portable-executable")
_, err = io.Copy(w, file)
require.NoError(t, err)
default:
w.WriteHeader(http.StatusNotFound)
return
}
file, err := os.Open(filepath.Join("testdata", "software-installers", "ruby.deb"))
require.NoError(t, err)
defer file.Close()
w.Header().Set("Content-Type", "application/vnd.debian.binary-package")
_, err = io.Copy(w, file)
require.NoError(t, err)
})
srv := httptest.NewServer(handler)
@ -17230,4 +17240,44 @@ func (s *integrationEnterpriseTestSuite) TestBatchSoftwareUploadWithSHAs() {
require.NotNil(t, packages[0].TeamID)
require.Equal(t, team2.ID, *packages[0].TeamID)
require.True(t, hitInstallerURL)
// add exe to payloads
exeURL := srv.URL + "/app.exe"
softwareToInstall = append(softwareToInstall, &fleet.SoftwareInstallerPayload{
URL: exeURL,
})
s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: softwareToInstall}, http.StatusAccepted, &batchResponse, "team_name", team2.Name)
errMsg = waitBatchSetSoftwareInstallersFailed(t, s, team2.Name, batchResponse.RequestUUID)
require.Contains(t, errMsg, "Couldn't add. Install script is required for .exe packages.")
softwareToInstall[1].InstallScript = "echo install"
softwareToInstall[1].UninstallScript = "echo uninstall"
s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: softwareToInstall}, http.StatusAccepted, &batchResponse, "team_name", team2.Name)
packages = waitBatchSetSoftwareInstallersCompleted(t, s, team2.Name, batchResponse.RequestUUID)
require.Len(t, packages, 2)
var exeHash string
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
return sqlx.GetContext(ctx, q, &exeHash, "SELECT storage_id FROM software_installers WHERE title_id = ?", packages[1].TitleID)
})
require.NotEmpty(t, exeHash)
// remove the URL and scripts, and add the hash. we should get an error because scripts are required.
softwareToInstall[1].InstallScript = ""
softwareToInstall[1].UninstallScript = ""
softwareToInstall[1].URL = ""
softwareToInstall[1].SHA256 = exeHash
s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: softwareToInstall}, http.StatusAccepted, &batchResponse, "team_name", team2.Name)
errMsg = waitBatchSetSoftwareInstallersFailed(t, s, team2.Name, batchResponse.RequestUUID)
require.Contains(t, errMsg, "Couldn't edit. Install script is required for .exe packages.")
// add the install script, but without uninstall script we should still fail
softwareToInstall[1].InstallScript = "echo install"
softwareToInstall[1].UninstallScript = ""
softwareToInstall[1].URL = ""
softwareToInstall[1].SHA256 = exeHash
s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: softwareToInstall}, http.StatusAccepted, &batchResponse, "team_name", team2.Name)
errMsg = waitBatchSetSoftwareInstallersFailed(t, s, team2.Name, batchResponse.RequestUUID)
require.Contains(t, errMsg, "Couldn't edit. Uninstall script is required for .exe packages.")
}