fix(datasets): accept BigTIFF headers and sanitise object keys
F1: ValidateFileContent rejected the pipeline's own output. GDAL writes a
BigTIFF header ("II+\0" / "MM\0+") whenever BIGTIFF=IF_SAFER kicks in for a
large raster, so accept both alongside the classic TIFF magic and reword the
error to "file is not a valid TIFF/BigTIFF GeoTIFF". GodalIO.Create now
delegates to an unexported createGTiff so a test can force BIGTIFF=YES and
assert the produced header passes the check.
F3: a user-supplied filename went straight into the S3 object key for dataset
uploads and for both dataset and dataset-group SLD uploads, so "../../x.sld"
escaped the dataset's own prefix. Reduce every such name to its base with a
shared safeObjectName helper, rejecting names with no usable base.
F9: rename the shadowing inner "raw" in the PATCH dg_id case to "s".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -226,7 +226,11 @@ func (s *DatasetService) Upload(ctx context.Context, in UploadInput) (domain.Dat
|
||||
status = domain.DatasetStatusParsing
|
||||
}
|
||||
|
||||
storageKey := fmt.Sprintf("%s/%s", uuid.New().String(), in.Filename)
|
||||
objectName, err := safeObjectName(in.Filename)
|
||||
if err != nil {
|
||||
return domain.Dataset{}, err
|
||||
}
|
||||
storageKey := fmt.Sprintf("%s/%s", uuid.New().String(), objectName)
|
||||
if err := s.store.Put(ctx, storageKey, content, in.Size, in.ContentType); err != nil {
|
||||
return domain.Dataset{}, err
|
||||
}
|
||||
@@ -297,7 +301,10 @@ func (s *DatasetService) UploadSLD(ctx context.Context, id uuid.UUID, in UploadS
|
||||
}
|
||||
content := io.MultiReader(bytes.NewReader(head), in.Reader)
|
||||
|
||||
key := deriveSLDKey(dataset.StorageKey, in.Filename)
|
||||
key, err := deriveSLDKey(dataset.StorageKey, in.Filename)
|
||||
if err != nil {
|
||||
return domain.Dataset{}, err
|
||||
}
|
||||
if err := s.store.Put(ctx, key, content, in.Size, in.ContentType); err != nil {
|
||||
return domain.Dataset{}, err
|
||||
}
|
||||
@@ -391,10 +398,27 @@ func (s *DatasetService) CreateRasterFromFile(ctx context.Context, in CreateRast
|
||||
})
|
||||
}
|
||||
|
||||
// safeObjectName reduces a user-supplied filename to a single path element so
|
||||
// it can never escape the object-key prefix it is appended to. Rejects names
|
||||
// that carry no usable base ("", ".", "..", "/").
|
||||
func safeObjectName(name string) (string, error) {
|
||||
base := path.Base(filepath.ToSlash(strings.TrimSpace(name)))
|
||||
switch base {
|
||||
case "", ".", "..", "/":
|
||||
return "", fmt.Errorf("%w: invalid filename", domain.ErrValidation)
|
||||
}
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// deriveSLDKey places a dataset's SLD style file alongside the original under
|
||||
// an sld/ prefix, named after the uploaded SLD file.
|
||||
func deriveSLDKey(storageKey, filename string) string {
|
||||
return path.Join(path.Dir(storageKey), "sld", filename)
|
||||
// an sld/ prefix, named after the uploaded SLD file. The filename is
|
||||
// sanitised, so the key always stays under the dataset's own prefix.
|
||||
func deriveSLDKey(storageKey, filename string) (string, error) {
|
||||
name, err := safeObjectName(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(path.Dir(storageKey), "sld", name), nil
|
||||
}
|
||||
|
||||
// enqueueProcessing schedules the appropriate async job for a dataset based on
|
||||
|
||||
Reference in New Issue
Block a user