package internal import ( "github.com/pkg/errors" "path/filepath" "regexp" "runtime" "strings" ) // annotation spex from https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys const ( separator = `(?:[-._:@+]|--)` alphanum = `(?:[A-Za-z0-9]+)` component = `(?:` + alphanum + `(?:` + separator + alphanum + `)*)` ) var refRegexp = regexp.MustCompile(`^` + component + `(?:/` + component + `)*$`) var windowsRefRegexp = regexp.MustCompile(`^([a-zA-Z]:\\.+?):(.*)$`) // ValidateImageName returns nil if the image name is empty or matches the open-containers image name specs. // In any other case an error is returned. func ValidateImageName(image string) error { if len(image) == 0 { return nil } var err error if !refRegexp.MatchString(image) { err = errors.Errorf("Invalid image %s", image) } return err } // SplitPathAndImage tries to split the provided OCI reference into the OCI path and image. // Neither path nor image parts are validated at this stage. func SplitPathAndImage(reference string) (string, string) { if runtime.GOOS == "windows" { return splitPathAndImageWindows(reference) } return splitPathAndImageNonWindows(reference) } func splitPathAndImageWindows(reference string) (string, string) { groups := windowsRefRegexp.FindStringSubmatch(reference) // nil group means no match if groups == nil { return reference, "" } // we expect three elements. First one full match, second the capture group for the path and // the third the capture group for the image if len(groups) != 3 { return reference, "" } return groups[1], groups[2] } func splitPathAndImageNonWindows(reference string) (string, string) { sep := strings.SplitN(reference, ":", 2) path := sep[0] var image string if len(sep) == 2 { image = sep[1] } return path, image } // ValidateOCIPath takes the OCI path and validates it. func ValidateOCIPath(path string) error { if runtime.GOOS == "windows" { // On Windows we must allow for a ':' as part of the path if strings.Count(path, ":") > 1 { return errors.Errorf("Invalid OCI reference: path %s contains more than one colon", path) } } else { if strings.Contains(path, ":") { return errors.Errorf("Invalid OCI reference: path %s contains a colon", path) } } return nil } // ValidateScope validates a policy configuration scope for an OCI transport. func ValidateScope(scope string) error { var err error if runtime.GOOS == "windows" { err = validateScopeWindows(scope) } else { err = validateScopeNonWindows(scope) } if err != nil { return err } cleaned := filepath.Clean(scope) if cleaned != scope { return errors.Errorf(`Invalid scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned) } return nil } func validateScopeWindows(scope string) error { matched, _ := regexp.Match(`^[a-zA-Z]:\\`, []byte(scope)) if !matched { return errors.Errorf("Invalid scope '%s'. Must be an absolute path", scope) } return nil } func validateScopeNonWindows(scope string) error { if !strings.HasPrefix(scope, "/") { return errors.Errorf("Invalid scope %s: must be an absolute path", scope) } // Refuse also "/", otherwise "/" and "" would have the same semantics, // and "" could be unexpectedly shadowed by the "/" entry. if scope == "/" { return errors.New(`Invalid scope "/": Use the generic default scope ""`) } return nil }