diff --git a/.gitignore b/.gitignore index 2798e557e..da92fff3a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out .idea +.vscode main vendor diff --git a/analyzer/pkg/apk/apk.go b/analyzer/pkg/apk/apk.go index 2e4502970..b8bd36fe5 100644 --- a/analyzer/pkg/apk/apk.go +++ b/analyzer/pkg/apk/apk.go @@ -90,6 +90,7 @@ func (a alpinePkgAnalyzer) parseApkInfo(scanner *bufio.Scanner) ([]types.Package return a.uniquePkgs(pkgs), installedFiles } + func (a alpinePkgAnalyzer) uniquePkgs(pkgs []types.Package) (uniqPkgs []types.Package) { uniq := map[string]struct{}{} for _, pkg := range pkgs { diff --git a/go.mod b/go.mod index e4858a4ae..ea2e35c3d 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516 github.com/aquasecurity/tfsec v0.63.1 github.com/aws/aws-sdk-go v1.42.0 + github.com/containerd/containerd v1.5.8 github.com/docker/docker v20.10.10+incompatible github.com/docker/go-connections v0.4.0 github.com/go-git/go-git/v5 v5.4.2 diff --git a/go.sum b/go.sum index 9ac0f51ba..1d3d30a33 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,9 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hcsshim/test v0.0.0-20200826032352-301c83a30e7c/go.mod h1:30A5igQ91GEmhYJF8TaRP79pMBOYynRsyOByfVV0dU4= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= @@ -228,6 +229,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= @@ -254,6 +257,7 @@ github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMS github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -262,6 +266,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -269,6 +274,7 @@ github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmE github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -306,12 +312,14 @@ github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.4.1-0.20201117152358-0edc412565dc/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.2 h1:MG/Bg1pbmMb61j3wHCFWPxESXHieiKr2xG64px/k8zQ= github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= +github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -325,6 +333,7 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU= github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= @@ -349,6 +358,8 @@ github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDG github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= @@ -443,6 +454,7 @@ github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avu github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= @@ -576,6 +588,7 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -968,6 +981,7 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/buildkit v0.8.1 h1:zrGxLwffKM8nVxBvaJa7H404eQLfqlg1GB6YVIzXVQ0= github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= @@ -1054,17 +1068,22 @@ github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2r github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= -github.com/opencontainers/runc v1.0.0-rc93 h1:x2UMpOOVf3kQ8arv/EsDGwim8PTNqzL1/EYDr/+scOM= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2 h1:c4ca10UMgRcvZ6h0K4HtS15UaVSBEaE+iln2LVpAuGc= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -1135,6 +1154,7 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= @@ -1646,6 +1666,7 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/image/daemon.go b/image/daemon.go index 1460388af..31cb85924 100644 --- a/image/daemon.go +++ b/image/daemon.go @@ -1,12 +1,21 @@ package image import ( + "context" + "github.com/google/go-containerregistry/pkg/name" + "golang.org/x/xerrors" + "github.com/containerd/containerd/namespaces" "github.com/aquasecurity/fanal/image/daemon" "github.com/aquasecurity/fanal/types" ) +const ( + defaultContainerdSocket = "/run/containerd/containerd.sock" + defaultContainerdNamespace = "default" +) + func tryDockerDaemon(imageName string, ref name.Reference) (types.Image, func(), error) { img, cleanup, err := daemon.DockerImage(ref) if err != nil { @@ -30,6 +39,23 @@ func tryPodmanDaemon(ref string) (types.Image, func(), error) { }, cleanup, nil } +func tryContainerdDaemon(imageName string, ref name.Reference) (types.Image, func(), error) { + ctx := context.Background() + ctx = namespaces.WithNamespace(ctx, defaultContainerdNamespace) + ci, err := daemon.NewContainerd(defaultContainerdSocket, ref.Name(), ctx) + if err != nil { + return nil, nil, xerrors.Errorf("tryContainerdDaemon: failed to initialize a docker client: %w", err) + } + img, cleanup, err := daemon.ContainerdImage(ci, ref, ctx) + if err != nil { + return nil, nil, err + } + return daemonImage{ + Image: img, + name: imageName, + }, cleanup, nil +} + type daemonImage struct { daemon.Image name string diff --git a/image/daemon/containerd.go b/image/daemon/containerd.go new file mode 100644 index 000000000..fdc9013c6 --- /dev/null +++ b/image/daemon/containerd.go @@ -0,0 +1,300 @@ +package daemon + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "time" + + "github.com/containerd/containerd" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/images/archive" + "github.com/containerd/containerd/platforms" + "github.com/docker/docker/api/types/container" + "github.com/google/go-containerregistry/pkg/name" + "golang.org/x/xerrors" + + api "github.com/docker/docker/api/types" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +const ( + containerdNamespace = "default" + tagTemplate = "%s:%s" + digestTemplate = "%s@%s" +) + +type ImageReference struct { + Named string + Tag string + Digest string +} + +type ContainerdInterface interface { + GetImageConfig(context.Context) (ocispec.Descriptor, error) + GetImageName(context.Context) (string, error) + ImageWriter(context.Context, []string) (io.ReadCloser, error) + ContentStore(context.Context) (content.Store, error) + Close() error + GetOCIImageBytes(context.Context) ([]byte, error) +} + +type containerdClient struct { + client *containerd.Client + refName string + img containerd.Image +} + +func (cc *containerdClient) GetImageConfig(ctx context.Context) (ocispec.Descriptor, error) { + img, err := cc.client.GetImage(ctx, cc.refName) + if err != nil { + return ocispec.Descriptor{}, xerrors.Errorf("GetImageConfig:: failed to get Image by: %v, err: %v", cc.refName, err) + } + return img.Config(ctx) +} + +func (cc *containerdClient) Close() error { + cc.client.Close() + return nil +} + +func (cc *containerdClient) ImageWriter(ctx context.Context, ref []string) (io.ReadCloser, error) { + if len(ref) == 0 { + return nil, xerrors.Errorf("imageWriter: failed to get iamge name: %v", ref) + } + imgOpts := archive.WithImage(cc.client.ImageService(), ref[0]) + manifestOpts := archive.WithManifest(cc.img.Target()) + platOpts := archive.WithPlatform(platforms.Default()) + pr, pw := io.Pipe() + go func() { + pw.CloseWithError(archive.Export(ctx, cc.client.ContentStore(), pw, imgOpts, manifestOpts, platOpts)) + }() + return pr, nil +} + +func (cc *containerdClient) ContentStore(ctx context.Context) (content.Store, error) { + return cc.img.ContentStore(), nil +} + +func (cc *containerdClient) GetImageName(ctx context.Context) (string, error) { + return cc.img.Name(), nil +} + +func (cc *containerdClient) GetOCIImageBytes(ctx context.Context) ([]byte, error) { + cfg, err := cc.img.Config(ctx) + if err != nil { + return nil, xerrors.Errorf("GetOCIImageBytes:: failed to get img config, err: %v", err) + } + data, err := content.ReadBlob(ctx, cc.img.ContentStore(), cfg) + if err != nil { + return nil, xerrors.Errorf("GetOCIImageBytes:: failed to read blob: %v", err) + } + return data, nil +} + +func NewContainerd(socket, refName string, ctx context.Context) (ContainerdInterface, error) { + cli, err := containerd.New(socket) + if err != nil { + return &containerdClient{}, err + } + i, err := cli.GetImage(ctx, refName) + if err != nil { + return &containerdClient{}, err + } + + return &containerdClient{client: cli, refName: refName, img: i}, nil +} + +// ContainerdImage implements v1.Image by extending +func ContainerdImage(ci ContainerdInterface, ref name.Reference, ctx context.Context) (Image, func(), error) { + cleanup := func() {} + inspect, err := imageInspect(ctx, ci) + defer func() { + if err != nil { + ci.Close() + } + }() + if err != nil { + return nil, cleanup, err + } + + f, err := os.CreateTemp("", "fanal-*") + if err != nil { + return nil, cleanup, xerrors.Errorf("ContainerImage: failed to create a temporary file") + } + + cleanup = func() { + ci.Close() + f.Close() + _ = os.Remove(f.Name()) + } + + return &image{ + opener: imageOpener(ctx, ref.Name(), f, ci.ImageWriter), + inspect: inspect, + }, cleanup, nil +} + +// imageInspect returns ImageInspect struct +func imageInspect(ctx context.Context, ci ContainerdInterface) (inspect api.ImageInspect, err error) { + descriptor, err := ci.GetImageConfig(ctx) + if err != nil { + return api.ImageInspect{}, err + } + ociImage, err := containerToOci(ctx, ci) + if err != nil { + return api.ImageInspect{}, err + } + var createAt string + if ociImage.Created != nil { + createAt = ociImage.Created.Format(time.RFC3339Nano) + } + repoDigests, repoTags := getRepoInfo(ctx, ci) + var architecture string + if descriptor.Platform != nil { + architecture = descriptor.Platform.Architecture + } else { + architecture = ociImage.Architecture + } + + return api.ImageInspect{ + Architecture: architecture, + Config: getImageInfoConfigFromOciImage(ociImage), + Created: createAt, + ID: string(descriptor.Digest), + Os: ociImage.OS, + RepoDigests: repoDigests, + RepoTags: repoTags, + RootFS: api.RootFS{ + Type: ociImage.RootFS.Type, + Layers: digestToString(ociImage.RootFS.DiffIDs), + }, + Size: descriptor.Size, + }, nil +} + +func digestToString(digests []digest.Digest) []string { + strs := make([]string, 0, len(digests)) + for _, d := range digests { + strs = append(strs, d.String()) + } + return strs +} + +// getImageInfoConfigFromOciImage returns config of ImageConfig from oci image. +func getImageInfoConfigFromOciImage(img ocispec.Image) *container.Config { + volumes := make(map[string]struct{}) + for k, obj := range img.Config.Volumes { + volumes[k] = obj + } + + return &container.Config{ + User: img.Config.User, + Env: img.Config.Env, + Entrypoint: img.Config.Entrypoint, + Cmd: img.Config.Cmd, + WorkingDir: img.Config.WorkingDir, + Labels: img.Config.Labels, + StopSignal: img.Config.StopSignal, + Volumes: volumes, + } +} + +func containerToOci(ctx context.Context, ci ContainerdInterface) (ocispec.Image, error) { + var ociImage ocispec.Image + + cfg, err := ci.GetImageConfig(ctx) + if err != nil { + return ocispec.Image{}, err + } + + switch cfg.MediaType { + case ocispec.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config, "application/octet-stream": + data, err := ci.GetOCIImageBytes(ctx) + if err != nil { + return ocispec.Image{}, err + } + err = json.Unmarshal(data, &ociImage) + if err != nil { + return ocispec.Image{}, err + } + default: + return ocispec.Image{}, xerrors.Errorf("containerToOci: invalid image config media type: %v", cfg.MediaType) + } + return ociImage, nil +} + +// splitReference splits reference into name, tag and digest in string format. +func splitReference(ref string) (name string, tag string, digStr string) { + name = ref + + if loc := regDigest.FindStringIndex(name); loc != nil { + name, digStr = name[:loc[0]], name[loc[0]+1:] + } + + if loc := regTag.FindStringIndex(name); loc != nil { + name, tag = name[:loc[0]], name[loc[0]+1:] + } + return +} + +// Parse parses ref into. +func Parse(ctx context.Context, imgRef string, conf ocispec.Descriptor) (ImageReference, error) { + if ok := regRef.MatchString(imgRef); !ok { + return ImageReference{}, xerrors.Errorf("Parse: invalid reference: %s", imgRef) + } + + name, tag, digStr := splitReference(imgRef) + + if digStr != "" { + dig, err := digest.Parse(digStr) + if err != nil { + return ImageReference{}, err + } + + return ImageReference{ + Named: name, + Digest: dig.String(), + Tag: tag, + }, nil + } + + if conf.Digest != "" { + return ImageReference{ + Named: name, + Digest: conf.Digest.String(), + Tag: tag, + }, nil + } + + return ImageReference{ + Named: name, + Tag: tag, + }, nil +} + +func getRepoInfo(ctx context.Context, ci ContainerdInterface) (repoDigests, repoTags []string) { + + refName, err := ci.GetImageName(ctx) + if err != nil { + return + } + cfg, err := ci.GetImageConfig(ctx) + if err != nil { + return + } + fmt.Printf("imageInspect name: %+v; \n", refName) + reference, _ := Parse(ctx, refName, cfg) + fmt.Printf("imageInspect referenece: %+v; \n", reference) + if reference.Tag != "" { + repoTags = append(repoTags, fmt.Sprintf(tagTemplate, reference.Named, reference.Tag)) + } + if reference.Digest != "" { + repoDigests = append(repoDigests, fmt.Sprintf(digestTemplate, reference.Named, reference.Digest)) + } + return +} diff --git a/image/daemon/containerd_test.go b/image/daemon/containerd_test.go new file mode 100644 index 000000000..bf108461d --- /dev/null +++ b/image/daemon/containerd_test.go @@ -0,0 +1,70 @@ +package daemon + +import ( + "testing" + + "github.com/docker/docker/api/types" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/assert" +) + +func TestContainerdImage(t *testing.T) { + type fields struct { + Image v1.Image + opener opener + inspect types.ImageInspect + } +} + +func TestDigestToString(t *testing.T) { + test1Str := "test1" + emptyStr := "" + + tests := []struct { + digests []digest.Digest + wantedStrs []string + }{ + { + digests: []digest.Digest{digest.NewDigestFromEncoded(digest.SHA256, test1Str)}, + wantedStrs: []string{digest.SHA256.String() + ":" + test1Str}, + }, + { + digests: []digest.Digest{digest.NewDigestFromEncoded(digest.SHA256, emptyStr)}, + wantedStrs: []string{digest.SHA256.String() + ":" + emptyStr}, + }, + } + for _, test := range tests { + r := digestToString(test.digests) + assert.Equal(t, r, test.wantedStrs) + } +} + +func TestSplitReference(t *testing.T) { + tests := []struct { + ref string + name string + tag string + digStr string + }{ + { + ref: "nginx@sha256:2e87d9ff130deb0c2d63600390c3f2370e71e71841573990d54579bc35046203", + name: "nginx", + digStr: "sha256:2e87d9ff130deb0c2d63600390c3f2370e71e71841573990d54579bc35046203", + tag: "", + }, + { + ref: "nginx:latest", + name: "nginx", + tag: "latest", + digStr: "", + }, + } + + for _, test := range tests { + name, tag, digStr := splitReference(test.ref) + assert.Equal(t, test.name, name) + assert.Equal(t, test.tag, tag) + assert.Equal(t, test.digStr, digStr) + } +} diff --git a/image/daemon/docker.go b/image/daemon/docker.go index 388f6b74e..27446229c 100644 --- a/image/daemon/docker.go +++ b/image/daemon/docker.go @@ -46,7 +46,7 @@ func DockerImage(ref name.Reference) (Image, func(), error) { } return &image{ - opener: imageOpener(ref.Name(), f, c.ImageSave), + opener: imageOpener(context.Background(), ref.Name(), f, c.ImageSave), inspect: inspect, history: history, }, cleanup, nil diff --git a/image/daemon/image.go b/image/daemon/image.go index b4ab5841d..a868bf38f 100644 --- a/image/daemon/image.go +++ b/image/daemon/image.go @@ -2,6 +2,7 @@ package daemon import ( "context" + "fmt" "io" "os" "sync" @@ -27,10 +28,10 @@ type opener func() (v1.Image, error) type imageSave func(context.Context, []string) (io.ReadCloser, error) -func imageOpener(ref string, f *os.File, imageSave imageSave) opener { +func imageOpener(ctx context.Context, ref string, f *os.File, imageSave imageSave) opener { return func() (v1.Image, error) { // Store the tarball in local filesystem and return a new reader into the bytes each time we need to access something. - rc, err := imageSave(context.Background(), []string{ref}) + rc, err := imageSave(ctx, []string{ref}) if err != nil { return nil, xerrors.Errorf("unable to export the image: %w", err) } @@ -41,6 +42,7 @@ func imageOpener(ref string, f *os.File, imageSave imageSave) opener { } defer f.Close() + fmt.Printf("imageFromPath: %v", f.Name()) img, err := tarball.ImageFromPath(f.Name(), nil) if err != nil { return nil, xerrors.Errorf("failed to initialize the struct from the temporary file: %w", err) diff --git a/image/daemon/podman.go b/image/daemon/podman.go index 08803131e..fb1cc113f 100644 --- a/image/daemon/podman.go +++ b/image/daemon/podman.go @@ -107,7 +107,7 @@ func PodmanImage(ref string) (Image, func(), error) { } return &image{ - opener: imageOpener(ref, f, c.imageSave), + opener: imageOpener(context.Background(), ref, f, c.imageSave), inspect: inspect, }, cleanup, nil } diff --git a/image/daemon/regex.go b/image/daemon/regex.go new file mode 100644 index 000000000..d9768ebc6 --- /dev/null +++ b/image/daemon/regex.go @@ -0,0 +1,71 @@ +package daemon + +import "regexp" + +// +// v1 org.opencontainers.image.ref.name: +// +// NOTE: extend separator with "__" here to compatibility with moby +// +// ref ::= component ("/" component)* +// component ::= alphanum (separator alphanum)* +// alphanum ::= [A-Za-z0-9]+ +// separator ::= [-._:@+] | "--" | "__" +// +// In the image-spec, there is no definition about Tag. +// +// But, in fact, the Tag looks like: +// +// :\w[\w.-]*$ +// +var ( + regAlphanum = expression(`[A-Za-z0-9]`) + + regSeparator = expression(`([-._:@+]|--|__)`) + + regComponent = group( + oneOrMore(regAlphanum), + zeroOrMore(regSeparator, oneOrMore(regAlphanum))) + + regRef = entire( + regComponent, + zeroOrMore(expression("/"), regComponent)) + + regTag = expression(`:\w[\w.-]*$`) + + regDigest = expression(`@[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) +) + +// expression converts literal into regexp. +func expression(literal string) *regexp.Regexp { + return regexp.MustCompile(literal) +} + +// concat concats several expressions into one. +func concat(exp ...*regexp.Regexp) *regexp.Regexp { + s := "" + for i := range exp { + s = s + exp[i].String() + } + return regexp.MustCompile(s) +} + +// entire will match the whole line. +func entire(exp ...*regexp.Regexp) *regexp.Regexp { + return regexp.MustCompile("^" + concat(exp...).String() + "$") +} + +// zeroOrMore will group the expressions and match zero or more times. +func zeroOrMore(exp ...*regexp.Regexp) *regexp.Regexp { + return regexp.MustCompile(group(concat(exp...)).String() + "*") +} + +// zeroOrMore will group the expressions and match one or more times. +func oneOrMore(exp ...*regexp.Regexp) *regexp.Regexp { + return regexp.MustCompile(group(concat(exp...)).String() + "+") +} + +// group defines sub expression. +func group(exp ...*regexp.Regexp) *regexp.Regexp { + return regexp.MustCompile("(?:" + concat(exp...).String() + ")") +} diff --git a/image/image.go b/image/image.go index 9b2d7bd50..9d49df2cc 100644 --- a/image/image.go +++ b/image/image.go @@ -39,6 +39,12 @@ func NewDockerImage(ctx context.Context, imageName string, option types.DockerOp } errs = multierror.Append(errs, err) + img, cleanup, err = tryContainerdDaemon(imageName, ref) + if err == nil { + return img, cleanup, nil + } + errs = multierror.Append(errs, err) + // Try accessing Docker Registry img, err = tryRemote(ctx, imageName, ref, option) if err == nil {