mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-30 22:11:07 +00:00 
			
		
		
		
	Backport of #22765 Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		
					parent
					
						
							
								ff2014690d
							
						
					
				
			
			
				commit
				
					
						a239d6c4a9
					
				
			
		
					 12 changed files with 90 additions and 326 deletions
				
			
		|  | @ -23,19 +23,23 @@ import ( | |||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	packages_module "code.gitea.io/gitea/modules/packages" | ||||
| 	container_module "code.gitea.io/gitea/modules/packages/container" | ||||
| 	"code.gitea.io/gitea/modules/packages/container/oci" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers/api/packages/helper" | ||||
| 	packages_service "code.gitea.io/gitea/services/packages" | ||||
| 	container_service "code.gitea.io/gitea/services/packages/container" | ||||
| 
 | ||||
| 	digest "github.com/opencontainers/go-digest" | ||||
| ) | ||||
| 
 | ||||
| // maximum size of a container manifest | ||||
| // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests | ||||
| const maxManifestSize = 10 * 1024 * 1024 | ||||
| 
 | ||||
| var imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`) | ||||
| var ( | ||||
| 	imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`) | ||||
| 	referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`) | ||||
| ) | ||||
| 
 | ||||
| type containerHeaders struct { | ||||
| 	Status        int | ||||
|  | @ -407,16 +411,16 @@ func CancelUploadBlob(ctx *context.Context) { | |||
| } | ||||
| 
 | ||||
| func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) { | ||||
| 	digest := ctx.Params("digest") | ||||
| 	d := ctx.Params("digest") | ||||
| 
 | ||||
| 	if !oci.Digest(digest).Validate() { | ||||
| 	if digest.Digest(d).Validate() != nil { | ||||
| 		return nil, container_model.ErrContainerBlobNotExist | ||||
| 	} | ||||
| 
 | ||||
| 	return workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{ | ||||
| 		OwnerID: ctx.Package.Owner.ID, | ||||
| 		Image:   ctx.Params("image"), | ||||
| 		Digest:  digest, | ||||
| 		Digest:  d, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
|  | @ -471,14 +475,14 @@ func GetBlob(ctx *context.Context) { | |||
| 
 | ||||
| // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs | ||||
| func DeleteBlob(ctx *context.Context) { | ||||
| 	digest := ctx.Params("digest") | ||||
| 	d := ctx.Params("digest") | ||||
| 
 | ||||
| 	if !oci.Digest(digest).Validate() { | ||||
| 	if digest.Digest(d).Validate() != nil { | ||||
| 		apiErrorDefined(ctx, errBlobUnknown) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := deleteBlob(ctx.Package.Owner.ID, ctx.Params("image"), digest); err != nil { | ||||
| 	if err := deleteBlob(ctx.Package.Owner.ID, ctx.Params("image"), d); err != nil { | ||||
| 		apiError(ctx, http.StatusInternalServerError, err) | ||||
| 		return | ||||
| 	} | ||||
|  | @ -493,15 +497,15 @@ func UploadManifest(ctx *context.Context) { | |||
| 	reference := ctx.Params("reference") | ||||
| 
 | ||||
| 	mci := &manifestCreationInfo{ | ||||
| 		MediaType: oci.MediaType(ctx.Req.Header.Get("Content-Type")), | ||||
| 		MediaType: ctx.Req.Header.Get("Content-Type"), | ||||
| 		Owner:     ctx.Package.Owner, | ||||
| 		Creator:   ctx.Doer, | ||||
| 		Image:     ctx.Params("image"), | ||||
| 		Reference: reference, | ||||
| 		IsTagged:  !oci.Digest(reference).Validate(), | ||||
| 		IsTagged:  digest.Digest(reference).Validate() != nil, | ||||
| 	} | ||||
| 
 | ||||
| 	if mci.IsTagged && !oci.Reference(reference).Validate() { | ||||
| 	if mci.IsTagged && !referencePattern.MatchString(reference) { | ||||
| 		apiErrorDefined(ctx, errManifestInvalid.WithMessage("Tag is invalid")) | ||||
| 		return | ||||
| 	} | ||||
|  | @ -539,7 +543,7 @@ func UploadManifest(ctx *context.Context) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) { | ||||
| func getBlobSearchOptionsFromContext(ctx *context.Context) (*container_model.BlobSearchOptions, error) { | ||||
| 	reference := ctx.Params("reference") | ||||
| 
 | ||||
| 	opts := &container_model.BlobSearchOptions{ | ||||
|  | @ -547,14 +551,24 @@ func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDe | |||
| 		Image:      ctx.Params("image"), | ||||
| 		IsManifest: true, | ||||
| 	} | ||||
| 	if oci.Digest(reference).Validate() { | ||||
| 
 | ||||
| 	if digest.Digest(reference).Validate() == nil { | ||||
| 		opts.Digest = reference | ||||
| 	} else if oci.Reference(reference).Validate() { | ||||
| 	} else if referencePattern.MatchString(reference) { | ||||
| 		opts.Tag = reference | ||||
| 	} else { | ||||
| 		return nil, container_model.ErrContainerBlobNotExist | ||||
| 	} | ||||
| 
 | ||||
| 	return opts, nil | ||||
| } | ||||
| 
 | ||||
| func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) { | ||||
| 	opts, err := getBlobSearchOptionsFromContext(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return workaroundGetContainerBlob(ctx, opts) | ||||
| } | ||||
| 
 | ||||
|  | @ -611,18 +625,8 @@ func GetManifest(ctx *context.Context) { | |||
| // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-tags | ||||
| // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-manifests | ||||
| func DeleteManifest(ctx *context.Context) { | ||||
| 	reference := ctx.Params("reference") | ||||
| 
 | ||||
| 	opts := &container_model.BlobSearchOptions{ | ||||
| 		OwnerID:    ctx.Package.Owner.ID, | ||||
| 		Image:      ctx.Params("image"), | ||||
| 		IsManifest: true, | ||||
| 	} | ||||
| 	if oci.Digest(reference).Validate() { | ||||
| 		opts.Digest = reference | ||||
| 	} else if oci.Reference(reference).Validate() { | ||||
| 		opts.Tag = reference | ||||
| 	} else { | ||||
| 	opts, err := getBlobSearchOptionsFromContext(ctx) | ||||
| 	if err != nil { | ||||
| 		apiErrorDefined(ctx, errManifestUnknown) | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
|  | @ -20,14 +20,28 @@ import ( | |||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	packages_module "code.gitea.io/gitea/modules/packages" | ||||
| 	container_module "code.gitea.io/gitea/modules/packages/container" | ||||
| 	"code.gitea.io/gitea/modules/packages/container/oci" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	packages_service "code.gitea.io/gitea/services/packages" | ||||
| 
 | ||||
| 	digest "github.com/opencontainers/go-digest" | ||||
| 	oci "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| ) | ||||
| 
 | ||||
| func isValidMediaType(mt string) bool { | ||||
| 	return strings.HasPrefix(mt, "application/vnd.docker.") || strings.HasPrefix(mt, "application/vnd.oci.") | ||||
| } | ||||
| 
 | ||||
| func isImageManifestMediaType(mt string) bool { | ||||
| 	return strings.EqualFold(mt, oci.MediaTypeImageManifest) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.v2+json") | ||||
| } | ||||
| 
 | ||||
| func isImageIndexMediaType(mt string) bool { | ||||
| 	return strings.EqualFold(mt, oci.MediaTypeImageIndex) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.list.v2+json") | ||||
| } | ||||
| 
 | ||||
| // manifestCreationInfo describes a manifest to create | ||||
| type manifestCreationInfo struct { | ||||
| 	MediaType  oci.MediaType | ||||
| 	MediaType  string | ||||
| 	Owner      *user_model.User | ||||
| 	Creator    *user_model.User | ||||
| 	Image      string | ||||
|  | @ -37,12 +51,12 @@ type manifestCreationInfo struct { | |||
| } | ||||
| 
 | ||||
| func processManifest(mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) { | ||||
| 	var schema oci.SchemaMediaBase | ||||
| 	if err := json.NewDecoder(buf).Decode(&schema); err != nil { | ||||
| 	var index oci.Index | ||||
| 	if err := json.NewDecoder(buf).Decode(&index); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if schema.SchemaVersion != 2 { | ||||
| 	if index.SchemaVersion != 2 { | ||||
| 		return "", errUnsupported.WithMessage("Schema version is not supported") | ||||
| 	} | ||||
| 
 | ||||
|  | @ -50,17 +64,17 @@ func processManifest(mci *manifestCreationInfo, buf *packages_module.HashedBuffe | |||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if !mci.MediaType.IsValid() { | ||||
| 		mci.MediaType = schema.MediaType | ||||
| 		if !mci.MediaType.IsValid() { | ||||
| 	if !isValidMediaType(mci.MediaType) { | ||||
| 		mci.MediaType = index.MediaType | ||||
| 		if !isValidMediaType(mci.MediaType) { | ||||
| 			return "", errManifestInvalid.WithMessage("MediaType not recognized") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if mci.MediaType.IsImageManifest() { | ||||
| 	if isImageManifestMediaType(mci.MediaType) { | ||||
| 		d, err := processImageManifest(mci, buf) | ||||
| 		return d, err | ||||
| 	} else if mci.MediaType.IsImageIndex() { | ||||
| 	} else if isImageIndexMediaType(mci.MediaType) { | ||||
| 		d, err := processImageManifestIndex(mci, buf) | ||||
| 		return d, err | ||||
| 	} | ||||
|  | @ -205,7 +219,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H | |||
| 		} | ||||
| 
 | ||||
| 		for _, manifest := range index.Manifests { | ||||
| 			if !manifest.MediaType.IsImageManifest() { | ||||
| 			if !isImageManifestMediaType(manifest.MediaType) { | ||||
| 				return errManifestInvalid | ||||
| 			} | ||||
| 
 | ||||
|  | @ -345,8 +359,8 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met | |||
| } | ||||
| 
 | ||||
| type blobReference struct { | ||||
| 	Digest       oci.Digest | ||||
| 	MediaType    oci.MediaType | ||||
| 	Digest       digest.Digest | ||||
| 	MediaType    string | ||||
| 	Name         string | ||||
| 	File         *packages_model.PackageFileDescriptor | ||||
| 	ExpectedSize int64 | ||||
|  | @ -380,7 +394,7 @@ func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *package | |||
| 	} | ||||
| 
 | ||||
| 	props := map[string]string{ | ||||
| 		container_module.PropertyMediaType: string(ref.MediaType), | ||||
| 		container_module.PropertyMediaType: ref.MediaType, | ||||
| 		container_module.PropertyDigest:    string(ref.Digest), | ||||
| 	} | ||||
| 	for name, value := range props { | ||||
|  | @ -425,7 +439,7 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack | |||
| 
 | ||||
| 	manifestDigest := digestFromHashSummer(buf) | ||||
| 	err = createFileFromBlobReference(ctx, pv, nil, &blobReference{ | ||||
| 		Digest:       oci.Digest(manifestDigest), | ||||
| 		Digest:       digest.Digest(manifestDigest), | ||||
| 		MediaType:    mci.MediaType, | ||||
| 		Name:         container_model.ManifestFilename, | ||||
| 		File:         &packages_model.PackageFileDescriptor{Blob: pb}, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue