A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at label-service 123 lines 3.1 kB view raw
1package main 2 3import ( 4 "os" 5 "strings" 6) 7 8// ImageRef is a parsed container image reference 9type ImageRef struct { 10 Host string 11 Identity string 12 Repo string 13 Tag string 14 Raw string 15} 16 17// detectImageRef walks the process tree looking for an image reference 18// that matches the given registry host. It starts from the parent process 19// and walks up to 5 ancestors to handle wrapper scripts (make, bash -c, etc.). 20// 21// Returns nil if no matching image reference is found — callers should 22// fall back to the active account. 23func detectImageRef(registryHost string) *ImageRef { 24 // Normalize the registry host for matching 25 matchHost := strings.TrimPrefix(registryHost, "https://") 26 matchHost = strings.TrimPrefix(matchHost, "http://") 27 matchHost = strings.TrimSuffix(matchHost, "/") 28 29 pid := os.Getppid() 30 for depth := 0; depth < 5; depth++ { 31 args, err := getProcessArgs(pid) 32 if err != nil { 33 break 34 } 35 36 for _, arg := range args { 37 if ref := parseImageRef(arg, matchHost); ref != nil { 38 return ref 39 } 40 } 41 42 ppid, err := getParentPID(pid) 43 if err != nil || ppid == pid || ppid <= 1 { 44 break 45 } 46 pid = ppid 47 } 48 49 return nil 50} 51 52// parseImageRef tries to parse a string as a container image reference. 53// Expected format: host/identity/repo:tag or host/identity/repo 54// 55// Handles: 56// - docker:// and oci:// transport prefixes (skopeo) 57// - Flags (- prefix), paths (/ or . prefix), shell artifacts (|, &, ;) 58// - Optional tag (defaults to "latest") 59// - Host must look like a domain (contains ., or is localhost, or has :port) 60// - If matchHost is non-empty, only returns refs matching that host 61func parseImageRef(s string, matchHost string) *ImageRef { 62 // Skip flags, absolute paths, relative paths 63 if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "/") || strings.HasPrefix(s, ".") { 64 return nil 65 } 66 67 // Strip docker:// or oci:// transport prefixes (skopeo) 68 s = strings.TrimPrefix(s, "docker://") 69 s = strings.TrimPrefix(s, "oci://") 70 71 // Skip other transport schemes 72 if strings.Contains(s, "://") { 73 return nil 74 } 75 // Must contain at least one slash 76 if !strings.Contains(s, "/") { 77 return nil 78 } 79 // Skip things that look like shell commands 80 if strings.ContainsAny(s, " |&;") { 81 return nil 82 } 83 84 // Split off tag 85 tag := "latest" 86 refPart := s 87 if atIdx := strings.LastIndex(s, ":"); atIdx != -1 { 88 lastSlash := strings.LastIndex(s, "/") 89 if atIdx > lastSlash { 90 tag = s[atIdx+1:] 91 refPart = s[:atIdx] 92 } 93 } 94 95 parts := strings.Split(refPart, "/") 96 97 // ATCR pattern requires host/identity/repo (3+ parts) 98 if len(parts) < 3 { 99 return nil 100 } 101 102 host := parts[0] 103 identity := parts[1] 104 repo := strings.Join(parts[2:], "/") 105 106 // Host must look like a domain 107 if !strings.Contains(host, ".") && host != "localhost" && !strings.Contains(host, ":") { 108 return nil 109 } 110 111 // If a specific host was requested, enforce it 112 if matchHost != "" && host != matchHost { 113 return nil 114 } 115 116 return &ImageRef{ 117 Host: host, 118 Identity: identity, 119 Repo: repo, 120 Tag: tag, 121 Raw: s, 122 } 123}