diff --git a/pkg/limatmpl/locator.go b/pkg/limatmpl/locator.go index ddb9b61bab9..2d73de694a5 100644 --- a/pkg/limatmpl/locator.go +++ b/pkg/limatmpl/locator.go @@ -14,6 +14,7 @@ import ( "github.com/containerd/containerd/identifiers" "github.com/lima-vm/lima/pkg/ioutilx" "github.com/lima-vm/lima/pkg/templatestore" + "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" ) @@ -33,12 +34,33 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { Locator: locator, } - isTemplateURL, templateURL := SeemsTemplateURL(locator) + algorithm := digest.SHA256 + expectedDigest := "" + + // Split off optional digest suffix + index := strings.LastIndex(locator, "@") + if index > strings.LastIndex(locator, "/") { + tmpl.Locator = locator[:index] + expectedDigest = locator[index+1:] + index = strings.LastIndex(expectedDigest, ":") + if index != -1 { + algorithm = digest.Algorithm(expectedDigest[:index]) + expectedDigest = expectedDigest[index+1:] + if !algorithm.Available() { + return nil, fmt.Errorf("locator %q digest algorithm is not available", locator) + } + } + if len(expectedDigest) < 7 { + return nil, fmt.Errorf("locator %q expected digest length less than 7 characters", locator) + } + } + + isTemplateURL, templateURL := SeemsTemplateURL(tmpl.Locator) switch { case isTemplateURL: // No need to use SecureJoin here. https://github.com/lima-vm/lima/pull/805#discussion_r853411702 templateName := filepath.Join(templateURL.Host, templateURL.Path) - logrus.Debugf("interpreting argument %q as a template name %q", locator, templateName) + logrus.Debugf("interpreting argument %q as a template name %q", tmpl.Locator, templateName) if tmpl.Name == "" { // e.g., templateName = "deprecated/centos-7" , tmpl.Name = "centos-7" tmpl.Name = filepath.Base(templateName) @@ -47,15 +69,15 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { if err != nil { return nil, err } - case SeemsHTTPURL(locator): + case SeemsHTTPURL(tmpl.Locator): if tmpl.Name == "" { - tmpl.Name, err = InstNameFromURL(locator) + tmpl.Name, err = InstNameFromURL(tmpl.Locator) if err != nil { return nil, err } } - logrus.Debugf("interpreting argument %q as a http url for instance %q", locator, tmpl.Name) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, locator, http.NoBody) + logrus.Debugf("interpreting argument %q as a http url for instance %q", tmpl.Locator, tmpl.Name) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, tmpl.Locator, http.NoBody) if err != nil { return nil, err } @@ -68,15 +90,15 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { if err != nil { return nil, err } - case SeemsFileURL(locator): + case SeemsFileURL(tmpl.Locator): if tmpl.Name == "" { - tmpl.Name, err = InstNameFromURL(locator) + tmpl.Name, err = InstNameFromURL(tmpl.Locator) if err != nil { return nil, err } } - logrus.Debugf("interpreting argument %q as a file url for instance %q", locator, tmpl.Name) - r, err := os.Open(strings.TrimPrefix(locator, "file://")) + logrus.Debugf("interpreting argument %q as a file url for instance %q", tmpl.Locator, tmpl.Name) + r, err := os.Open(strings.TrimPrefix(tmpl.Locator, "file://")) if err != nil { return nil, err } @@ -85,15 +107,15 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { if err != nil { return nil, err } - case SeemsYAMLPath(locator): + case SeemsYAMLPath(tmpl.Locator): if tmpl.Name == "" { - tmpl.Name, err = InstNameFromYAMLPath(locator) + tmpl.Name, err = InstNameFromYAMLPath(tmpl.Locator) if err != nil { return nil, err } } - logrus.Debugf("interpreting argument %q as a file path for instance %q", locator, tmpl.Name) - r, err := os.Open(locator) + logrus.Debugf("interpreting argument %q as a file path for instance %q", tmpl.Locator, tmpl.Name) + r, err := os.Open(tmpl.Locator) if err != nil { return nil, err } @@ -102,12 +124,22 @@ func Read(ctx context.Context, name, locator string) (*Template, error) { if err != nil { return nil, err } - case locator == "-": + case tmpl.Locator == "-": tmpl.Bytes, err = io.ReadAll(os.Stdin) if err != nil { return nil, fmt.Errorf("unexpected error reading stdin: %w", err) } } + + if expectedDigest != "" { + actualDigest := algorithm.FromBytes(tmpl.Bytes).Encoded() + if len(expectedDigest) < len(actualDigest) { + actualDigest = actualDigest[:len(expectedDigest)] + } + if actualDigest != expectedDigest { + return nil, fmt.Errorf("locator %q digest doesn't match content digest %q", locator, actualDigest) + } + } return tmpl, nil }