restricting icon size to 100kb. (#32834)
fixes https://github.com/fleetdm/fleet/issues/32826 ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [x] Confirmed that the fix is not expected to adversely impact load test results - [x] Alerted the release DRI if additional load testing is needed
This commit is contained in:
+3
-1
@@ -5,6 +5,7 @@ import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
||||
import { IAppStoreApp, ISoftwarePackage } from "interfaces/software";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { getErrorReason } from "interfaces/errors";
|
||||
import softwareAPI from "services/entities/software";
|
||||
|
||||
import Modal from "components/Modal";
|
||||
@@ -529,7 +530,8 @@ const EditIconModal = ({
|
||||
setIconUploadedAt(new Date().toISOString());
|
||||
onExitEditIconModal();
|
||||
} catch (e) {
|
||||
renderFlash("error", DEFAULT_ERROR_MESSAGE);
|
||||
const errorMessage = getErrorReason(e) || DEFAULT_ERROR_MESSAGE;
|
||||
renderFlash("error", errorMessage);
|
||||
} finally {
|
||||
setIsUpdatingIcon(false);
|
||||
}
|
||||
|
||||
@@ -12226,6 +12226,47 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareTitleIcons() {
|
||||
require.Equal(t, iconUrl, *details.SoftwareIconURL)
|
||||
require.Equal(t, titleID, details.SoftwareTitleID)
|
||||
|
||||
// PUT: Icon too large
|
||||
// Create a fake large file (101KB of data)
|
||||
largeData := make([]byte, 101*1024)
|
||||
reader := bytes.NewReader(largeData)
|
||||
iconFile, err := fleet.NewTempFileReader(reader, func() string { return t.TempDir() })
|
||||
require.NoError(t, err)
|
||||
|
||||
var bufInvalid bytes.Buffer
|
||||
writer = multipart.NewWriter(&bufInvalid)
|
||||
fileWriter, err = writer.CreateFormFile("icon", "icon.png")
|
||||
require.NoError(t, err)
|
||||
_, err = io.Copy(fileWriter, iconFile)
|
||||
require.NoError(t, err)
|
||||
err = writer.Close()
|
||||
require.NoError(t, err)
|
||||
headers = map[string]string{
|
||||
"Content-Type": writer.FormDataContentType(),
|
||||
"Authorization": fmt.Sprintf("Bearer %s", s.token),
|
||||
}
|
||||
resp = s.DoRawWithHeaders(
|
||||
"PUT",
|
||||
fmt.Sprintf("/api/latest/fleet/software/titles/%d/icon?team_id=%d", titleID, tm.ID),
|
||||
bufInvalid.Bytes(),
|
||||
http.StatusBadRequest,
|
||||
headers,
|
||||
)
|
||||
var errorResp struct {
|
||||
Message string `json:"message"`
|
||||
Errors []struct {
|
||||
Name string `json:"name"`
|
||||
Reason string `json:"reason"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(body, &errorResp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Bad request", errorResp.Message)
|
||||
require.Len(t, errorResp.Errors, 1)
|
||||
assert.Contains(t, errorResp.Errors[0].Reason, "icon must be less than 100KB")
|
||||
|
||||
// PUT: gitops workflow, passing in sha256 & filename
|
||||
var storedIcons []fleet.SoftwareTitleIcon
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
|
||||
@@ -171,6 +171,19 @@ func (putSoftwareTitleIconRequest) DecodeRequest(ctx context.Context, r *http.Re
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the file if one is provided
|
||||
if decoded.File != nil {
|
||||
file, err := decoded.File.Open()
|
||||
if err != nil {
|
||||
return nil, &fleet.BadRequestError{Message: "failed to open file"}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := iconValidator(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &decoded, nil
|
||||
}
|
||||
|
||||
@@ -189,12 +202,6 @@ func putSoftwareTitleIconEndpoint(ctx context.Context, request interface{}, svc
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// ensure icon is png and fits sizing restrictions
|
||||
err = iconValidator(file)
|
||||
if err != nil {
|
||||
return putSoftwareTitleIconResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
tfr, err := fleet.NewTempFileReader(file, nil)
|
||||
if err != nil {
|
||||
return putSoftwareTitleIconResponse{Err: err}, nil
|
||||
@@ -228,6 +235,20 @@ func (svc *Service) UploadSoftwareTitleIcon(ctx context.Context, payload *fleet.
|
||||
}
|
||||
|
||||
func iconValidator(file multipart.File) error {
|
||||
// Check file size first
|
||||
fileSize, err := file.Seek(0, 2) // Seek to end to get size
|
||||
if err != nil {
|
||||
return &fleet.BadRequestError{Message: "failed to read file size"}
|
||||
}
|
||||
if _, err := file.Seek(0, 0); err != nil { // Reset to beginning
|
||||
return &fleet.BadRequestError{Message: "failed to rewind file"}
|
||||
}
|
||||
|
||||
maxSize := int64(100 * 1024) // 100KB
|
||||
if fileSize > maxSize {
|
||||
return &fleet.BadRequestError{Message: "icon must be less than 100KB"}
|
||||
}
|
||||
|
||||
config, format, err := image.DecodeConfig(file)
|
||||
if err != nil || format != "png" {
|
||||
return &fleet.BadRequestError{Message: "icon must be a PNG image"}
|
||||
|
||||
Reference in New Issue
Block a user