Skip to content

Commit ed18edb

Browse files
committed
bug(aws): handle ECR repositories in different regions
Signed-off-by: Kevin Conner <[email protected]>
1 parent e1ea02c commit ed18edb

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

pkg/fanal/image/registry/ecr/ecr.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ecr
33
import (
44
"context"
55
"encoding/base64"
6+
"regexp"
67
"strings"
78

89
"github.com/aws/aws-sdk-go-v2/aws"
@@ -14,8 +15,6 @@ import (
1415
"github.com/aquasecurity/trivy/pkg/fanal/types"
1516
)
1617

17-
const ecrURL = "amazonaws.com"
18-
1918
type ecrAPI interface {
2019
GetAuthorizationToken(ctx context.Context, params *ecr.GetAuthorizationTokenInput, optFns ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error)
2120
}
@@ -37,7 +36,8 @@ func getSession(option types.RegistryOptions) (aws.Config, error) {
3736
}
3837

3938
func (e *ECR) CheckOptions(domain string, option types.RegistryOptions) error {
40-
if !strings.HasSuffix(domain, ecrURL) {
39+
region := determineRegion(domain)
40+
if region == "" {
4141
return xerrors.Errorf("ECR : %w", types.InvalidURLPattern)
4242
}
4343

@@ -46,11 +46,34 @@ func (e *ECR) CheckOptions(domain string, option types.RegistryOptions) error {
4646
return err
4747
}
4848

49+
// override region with the value from the repository domain
50+
cfg.Region = region
51+
4952
svc := ecr.NewFromConfig(cfg)
5053
e.Client = svc
5154
return nil
5255
}
5356

57+
// Endpoints take the form
58+
// <registry-id>.dkr.ecr.<region>.amazonaws.com
59+
// <registry-id>.dkr.ecr-fips.<region>.amazonaws.com
60+
// <registry-id>.dkr.ecr.<region>.amazonaws.com.cn
61+
// <registry-id>.dkr.ecr.<region>.sc2s.sgov.gov
62+
// <registry-id>.dkr.ecr.<region>.c2s.ic.gov
63+
// see
64+
// - https://docs.aws.amazon.com/general/latest/gr/ecr.html
65+
// - https://docs.amazonaws.cn/en_us/aws/latest/userguide/endpoints-arns.html
66+
// - https://github.com/boto/botocore/blob/1.34.51/botocore/data/endpoints.json
67+
var ecrEndpointMatch = regexp.MustCompile(`^[^.]+\.dkr\.ecr(?:-fips)?\.([^.]+)\.(?:amazonaws\.com(?:\.cn)?|sc2s\.sgov\.gov|c2s\.ic\.gov)$`)
68+
69+
func determineRegion(domain string) string {
70+
matches := ecrEndpointMatch.FindStringSubmatch(domain)
71+
if matches != nil {
72+
return matches[1]
73+
}
74+
return ""
75+
}
76+
5477
func (e *ECR) GetCredential(ctx context.Context) (username, password string, err error) {
5578
input := &ecr.GetAuthorizationTokenInput{}
5679
result, err := e.Client.GetAuthorizationToken(ctx, input)

pkg/fanal/image/registry/ecr/ecr_test.go

+61-3
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,76 @@ import (
88
"github.com/aws/aws-sdk-go-v2/aws"
99
"github.com/aws/aws-sdk-go-v2/service/ecr"
1010
awstypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
11+
"github.com/stretchr/testify/require"
1112

1213
"github.com/aquasecurity/trivy/pkg/fanal/types"
1314
)
1415

16+
type testECRClient interface {
17+
Options() ecr.Options
18+
}
19+
1520
func TestCheckOptions(t *testing.T) {
1621
var tests = map[string]struct {
17-
domain string
18-
wantErr error
22+
domain string
23+
expectedRegion string
24+
wantErr error
1925
}{
2026
"InvalidURL": {
2127
domain: "alpine:3.9",
2228
wantErr: types.InvalidURLPattern,
2329
},
2430
"NoOption": {
25-
domain: "xxx.ecr.ap-northeast-1.amazonaws.com",
31+
domain: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com",
32+
expectedRegion: "ap-northeast-1",
33+
},
34+
"region-1": {
35+
domain: "xxx.dkr.ecr.region-1.amazonaws.com",
36+
expectedRegion: "region-1",
37+
},
38+
"region-2": {
39+
domain: "xxx.dkr.ecr.region-2.amazonaws.com",
40+
expectedRegion: "region-2",
41+
},
42+
"fips-region-1": {
43+
domain: "xxx.dkr.ecr-fips.fips-region.amazonaws.com",
44+
expectedRegion: "fips-region",
45+
},
46+
"cn-region-1": {
47+
domain: "xxx.dkr.ecr.region-1.amazonaws.com.cn",
48+
expectedRegion: "region-1",
49+
},
50+
"cn-region-2": {
51+
domain: "xxx.dkr.ecr.region-2.amazonaws.com.cn",
52+
expectedRegion: "region-2",
53+
},
54+
"sc2s-region-1": {
55+
domain: "xxx.dkr.ecr.sc2s-region.sc2s.sgov.gov",
56+
expectedRegion: "sc2s-region",
57+
},
58+
"c2s-region-1": {
59+
domain: "xxx.dkr.ecr.c2s-region.c2s.ic.gov",
60+
expectedRegion: "c2s-region",
61+
},
62+
"invalid-ecr": {
63+
domain: "xxx.dkrecr.region-1.amazonaws.com",
64+
wantErr: types.InvalidURLPattern,
65+
},
66+
"invalid-fips": {
67+
domain: "xxx.dkr.ecrfips.fips-region.amazonaws.com",
68+
wantErr: types.InvalidURLPattern,
69+
},
70+
"invalid-cn": {
71+
domain: "xxx.dkr.ecr.region-2.amazonaws.cn",
72+
wantErr: types.InvalidURLPattern,
73+
},
74+
"invalid-sc2s": {
75+
domain: "xxx.dkr.ecr.sc2s-region.sc2s.sgov",
76+
wantErr: types.InvalidURLPattern,
77+
},
78+
"invalid-cs2": {
79+
domain: "xxx.dkr.ecr.c2s-region.c2s.ic",
80+
wantErr: types.InvalidURLPattern,
2681
},
2782
}
2883

@@ -35,6 +90,9 @@ func TestCheckOptions(t *testing.T) {
3590
}
3691
continue
3792
}
93+
94+
client := a.Client.(testECRClient)
95+
require.Equal(t, v.expectedRegion, client.Options().Region)
3896
}
3997
}
4098

0 commit comments

Comments
 (0)