Compare commits

...

2 Commits

Author SHA1 Message Date
snyk-bot
971c746b50 fix: frontend/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-15965856
2026-04-10 14:28:15 +00:00
Vlad Mocanu
626adbf14c fix(storage): strip Root prefix from S3 List() returned paths (#1413) 2026-04-08 19:01:34 +00:00
3 changed files with 53 additions and 2 deletions

View File

@@ -138,7 +138,7 @@ func (s *s3Storage) List(ctx context.Context, path string) ([]ObjectInfo, error)
continue
}
objects = append(objects, ObjectInfo{
Path: aws.ToString(obj.Key),
Path: s.pathFromKey(aws.ToString(obj.Key)),
Size: aws.ToInt64(obj.Size),
ModTime: aws.ToTime(obj.LastModified),
})
@@ -147,6 +147,13 @@ func (s *s3Storage) List(ctx context.Context, path string) ([]ObjectInfo, error)
return objects, nil
}
func (s *s3Storage) pathFromKey(key string) string {
if s.prefix == "" {
return key
}
return strings.TrimPrefix(key, s.prefix+"/")
}
func (s *s3Storage) Walk(ctx context.Context, root string, fn func(ObjectInfo) error) error {
objects, err := s.List(ctx, root)
if err != nil {

View File

@@ -35,6 +35,50 @@ func TestS3Helpers(t *testing.T) {
}
})
t.Run("pathFromKey strips prefix to honor relative-path contract", func(t *testing.T) {
tests := []struct {
name string
prefix string
key string
expected string
}{
{name: "no prefix returns key unchanged", prefix: "", key: "images/logo.png", expected: "images/logo.png"},
{name: "no prefix empty key", prefix: "", key: "", expected: ""},
{name: "prefix matches and is stripped", prefix: "data/uploads", key: "data/uploads/application-images/logo.svg", expected: "application-images/logo.svg"},
{name: "single-segment prefix stripped", prefix: "root", key: "root/foo/bar.txt", expected: "foo/bar.txt"},
{name: "prefix equal to key without trailing slash is unchanged", prefix: "root", key: "root", expected: "root"},
{name: "key without expected prefix returned unchanged", prefix: "data/uploads", key: "other/path.txt", expected: "other/path.txt"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
s := &s3Storage{
bucket: "bucket",
prefix: tc.prefix,
}
assert.Equal(t, tc.expected, s.pathFromKey(tc.key))
})
}
})
t.Run("pathFromKey is the inverse of buildObjectKey for clean paths", func(t *testing.T) {
paths := []string{
"images/logo.png",
"application-images/logo.svg",
"oidc-client-images/abc.png",
"deeply/nested/file.bin",
}
prefixes := []string{"", "root", "data/uploads"}
for _, prefix := range prefixes {
for _, p := range paths {
s := &s3Storage{bucket: "bucket", prefix: prefix}
assert.Equal(t, p, s.pathFromKey(s.buildObjectKey(p)),
"round-trip failed for prefix=%q path=%q", prefix, p)
}
}
})
t.Run("isS3NotFound detects expected errors", func(t *testing.T) {
assert.True(t, isS3NotFound(&smithy.GenericAPIError{Code: "NoSuchKey"}))
assert.True(t, isS3NotFound(&smithy.GenericAPIError{Code: "NotFound"}))

View File

@@ -16,7 +16,7 @@
"dependencies": {
"@simplewebauthn/browser": "^13.2.2",
"@tailwindcss/vite": "^4.2.0",
"axios": "^1.13.5",
"axios": "^1.15.0",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"jose": "^6.1.3",