Skip to content

Commit

Permalink
Merge pull request from GHSA-vfp6-jrw2-99g9
Browse files Browse the repository at this point in the history
* Add limit to number of sigs and attestations

Signed-off-by: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com>

* Update pkg/cosign/fetch.go

Co-authored-by: Cody Soyland <codysoyland@gmail.com>
Signed-off-by: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com>

* Update error message

Signed-off-by: Hayden B <hblauzvern@google.com>

* fix compilation error

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>

* Add e2e tests

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>

---------

Signed-off-by: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com>
Signed-off-by: Hayden B <hblauzvern@google.com>
Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
Co-authored-by: Cody Soyland <codysoyland@gmail.com>
Co-authored-by: Hayden B <hblauzvern@google.com>
  • Loading branch information
3 people authored Nov 7, 2023
1 parent 8b366c4 commit 8ac891f
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
9 changes: 9 additions & 0 deletions pkg/cosign/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (
"golang.org/x/sync/errgroup"
)

const maxAllowedSigsOrAtts = 100

type SignedPayload struct {
Base64Signature string
Payload []byte
Expand Down Expand Up @@ -84,6 +86,9 @@ func FetchSignaturesForReference(_ context.Context, ref name.Reference, opts ...
if len(l) == 0 {
return nil, fmt.Errorf("no signatures associated with %s", ref)
}
if len(l) > maxAllowedSigsOrAtts {
return nil, fmt.Errorf("maximum number of signatures on an image is %d, found %d", maxAllowedSigsOrAtts, len(l))
}

signatures := make([]SignedPayload, len(l))
var g errgroup.Group
Expand Down Expand Up @@ -145,6 +150,10 @@ func FetchAttestations(se oci.SignedEntity, predicateType string) ([]Attestation
if len(l) == 0 {
return nil, errors.New("found no attestations")
}
if len(l) > maxAllowedSigsOrAtts {
errMsg := fmt.Sprintf("maximum number of attestations on an image is %d, found %d", maxAllowedSigsOrAtts, len(l))
return nil, errors.New(errMsg)
}

attestations := make([]AttestationPayload, 0, len(l))
var attMu sync.Mutex
Expand Down
114 changes: 114 additions & 0 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,88 @@ func TestAttestationReplaceCreate(t *testing.T) {
}
}

func TestExcessiveAttestations(t *testing.T) {
repo, stop := reg(t)
defer stop()
td := t.TempDir()

imgName := path.Join(repo, "cosign-attest-download-e2e")

_, _, cleanup := mkimage(t, imgName)
defer cleanup()

_, privKeyPath, _ := keypair(t, td)
ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}

ctx := context.Background()

slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
slsaAttestationPath := filepath.Join(td, "attestation.slsa.json")
if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil {
t.Fatal(err)
}

vulnAttestation := `
{
"invocation": {
"parameters": null,
"uri": "invocation.example.com/cosign-testing",
"event_id": "",
"builder.id": ""
},
"scanner": {
"uri": "fakescanner.example.com/cosign-testing",
"version": "",
"db": {
"uri": "",
"version": ""
},
"result": null
},
"metadata": {
"scanStartedOn": "2022-04-12T00:00:00Z",
"scanFinishedOn": "2022-04-12T00:10:00Z"
}
}
`
ref, err := name.ParseReference(imgName)
if err != nil {
t.Fatal(err)
}
regOpts := options.RegistryOptions{}
ociremoteOpts, err := regOpts.ClientOpts(ctx)
if err != nil {
t.Fatal(err)
}

for i := 0; i < 102; i++ {
vulnAttestationPath := filepath.Join(td, fmt.Sprintf("attestation-%d.vuln.json", i))
if err := os.WriteFile(vulnAttestationPath, []byte(vulnAttestation), 0600); err != nil {
t.Fatal(err)
}

// Attest to create a vuln attestation
attestCommand := attest.AttestCommand{
KeyOpts: ko,
PredicatePath: vulnAttestationPath,
PredicateType: "vuln",
Timeout: 30 * time.Second,
Replace: false,
}
must(attestCommand.Exec(ctx, imgName), t)
}

attOpts := options.AttestationDownloadOptions{}
_, err = cosign.FetchAttestationsForReference(ctx, ref, attOpts.PredicateType, ociremoteOpts...)
if err == nil {
t.Fatalf("Expected an error, but 'err' was 'nil'")
}
expectedError := "maximum number of attestations on an image is 100, found 102"
if err.Error() != expectedError {
t.Errorf("Exted the error to be: '%s' but it was '%s'", expectedError, err.Error())
}
}

func TestAttestationReplace(t *testing.T) {
repo, stop := reg(t)
defer stop()
Expand Down Expand Up @@ -1254,6 +1336,38 @@ func TestDuplicateSign(t *testing.T) {
}
}

func TestExcessiveSignatures(t *testing.T) {
repo, stop := reg(t)
defer stop()
td := t.TempDir()

imgName := path.Join(repo, "cosign-e2e")

_, _, cleanup := mkimage(t, imgName)
defer cleanup()

ctx := context.Background()

for i := 0; i < 102; i++ {
_, privKeyPath, _ := keypair(t, td)

// Sign the image
ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
so := options.SignOptions{
Upload: true,
}
must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
}
err := download.SignatureCmd(ctx, options.RegistryOptions{}, imgName)
if err == nil {
t.Fatal("Expected an error, but 'err' was 'nil'")
}
expectedErr := "maximum number of signatures on an image is 100, found 102"
if err.Error() != expectedErr {
t.Fatalf("Expected the error '%s', but got the error '%s'", expectedErr, err.Error())
}
}

func TestKeyURLVerify(t *testing.T) {
// TODO: re-enable once distroless images are being signed by the new client
t.Skip()
Expand Down

0 comments on commit 8ac891f

Please sign in to comment.