ci / api / api (push) Successful in 56s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m23s
ci / db / db (push) Successful in 1m8s
ci / recommender / recommender (push) Failing after 40s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m25s
ci / ts / ts (packages + web) (push) Successful in 7m20s
51 lines
1.2 KiB
Go
51 lines
1.2 KiB
Go
package library
|
|
|
|
import (
|
|
"path"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"pipu/api/internal/platform/apperr"
|
|
)
|
|
|
|
// normalizeExcludedDirs accepts literal paths relative to the storage root, not globs.
|
|
func normalizeExcludedDirs(dirs []string) ([]string, error) {
|
|
invalid := func() ([]string, error) {
|
|
return nil, apperr.Validation("excluded_dirs", "validation.excluded_dirs_invalid", "use up to 100 relative folder paths without .., one per entry", nil)
|
|
}
|
|
if len(dirs) > 100 {
|
|
return invalid()
|
|
}
|
|
out := []string{}
|
|
seen := map[string]bool{}
|
|
for _, dir := range dirs {
|
|
dir = strings.ReplaceAll(strings.TrimSpace(dir), "\\", "/")
|
|
if dir == "" || strings.HasPrefix(dir, "/") || strings.ContainsAny(dir, "\x00\r\n") || utf8.RuneCountInString(dir) > 1000 || (len(dir) > 1 && dir[1] == ':') {
|
|
return invalid()
|
|
}
|
|
for _, part := range strings.Split(dir, "/") {
|
|
if part == ".." {
|
|
return invalid()
|
|
}
|
|
}
|
|
dir = path.Clean(dir)
|
|
if dir == "." {
|
|
return invalid()
|
|
}
|
|
if !seen[dir] {
|
|
out = append(out, dir)
|
|
seen[dir] = true
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func pathExcluded(rel string, dirs []string) bool {
|
|
for _, dir := range dirs {
|
|
if rel == dir || strings.HasPrefix(rel, dir+"/") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|