| 541 | } |
| 542 | |
| 543 | func (s *ObjectTokenStore) resolveDeletePath(id string) (string, error) { |
| 544 | id = strings.TrimSpace(id) |
| 545 | if id == "" { |
| 546 | return "", fmt.Errorf("object store: id is empty") |
| 547 | } |
| 548 | // Absolute paths are honored as-is; callers must ensure they point inside the mirror. |
| 549 | if filepath.IsAbs(id) { |
| 550 | return id, nil |
| 551 | } |
| 552 | // Treat any non-absolute id (including nested like "team/foo") as relative to the mirror authDir. |
| 553 | // Normalize separators and guard against path traversal. |
| 554 | clean := filepath.Clean(filepath.FromSlash(id)) |
| 555 | if clean == "." || clean == ".." || strings.HasPrefix(clean, ".."+string(os.PathSeparator)) { |
| 556 | return "", fmt.Errorf("object store: invalid auth identifier %s", id) |
| 557 | } |
| 558 | // Ensure .json suffix. |
| 559 | if !strings.HasSuffix(strings.ToLower(clean), ".json") { |
| 560 | clean += ".json" |
| 561 | } |
| 562 | return filepath.Join(s.authDir, clean), nil |
| 563 | } |
| 564 | |
| 565 | func (s *ObjectTokenStore) readAuthFile(path, baseDir string) (*cliproxyauth.Auth, error) { |
| 566 | data, err := os.ReadFile(path) |