package attach import ( "io" "net/http" "strings" "gocloud.dev/blob" ) const defaultStaticContentType = "application/octet-stream" // StaticHandler serves GET prefix// from bucket. // The blob key is rebuilt from disk_name via PartitionDirectory; request // path segments never reach NewReader unvalidated (T-05-13). func StaticHandler(bucket *blob.Bucket, prefix string) http.Handler { prefix = strings.TrimSuffix(prefix, "/") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet && r.Method != http.MethodHead { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if bucket == nil { http.NotFound(w, r) return } rel, ok := stripStaticPrefix(r.URL.Path, prefix) if !ok { http.NotFound(w, r) return } diskName, ok := parsePublicBlobPath(rel) if !ok { http.NotFound(w, r) return } key := BlobKey(diskName) reader, err := bucket.NewReader(r.Context(), key, nil) if err != nil { http.NotFound(w, r) return } defer reader.Close() ct := reader.ContentType() if ct == "" { ct = defaultStaticContentType } w.Header().Set("Content-Type", ct) if r.Method == http.MethodHead { return } _, _ = io.Copy(w, reader) }) } func stripStaticPrefix(path, prefix string) (string, bool) { if prefix == "" { return strings.TrimPrefix(path, "/"), true } if path == prefix { return "", false } if strings.HasPrefix(path, prefix+"/") { return path[len(prefix)+1:], true } return "", false } // parsePublicBlobPath accepts exactly 3 partition groups plus disk_name // whose PartitionDirectory matches those groups. Rejects "..", empty // segments, extra slashes, and mismatched partitions. func parsePublicBlobPath(p string) (string, bool) { if p == "" || strings.Contains(p, "\\") || strings.Contains(p, "..") || strings.Contains(p, "//") { return "", false } parts := strings.Split(p, "/") if len(parts) != 4 { return "", false } for _, part := range parts { if part == "" || part == "." || part == ".." { return "", false } } diskName := parts[3] got := strings.Join(parts[:3], "/") want := strings.TrimSuffix(PartitionDirectory(diskName), "/") if got != want { return "", false } return diskName, true }