Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin-upstream/main' into gc-referrers
Browse files Browse the repository at this point in the history
  • Loading branch information
qweeah committed Jun 29, 2023
2 parents a69435d + f9aec91 commit fdc59b8
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 17 deletions.
22 changes: 22 additions & 0 deletions cmd/oras/internal/option/packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/pflag"
"oras.land/oras-go/v2/content"
"oras.land/oras/cmd/oras/internal/fileref"
)

// Pre-defined annotation keys for annotation file
Expand All @@ -38,6 +40,7 @@ var (
errAnnotationConflict = errors.New("`--annotation` and `--annotation-file` cannot be both specified")
errAnnotationFormat = errors.New("missing key in `--annotation` flag")
errAnnotationDuplication = errors.New("duplicate annotation key")
errPathValidation = errors.New("absolute file path detected. If it's intentional, use --disable-path-validation flag to skip this check")
)

// Packer option struct.
Expand Down Expand Up @@ -69,6 +72,25 @@ func (opts *Packer) ExportManifest(ctx context.Context, fetcher content.Fetcher,
}
return os.WriteFile(opts.ManifestExportPath, manifestBytes, 0666)
}
func (opts *Packer) Parse() error {
if !opts.PathValidationDisabled {
var failedPaths []string
for _, path := range opts.FileRefs {
// Remove the type if specified in the path <file>[:<type>] format
path, _, err := fileref.Parse(path, "")
if err != nil {
return err
}
if filepath.IsAbs(path) {
failedPaths = append(failedPaths, path)
}
}
if len(failedPaths) > 0 {
return fmt.Errorf("%w: %v", errPathValidation, strings.Join(failedPaths, ", "))
}
}
return nil
}

// LoadManifestAnnotations loads the manifest annotation map.
func (opts *Packer) LoadManifestAnnotations() (annotations map[string]map[string]string, err error) {
Expand Down
1 change: 0 additions & 1 deletion cmd/oras/root/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ func runAttach(ctx context.Context, opts attachOptions) error {
return err
}
defer store.Close()
store.AllowPathTraversalOnWrite = opts.PathValidationDisabled

dst, err := opts.NewTarget(opts.Common)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions cmd/oras/root/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package root

import (
"context"
"errors"
"fmt"
"io"
"sync"
Expand Down Expand Up @@ -237,6 +238,9 @@ func runPull(ctx context.Context, opts pullOptions) error {
// Copy
desc, err := oras.Copy(ctx, src, opts.Reference, dst, opts.Reference, copyOptions)
if err != nil {
if errors.Is(err, file.ErrPathTraversalDisallowed) {
err = fmt.Errorf("%s: %w", "use flag --allow-path-traversal to allow insecurely pulling files outside of working directory", err)
}
return err
}
if pulledEmpty {
Expand Down
1 change: 0 additions & 1 deletion cmd/oras/root/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ func runPush(ctx context.Context, opts pushOptions) error {
return err
}
defer store.Close()
store.AllowPathTraversalOnWrite = opts.PathValidationDisabled
if opts.manifestConfigRef != "" {
path, cfgMediaType, err := fileref.Parse(opts.manifestConfigRef, oras.MediaTypeUnknownConfig)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions cmd/oras/root/repo/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ Example - Show tags of the target OCI layout folder 'layout-dir':
Example - Show tags of the target OCI layout archive 'layout.tar':
oras repo tags --oci-layout layout.tar
Example - Show tags associated with a particular tagged resource:
Example - [Experimental] Show tags associated with a particular tagged resource:
oras repo tags localhost:5000/hello:latest
Example - Show tags associated with a digest:
Example - [Experimental] Show tags associated with a digest:
oras repo tags localhost:5000/hello@sha256:c551125a624189cece9135981621f3f3144564ddabe14b523507bf74c2281d9b
`,
Args: cobra.ExactArgs(1),
Expand Down Expand Up @@ -95,7 +95,7 @@ func showTags(ctx context.Context, opts showTagsOptions) error {
}
filter = desc.Digest.String()
}
logger.Infof("[Experimental] querying tags associated to %s, it may take a while...\n", filter)
logger.Warnf("[Experimental] querying tags associated to %s, it may take a while...\n", filter)
}
return finder.Tags(ctx, opts.last, func(tags []string) error {
for _, tag := range tags {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/internal/testdata/multi_arch/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ var (
LinuxAMD64ReferrerStateKey = match.StateKey{Digest: "57e6462826c8", Name: "application/vnd.oci.image.manifest.v1+json"}
LinuxAMD64ReferrerConfigStateKey = match.StateKey{Digest: "44136fa355b3", Name: "referrer.image"}
LinuxAMD64StateKeys = []match.StateKey{
{Digest: "9d84a5716c66", Name: ocispec.MediaTypeImageManifest},
{Digest: "fe9dbc99451d", Name: ocispec.MediaTypeImageConfig},
{Digest: "9d84a5716c66", Name: "application/vnd.oci.image.manifest.v1+json"},
{Digest: "fe9dbc99451d", Name: "application/vnd.oci.image.config.v1+json"},
{Digest: "2ef548696ac7", Name: "hello.tar"},
}
)
44 changes: 36 additions & 8 deletions test/e2e/suite/command/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ var _ = Describe("Common registry users:", func() {
fetched := ORAS("manifest", "fetch", RegistryRef(Host, testRepo, index.Manifests[0].Digest.String())).Exec().Out.Contents()
MatchFile(filepath.Join(tempDir, exportName), string(fetched), DefaultTimeout)
})

It("should attach a file via a OCI Image", func() {
testRepo := attachTestRepo("image")
tempDir := PrepareTempFiles()
Expand All @@ -119,8 +120,35 @@ var _ = Describe("Common registry users:", func() {
bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.image.manifest.v1+json"))
})

It("should attach file with path validation disabled", func() {
testRepo := attachTestRepo("simple")
absAttachFileName := filepath.Join(PrepareTempFiles(), foobar.AttachFileName)

subjectRef := RegistryRef(Host, testRepo, foobar.Tag)
prepare(RegistryRef(Host, ImageRepo, foobar.Tag), subjectRef)
statusKey := foobar.AttachFileStateKey
statusKey.Name = absAttachFileName
ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", absAttachFileName, foobar.AttachFileMedia), "--disable-path-validation").
MatchStatus([]match.StateKey{statusKey}, false, 1).
Exec()
})

It("should fail path validation when attaching file with absolute path", func() {
testRepo := attachTestRepo("simple")
absAttachFileName := filepath.Join(PrepareTempFiles(), foobar.AttachFileName)

subjectRef := RegistryRef(Host, testRepo, foobar.Tag)
prepare(RegistryRef(Host, ImageRepo, foobar.Tag), subjectRef)
statusKey := foobar.AttachFileStateKey
statusKey.Name = absAttachFileName
ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", absAttachFileName, foobar.AttachFileMedia)).
ExpectFailure().
Exec()
})

It("should attach a file via a OCI Artifact", func() {
testRepo := attachTestRepo("artifact")
tempDir := PrepareTempFiles()
Expand All @@ -136,7 +164,7 @@ var _ = Describe("Common registry users:", func() {
bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeArtifactManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.artifact.manifest.v1+json"))
})
})
})
Expand All @@ -158,7 +186,7 @@ var _ = Describe("Fallback registry users:", func() {
bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.image.manifest.v1+json"))
})

It("should attach a file via a OCI Image by default", func() {
Expand All @@ -176,7 +204,7 @@ var _ = Describe("Fallback registry users:", func() {
bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.image.manifest.v1+json"))
})

It("should fail to attach again when cleaning referrers index", func() {
Expand Down Expand Up @@ -241,7 +269,7 @@ var _ = Describe("Fallback registry users:", func() {
bytes := ORAS("discover", subjectRef, "--distribution-spec", "v1.1-referrers-tag", "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.image.manifest.v1+json"))
})
})
})
Expand Down Expand Up @@ -286,7 +314,7 @@ var _ = Describe("OCI image layout users:", func() {
bytes := ORAS("discover", Flags.Layout, subjectRef, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.image.manifest.v1+json"))
fetched := ORAS("manifest", "fetch", Flags.Layout, LayoutRef(root, index.Manifests[0].Digest.String())).Exec().Out.Contents()
MatchFile(filepath.Join(root, exportName), string(fetched), DefaultTimeout)
})
Expand All @@ -304,7 +332,7 @@ var _ = Describe("OCI image layout users:", func() {
bytes := ORAS("discover", subjectRef, Flags.Layout, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.image.manifest.v1+json"))
})
It("should attach a file via a OCI Artifact", func() {
root := PrepareTempFiles()
Expand All @@ -320,7 +348,7 @@ var _ = Describe("OCI image layout users:", func() {
bytes := ORAS("discover", subjectRef, Flags.Layout, "-o", "json").Exec().Out.Contents()
Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred())
Expect(len(index.Manifests)).To(Equal(1))
Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeArtifactManifest))
Expect(index.Manifests[0].MediaType).To(Equal("application/vnd.oci.artifact.manifest.v1+json"))
})
})
})
4 changes: 2 additions & 2 deletions test/e2e/suite/command/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ var _ = Describe("Common registry users:", func() {
It("should push a manifest from file", func() {
manifestPath := WriteTempFile("manifest.json", manifest)
tag := "from-file"
ORAS("manifest", "push", RegistryRef(Host, ImageRepo, tag), manifestPath, "--media-type", ocispec.MediaTypeImageManifest).
ORAS("manifest", "push", RegistryRef(Host, ImageRepo, tag), manifestPath, "--media-type", "application/vnd.oci.image.manifest.v1+json").
MatchKeyWords("Pushed", RegistryRef(Host, ImageRepo, tag), "Digest:", digest).
WithInput(strings.NewReader(manifest)).Exec()
})
Expand All @@ -308,7 +308,7 @@ var _ = Describe("Common registry users:", func() {
manifest := `{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53}}`
digest := "sha256:0c2ae2c73c5dde0a42582d328b2e2ea43f36ba20f604fa8706f441ac8b0a3445"
tag := "mediatype-flag"
ORAS("manifest", "push", RegistryRef(Host, ImageRepo, tag), "-", "--media-type", ocispec.MediaTypeImageManifest).
ORAS("manifest", "push", RegistryRef(Host, ImageRepo, tag), "-", "--media-type", "application/vnd.oci.image.manifest.v1+json").
MatchKeyWords("Pushed", RegistryRef(Host, ImageRepo, tag), "Digest:", digest).
WithInput(strings.NewReader(manifest)).Exec()

Expand Down
34 changes: 34 additions & 0 deletions test/e2e/suite/command/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/onsi/gomega"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras/test/e2e/internal/testdata/feature"
"oras.land/oras/test/e2e/internal/testdata/foobar"
Expand Down Expand Up @@ -65,6 +66,39 @@ var _ = Describe("Remote registry users:", func() {
Expect(manifest.Layers).Should(ContainElements(foobar.BlobBarDescriptor("application/vnd.oci.image.layer.v1.tar")))
})

It("should push files with path validation disabled", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "disable-path-validation")
ref := RegistryRef(Host, repo, tag)
absBarName := filepath.Join(PrepareTempFiles(), foobar.FileBarName)

ORAS("push", ref, absBarName, "-v", "--disable-path-validation").
Exec()

// validate
fetched := ORAS("manifest", "fetch", ref).Exec().Out.Contents()
var manifest ocispec.Manifest
Expect(json.Unmarshal(fetched, &manifest)).ShouldNot(HaveOccurred())
Expect(manifest.Layers).Should(ContainElements(ocispec.Descriptor{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest.Digest(foobar.BarBlobDigest),
Size: 3,
Annotations: map[string]string{
"org.opencontainers.image.title": absBarName,
},
}))
})

It("should fail path validation when pushing file with absolute path", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "path-validation")
ref := RegistryRef(Host, repo, tag)
absBarName := filepath.Join(PrepareTempFiles(), foobar.FileBarName)
// test
ORAS("push", ref, absBarName, "-v").
MatchErrKeyWords("--disable-path-validation").
ExpectFailure().
Exec()
})

It("should push files and tag", func() {
repo := fmt.Sprintf("%s/%s", repoPrefix, "multi-tag")
tempDir := PrepareTempFiles()
Expand Down

0 comments on commit fdc59b8

Please sign in to comment.