package handler import ( "errors" "path/filepath" "strings" ) // URL ↔ filesystem path math used by several handler files. Pure // string manipulation — no I/O, no policy decisions — so it lives // in its own file rather than being attached to any one feature. // resolvePath translates a URL `path=` query (relative to fsRoot, with // '/' separator and leading '/') into an absolute filesystem path. It // rejects path traversal and the reserved .zddc.d/ bookkeeping sidecar so // the token store et al. cannot be addressed through admin APIs (admins // manage tokens via /.tokens, not the generic file path). All other // dot-/underscore-prefixed paths are ordinary content. Returns the cleaned // absolute path or an error suitable for a 404. func resolvePath(fsRoot, urlPath string) (string, error) { urlPath = strings.TrimSpace(urlPath) if urlPath == "" { urlPath = "/" } if !strings.HasPrefix(urlPath, "/") { return "", errors.New("path must be absolute (start with /)") } cleanURL := filepath.ToSlash(filepath.Clean(urlPath)) // Reject the one reserved namespace (.zddc.d/) so admin APIs cannot // address the token store / history / caches through a generic path. for _, seg := range strings.Split(strings.Trim(cleanURL, "/"), "/") { if seg == ReservedSidecar { return "", errors.New("reserved path segment") } } rel := strings.TrimPrefix(cleanURL, "/") abs := filepath.Join(fsRoot, filepath.FromSlash(rel)) abs = filepath.Clean(abs) // Path containment. if abs != fsRoot && !strings.HasPrefix(abs, fsRoot+string(filepath.Separator)) { return "", errors.New("path escapes root") } return abs, nil } // urlPathOf produces the URL form of an absolute filesystem path under // fsRoot. Returns "/" for fsRoot itself, otherwise "/". func urlPathOf(fsRoot, abs string) string { if abs == fsRoot { return "/" } rel, err := filepath.Rel(fsRoot, abs) if err != nil { return "/" } return "/" + filepath.ToSlash(rel) } // chainDirs reproduces EffectivePolicy's directory walk so callers can // label each policy-chain level with the directory it came from. Used // by the virtual-.zddc body to annotate which ancestor contributed // which rule. func chainDirs(fsRoot, dirPath string) []string { fsRoot = filepath.Clean(fsRoot) dirPath = filepath.Clean(dirPath) dirs := []string{fsRoot} if dirPath == fsRoot { return dirs } rel, err := filepath.Rel(fsRoot, dirPath) if err != nil || rel == "." { return dirs } current := fsRoot for _, part := range strings.Split(rel, string(filepath.Separator)) { current = filepath.Join(current, part) dirs = append(dirs, current) } return dirs }