44 lines
1.1 KiB
Go
44 lines
1.1 KiB
Go
// Adapted from Sendico edge/shared/storage/s3key; see backend/PROVENANCE.md.
|
|
package evidence
|
|
|
|
import (
|
|
"errors"
|
|
"path"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
var ErrInvalidKey = errors.New("invalid evidence key")
|
|
|
|
// JoinKey confines a server-generated object reference to a relative prefix.
|
|
// It is not an ownership check or a local-filesystem path sanitizer. Inputs must
|
|
// not be URL-decoded or interpreted as filesystem paths after validation.
|
|
func JoinKey(prefix, reference string) (string, error) {
|
|
a, err := relative(prefix)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
b, err := relative(reference)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return path.Join(a, b), nil
|
|
}
|
|
|
|
func relative(value string) (string, error) {
|
|
value = strings.TrimSpace(value)
|
|
if strings.ContainsAny(value, "\\%") || strings.ContainsFunc(value, unicode.IsControl) {
|
|
return "", ErrInvalidKey
|
|
}
|
|
for _, segment := range strings.Split(value, "/") {
|
|
if segment == ".." {
|
|
return "", ErrInvalidKey
|
|
}
|
|
}
|
|
clean := path.Clean(value)
|
|
if value == "" || clean == "." || path.IsAbs(clean) {
|
|
return "", ErrInvalidKey
|
|
}
|
|
return clean, nil
|
|
}
|