From f66fd1f38cebe06b08b539bfa1aacf0bb5f18c68 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 21:09:53 +0300 Subject: [PATCH 01/22] hash for release --- .github/workflows/build.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 454681c9..2d0ea623 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,6 +46,16 @@ jobs: CGO_ENABLED: 0 run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape # && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5 + - uses: MCJack123/ghaction-generate-release-hashes@v1 + with: + hash-type: sha1 + file-name: release.sha1 + + - uses: actions/upload-artifact@v2 + with: + name: Asset Hashes + path: release.sha1 + - name: Upload Release binaries id: upload-release-asset uses: actions/upload-release-asset@v1 @@ -56,3 +66,17 @@ jobs: asset_path: build/${{ matrix.os }}/kubescape asset_name: kubescape-${{ matrix.os }} asset_content_type: application/octet-stream + + - name: Upload Release hashes + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.once.outputs.upload_url }} + asset_path: release.sha1 + asset_name: kubescape-${{ matrix.os }}release.sha1 + asset_content_type: application/octet-stream + + + From 536d7fb3c560b0260d3001e461244d3f60bcc73c Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 21:20:25 +0300 Subject: [PATCH 02/22] try another release plugin --- .github/workflows/build.yaml | 42 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2d0ea623..40a09248 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -44,7 +44,7 @@ jobs: ArmoERServer: report.euprod1.cyberarmorsoft.com ArmoWebsite: portal.armo.cloud CGO_ENABLED: 0 - run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape # && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5 + run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5 - uses: MCJack123/ghaction-generate-release-hashes@v1 with: @@ -56,27 +56,23 @@ jobs: name: Asset Hashes path: release.sha1 +# - name: Upload Release binaries +# id: upload-release-asset +# uses: actions/upload-release-asset@v1 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# with: +# upload_url: ${{ needs.once.outputs.upload_url }} +# asset_path: build/${{ matrix.os }}/kubescape +# asset_name: kubescape-${{ matrix.os }} +# asset_content_type: application/octet-stream + - name: Upload Release binaries - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') with: - upload_url: ${{ needs.once.outputs.upload_url }} - asset_path: build/${{ matrix.os }}/kubescape - asset_name: kubescape-${{ matrix.os }} - asset_content_type: application/octet-stream - - - name: Upload Release hashes - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.once.outputs.upload_url }} - asset_path: release.sha1 - asset_name: kubescape-${{ matrix.os }}release.sha1 - asset_content_type: application/octet-stream - - - + token: ${{ secrets.GITHUB_TOKEN }} + files: | + build/${{ matrix.os }}/kubescape + build/${{ matrix.os }}/kubescape.md5 + \ No newline at end of file From d5ca49ef9b32f75c375744a744adb51ee81abb4e Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 21:23:00 +0300 Subject: [PATCH 03/22] removing not needed plugin --- .github/workflows/build.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 40a09248..a1321586 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,16 +45,6 @@ jobs: ArmoWebsite: portal.armo.cloud CGO_ENABLED: 0 run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5 - - - uses: MCJack123/ghaction-generate-release-hashes@v1 - with: - hash-type: sha1 - file-name: release.sha1 - - - uses: actions/upload-artifact@v2 - with: - name: Asset Hashes - path: release.sha1 # - name: Upload Release binaries # id: upload-release-asset From 79baa0d66eb558aa97fc972d2f443f7878dc1aa0 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 21:36:37 +0300 Subject: [PATCH 04/22] hashing --- .github/workflows/build.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a1321586..f2b20b73 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -44,7 +44,13 @@ jobs: ArmoERServer: report.euprod1.cyberarmorsoft.com ArmoWebsite: portal.armo.cloud CGO_ENABLED: 0 - run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5 + run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape + + - name: Digest + uses: MCJack123/ghaction-generate-release-hashes@v1 + with: + hash-type: sha1 + file-name: build/${{ matrix.os }}/kubescape.sha1 # - name: Upload Release binaries # id: upload-release-asset @@ -64,5 +70,5 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} files: | build/${{ matrix.os }}/kubescape - build/${{ matrix.os }}/kubescape.md5 + build/${{ matrix.os }}/kubescape.sha1 \ No newline at end of file From 4f07d23dd65e3e8ce99c7c0d47d12ba9d59f484f Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 21:50:35 +0300 Subject: [PATCH 05/22] moving to post release --- .github/workflows/build.yaml | 32 ++++++++--------------------- .github/workflows/post-release.yaml | 17 +++++++++++++++ 2 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/post-release.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f2b20b73..fa70583c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,29 +46,13 @@ jobs: CGO_ENABLED: 0 run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape - - name: Digest - uses: MCJack123/ghaction-generate-release-hashes@v1 - with: - hash-type: sha1 - file-name: build/${{ matrix.os }}/kubescape.sha1 - -# - name: Upload Release binaries -# id: upload-release-asset -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ needs.once.outputs.upload_url }} -# asset_path: build/${{ matrix.os }}/kubescape -# asset_name: kubescape-${{ matrix.os }} -# asset_content_type: application/octet-stream - - name: Upload Release binaries - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - token: ${{ secrets.GITHUB_TOKEN }} - files: | - build/${{ matrix.os }}/kubescape - build/${{ matrix.os }}/kubescape.sha1 - \ No newline at end of file + upload_url: ${{ needs.once.outputs.upload_url }} + asset_path: build/${{ matrix.os }}/kubescape + asset_name: kubescape-${{ matrix.os }} + asset_content_type: application/octet-stream diff --git a/.github/workflows/post-release.yaml b/.github/workflows/post-release.yaml new file mode 100644 index 00000000..3afe6539 --- /dev/null +++ b/.github/workflows/post-release.yaml @@ -0,0 +1,17 @@ +name: create release digests + +on: + release: + types: [ published] + branches: [ master ] + +jobs: + once: + name: Creating digests + runs-on: ubuntu-latest + steps: + - name: Digest + uses: MCJack123/ghaction-generate-release-hashes@v1 + with: + hash-type: sha1 + file-name: kubescape-release-digests From 0d75a273f00bb1f6888a083e09c0f87fec1055cc Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 22:06:27 +0300 Subject: [PATCH 06/22] post release event --- .github/workflows/build.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fa70583c..f9a56cb2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,7 +21,7 @@ jobs: with: tag_name: v1.0.${{ github.run_number }} release_name: Release v1.0.${{ github.run_number }} - draft: false + draft: true prerelease: false build: name: Create cross-platform release build, tag and upload binaries @@ -56,3 +56,14 @@ jobs: asset_path: build/${{ matrix.os }}/kubescape asset_name: kubescape-${{ matrix.os }} asset_content_type: application/octet-stream + post: + name: Publish release + runs-on: ubuntu-latest + steps: + - name: Publish release + uses: StuYarrow/publish-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + id: ${{ once.steps.create_release.outputs.id }} + From ddb86085019b778ee9a7315f6237b9aeb0a7ab64 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 22:11:20 +0300 Subject: [PATCH 07/22] moving up the publishing step --- .github/workflows/build.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f9a56cb2..75896fc8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -56,14 +56,10 @@ jobs: asset_path: build/${{ matrix.os }}/kubescape asset_name: kubescape-${{ matrix.os }} asset_content_type: application/octet-stream - post: - name: Publish release - runs-on: ubuntu-latest - steps: + - name: Publish release uses: StuYarrow/publish-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: id: ${{ once.steps.create_release.outputs.id }} - From 0aff119260b6c59d46aff5e6339cb8751a0fedc9 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 22:13:00 +0300 Subject: [PATCH 08/22] fix bad reference --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 75896fc8..6077ca0e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -62,4 +62,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - id: ${{ once.steps.create_release.outputs.id }} + id: ${{ steps.create_release.outputs.id }} From 015206a76059b785ebcaf279fa132db298ad6f00 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 3 Oct 2021 22:21:54 +0300 Subject: [PATCH 09/22] release id --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6077ca0e..0b69321e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -62,4 +62,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - id: ${{ steps.create_release.outputs.id }} + id: v1.0.${{ github.run_number }} From f903e13d7bd49d2e9c5b35add80febd431a7bff1 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 4 Oct 2021 08:19:44 +0300 Subject: [PATCH 10/22] adding hash to release zip --- .github/workflows/build.yaml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0b69321e..45d80a1c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,7 +21,7 @@ jobs: with: tag_name: v1.0.${{ github.run_number }} release_name: Release v1.0.${{ github.run_number }} - draft: true + draft: false prerelease: false build: name: Create cross-platform release build, tag and upload binaries @@ -44,8 +44,13 @@ jobs: ArmoERServer: report.euprod1.cyberarmorsoft.com ArmoWebsite: portal.armo.cloud CGO_ENABLED: 0 - run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape - + run: | + pip install sha1 + mkdir -p build/${{ matrix.os }} + go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape + python -m sha1 build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.sha1 + zip build/${{ matrix.os }}/kubescape.zip build/${{ matrix.os }}/kubescape build/${{ matrix.os }}/kubescape.sha1 + - name: Upload Release binaries id: upload-release-asset uses: actions/upload-release-asset@v1 @@ -53,13 +58,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.once.outputs.upload_url }} - asset_path: build/${{ matrix.os }}/kubescape - asset_name: kubescape-${{ matrix.os }} - asset_content_type: application/octet-stream - - - name: Publish release - uses: StuYarrow/publish-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - id: v1.0.${{ github.run_number }} + asset_path: build/${{ matrix.os }}/kubescape.zip + asset_name: kubescape-${{ matrix.os }}.zip + asset_content_type: application/zip \ No newline at end of file From 86b6a1d88afe335d41a009a1df4287d31ef6cd3e Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 4 Oct 2021 08:36:53 +0300 Subject: [PATCH 11/22] complete zip release and install with hash --- .github/workflows/build.yaml | 2 +- install.sh | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 45d80a1c..fa4bd86f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -49,7 +49,7 @@ jobs: mkdir -p build/${{ matrix.os }} go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape python -m sha1 build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.sha1 - zip build/${{ matrix.os }}/kubescape.zip build/${{ matrix.os }}/kubescape build/${{ matrix.os }}/kubescape.sha1 + python -m zipfile -c build/${{ matrix.os }}/kubescape.zip build/${{ matrix.os }}/kubescape build/${{ matrix.os }}/kubescape.sha1 - name: Upload Release binaries id: upload-release-asset diff --git a/install.sh b/install.sh index 56dab602..2e7db033 100755 --- a/install.sh +++ b/install.sh @@ -6,6 +6,7 @@ echo BASE_DIR=~/.kubescape KUBESCAPE_EXEC=kubescape +KUBESCAPE_ZIP=kubescape.zip osName=$(uname -s) if [[ $osName == *"MINGW"* ]]; then @@ -25,8 +26,10 @@ DOWNLOAD_URL=${DOWNLOAD_URL/browser_download_url: /} mkdir -p $BASE_DIR OUTPUT=$BASE_DIR/$KUBESCAPE_EXEC +OUTPUT_ZIP=$BASE_DIR/$KUBESCAPE_ZIP -curl --progress-bar -L $DOWNLOAD_URL -o $OUTPUT +curl --progress-bar -L $DOWNLOAD_URL -o $OUTPUT_ZIP +unzip $OUTPUT_ZIP $KUBESCAPE_EXEC -d $BASE_DIR # Checking if SUDO needed/exists SUDO= From f255df01984ab2b9796a8b23383eb8d9269cf709 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 10 Jan 2022 07:39:54 +0200 Subject: [PATCH 12/22] login first part --- container-images/civ-adaptor-if.go | 43 +++++++++++++++ container-images/civ-armo-adaptor.go | 80 ++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 container-images/civ-adaptor-if.go create mode 100644 container-images/civ-armo-adaptor.go diff --git a/container-images/civ-adaptor-if.go b/container-images/civ-adaptor-if.go new file mode 100644 index 00000000..ab11bb4b --- /dev/null +++ b/container-images/civ-adaptor-if.go @@ -0,0 +1,43 @@ +package containerimages + +import "time" + +type ContainerImageIdentifier struct { + Registry string + Repository string + Tag string + Hash string +} + +type ContainerImageScanStatus struct { + ImageID ContainerImageIdentifier + IsScanAvailable bool + IsBomAvailable bool + LastScanDate time.Time +} + +type ContainerImageVulnerability struct { + ImageID ContainerImageIdentifier + // TBD +} + +type ContainerImageInformation struct { + ImageID ContainerImageIdentifier + Bom []string + //ImageManifest Manifest // will use here Docker package definition +} + +type IContainerImageVulnerabilityAdaptor interface { + // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level + // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} + Login(registry string, credentials map[string]string) error + + // For "help" purposes + DescribeAdaptor() string + + GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) + + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} diff --git a/container-images/civ-armo-adaptor.go b/container-images/civ-armo-adaptor.go new file mode 100644 index 00000000..bf548b47 --- /dev/null +++ b/container-images/civ-armo-adaptor.go @@ -0,0 +1,80 @@ +package containerimages + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) + +type FeLoginData struct { + Secret string `json:"secret"` + ClientId string `json:"clientId"` +} + +type FeLoginResponse struct { + Token string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int32 `json:"expiresIn"` + Expires string `json:"expires"` +} + +type ArmoCivAdaptor struct { + registry string + accountId string + clientId string + accessKey string + feToken FeLoginResponse +} + +func CreateArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { + var accountId string + var accessKey string + var clientId string + var ok bool + if accountId, ok = credentials["accountId"]; !ok { + return nil, fmt.Errorf("Define accountId in credentials") + } + if clientId, ok = credentials["clientId"]; !ok { + return nil, fmt.Errorf("Define clientId in credentials") + } + if accessKey, ok = credentials["accessKey"]; !ok { + return nil, fmt.Errorf("Define accessKey in credentials") + } + return &ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey}, nil +} + +func (armoCivAdaptor ArmoCivAdaptor) Login() error { + feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey} + body, _ := json.Marshal(feLoginData) + + resp, err := http.Post("https://eggauth.eudev3.cyberarmorsoft.com/frontegg/identity/resources/auth/v1/api-token", "application/json", bytes.NewBuffer(body)) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + var feLoginResponse FeLoginResponse + err = json.Unmarshal(body, &feLoginResponse) + //fmt.Printf("Token: %s\n", feLoginResponse.Token) + //fmt.Printf("Body: %s\n", string(body)) + if err != nil { + return err + } + } else { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("Error authenticating: %d", resp.StatusCode) + + } + return fmt.Errorf("Error authenticating: %d - %s", resp.StatusCode, string(body)) + } + fmt.Printf("Success!") + return nil +} From 83246a1802529f1b8d05dd2de4e1da6d4f2f8f7e Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 10 Jan 2022 08:10:40 +0200 Subject: [PATCH 13/22] generalizing url settings --- container-images/civ-armo-adaptor.go | 43 +++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/container-images/civ-armo-adaptor.go b/container-images/civ-armo-adaptor.go index bf548b47..df573233 100644 --- a/container-images/civ-armo-adaptor.go +++ b/container-images/civ-armo-adaptor.go @@ -20,12 +20,18 @@ type FeLoginResponse struct { Expires string `json:"expires"` } +type ArmoBeConfiguration struct { + BackendUrl string `json:"backend"` + AuthUrl string `json:"authUrl"` +} + type ArmoCivAdaptor struct { registry string accountId string clientId string accessKey string feToken FeLoginResponse + armoUrls ArmoBeConfiguration } func CreateArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { @@ -42,16 +48,44 @@ func CreateArmoAdaptor(registry string, credentials map[string]string) (*ArmoCiv if accessKey, ok = credentials["accessKey"]; !ok { return nil, fmt.Errorf("Define accessKey in credentials") } - return &ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey}, nil + armoCivAdaptor := ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey} + err := armoCivAdaptor.initializeUrls() + if err != nil { + return nil, err + } + return &armoCivAdaptor, nil } -func (armoCivAdaptor ArmoCivAdaptor) Login() error { +func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error { + configUrl := fmt.Sprintf("https://%s/assets/configs/config.json", armoCivAdaptor.registry) + resp, err := http.Get(configUrl) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("cannot retrieve backend config file %s: status %d", configUrl, resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + err = json.Unmarshal(body, &armoCivAdaptor.armoUrls) + if err != nil { + return err + } + return nil + +} + +func (armoCivAdaptor *ArmoCivAdaptor) Login() error { feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey} body, _ := json.Marshal(feLoginData) - resp, err := http.Post("https://eggauth.eudev3.cyberarmorsoft.com/frontegg/identity/resources/auth/v1/api-token", "application/json", bytes.NewBuffer(body)) + authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl) + resp, err := http.Post(authApiTokenEndpoint, "application/json", bytes.NewBuffer(body)) if err != nil { - panic(err) + return err } defer resp.Body.Close() @@ -62,6 +96,7 @@ func (armoCivAdaptor ArmoCivAdaptor) Login() error { } var feLoginResponse FeLoginResponse err = json.Unmarshal(body, &feLoginResponse) + armoCivAdaptor.feToken = feLoginResponse //fmt.Printf("Token: %s\n", feLoginResponse.Token) //fmt.Printf("Body: %s\n", string(body)) if err != nil { From 39b95eff4f33b7ea93cb5d142e6942e23166e45a Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 10 Jan 2022 19:50:07 +0200 Subject: [PATCH 14/22] got full auth cycle --- container-images/armo-datastructures.go | 28 +++++ container-images/civ-armo-adaptor.go | 151 +++++++++++++++++++----- 2 files changed, 149 insertions(+), 30 deletions(-) create mode 100644 container-images/armo-datastructures.go diff --git a/container-images/armo-datastructures.go b/container-images/armo-datastructures.go new file mode 100644 index 00000000..16e94f38 --- /dev/null +++ b/container-images/armo-datastructures.go @@ -0,0 +1,28 @@ +package containerimages + +import "time" + +type V2ListRequest struct { + // properties of the requested next page + // Use ValidatePageProperties to set PageSize field + PageSize *int `json:"pageSize,omitempty"` + // One can leave it empty for 0, then call ValidatePageProperties + PageNum *int `json:"pageNum,omitempty"` + // The time window of the list to return. Default: since - begining og the time, until - now. + Since *time.Time `json:"since,omitempty"` + Until *time.Time `json:"until,omitempty"` + // Which elements of the list to return, each field can hold multiple values separated by comma + // Example: ": {"severity": "High,Medium", "type": "61539,30303"} + // An empty map means "return the complete list" + InnerFilters []map[string]string `json:"innerFilters,omitempty"` + // How to order (sort) the list, field name + sort order (asc/desc), like https://www.w3schools.com/sql/sql_orderby.asp + // Example: "timestamp:asc,severity:desc" + OrderBy string `json:"orderBy,omitempty"` + // Cursor to the next page of former requset. Not supported yet + // Cursor cannot be used with another parameters of this struct + Cursor string `json:"cursor,omitempty"` + // FieldsList allow us to return only subset of the source document fields + // Don't expose FieldsList outside without well designed decision + FieldsList []string `json:"includeFields,omitempty"` + FieldsReverseKeywordMap map[string]string `json:"-,omitempty"` +} diff --git a/container-images/civ-armo-adaptor.go b/container-images/civ-armo-adaptor.go index df573233..a1f69016 100644 --- a/container-images/civ-armo-adaptor.go +++ b/container-images/civ-armo-adaptor.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "net/http" + "strings" ) type FeLoginData struct { @@ -24,14 +25,18 @@ type ArmoBeConfiguration struct { BackendUrl string `json:"backend"` AuthUrl string `json:"authUrl"` } +type ArmoSelectCustomer struct { + SelectedCustomerGuid string `json:"selectedCustomer"` +} type ArmoCivAdaptor struct { - registry string - accountId string - clientId string - accessKey string - feToken FeLoginResponse - armoUrls ArmoBeConfiguration + registry string + accountId string + clientId string + accessKey string + feToken FeLoginResponse + armoUrls ArmoBeConfiguration + authCookie string } func CreateArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { @@ -40,13 +45,13 @@ func CreateArmoAdaptor(registry string, credentials map[string]string) (*ArmoCiv var clientId string var ok bool if accountId, ok = credentials["accountId"]; !ok { - return nil, fmt.Errorf("Define accountId in credentials") + return nil, fmt.Errorf("define accountId in credentials") } if clientId, ok = credentials["clientId"]; !ok { - return nil, fmt.Errorf("Define clientId in credentials") + return nil, fmt.Errorf("define clientId in credentials") } if accessKey, ok = credentials["accessKey"]; !ok { - return nil, fmt.Errorf("Define accessKey in credentials") + return nil, fmt.Errorf("define accessKey in credentials") } armoCivAdaptor := ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey} err := armoCivAdaptor.initializeUrls() @@ -78,6 +83,50 @@ func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error { } +func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { + selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountId} + requestBody, _ := json.Marshal(selectCustomer) + requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl) + client := &http.Client{} + httpRequest, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return "", err + } + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + /*fmt.Println(requestUrl) + fmt.Println(httpRequest.Header.Get("Content-Type")) + fmt.Println(httpRequest.Header.Get("Authorization")) + fmt.Println(string(requestBody))*/ + httpResponse, err := client.Do(httpRequest) + if err != nil { + return "", err + } + defer httpResponse.Body.Close() + if httpResponse.StatusCode != http.StatusOK { + return "", fmt.Errorf("error getting cookie at %s: status %d", requestUrl, httpResponse.StatusCode) + } + + cookies := httpResponse.Header.Get("set-cookie") + if len(cookies) == 0 { + return "", fmt.Errorf("no cookie field in response from %s", requestUrl) + } + + authCookie := "" + for _, cookie := range strings.Split(cookies, ";") { + kv := strings.Split(cookie, "=") + if kv[0] == "auth" { + authCookie = kv[1] + } + } + + if len(authCookie) == 0 { + return "", fmt.Errorf("no auth cookie field in response from %s", requestUrl) + } + + return authCookie, nil +} + func (armoCivAdaptor *ArmoCivAdaptor) Login() error { feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey} body, _ := json.Marshal(feLoginData) @@ -89,27 +138,69 @@ func (armoCivAdaptor *ArmoCivAdaptor) Login() error { } defer resp.Body.Close() - if resp.StatusCode == http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - var feLoginResponse FeLoginResponse - err = json.Unmarshal(body, &feLoginResponse) - armoCivAdaptor.feToken = feLoginResponse - //fmt.Printf("Token: %s\n", feLoginResponse.Token) - //fmt.Printf("Body: %s\n", string(body)) - if err != nil { - return err - } - } else { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("Error authenticating: %d", resp.StatusCode) - - } - return fmt.Errorf("Error authenticating: %d - %s", resp.StatusCode, string(body)) + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("error authenticating: %d", resp.StatusCode) } - fmt.Printf("Success!") + + responseBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + var feLoginResponse FeLoginResponse + err = json.Unmarshal(responseBody, &feLoginResponse) + armoCivAdaptor.feToken = feLoginResponse + if err != nil { + return err + } + /* Now we have JWT */ + + armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie() + if err != nil { + return err + } + return nil } + +func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageID *ContainerImageIdentifier) (*ContainerImageVulnerability, error) { + filter := []map[string]string{{"imageTag": imageID.Tag}} + pageSize := 100 + pageNumber := 1 + request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} + requestBody, _ := json.Marshal(request) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) + client := &http.Client{} + httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) + //fmt.Printf("**** token %s\n", armoCivAdaptor.feToken.Token) + resp, err := client.Do(httpRequest) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + fmt.Println(string(body)) + + /*err = json.Unmarshal(body, &feLoginResponse) + if err != nil { + return nil, err + }*/ + + return nil, nil + // https://api-dev.armo.cloud/api/v1/vulnerability/scanResultsSumSummary?customerGUID=1e3a88bf-92ce-44f8-914e-cbe71830d566%22 +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) { + return nil, nil +} From b93e7b9abfd858be9179da2eb58a6cd5b716d487 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 10 Jan 2022 22:48:00 +0200 Subject: [PATCH 15/22] take only the first result --- container-images/civ-armo-adaptor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container-images/civ-armo-adaptor.go b/container-images/civ-armo-adaptor.go index a1f69016..155f38d1 100644 --- a/container-images/civ-armo-adaptor.go +++ b/container-images/civ-armo-adaptor.go @@ -164,7 +164,7 @@ func (armoCivAdaptor *ArmoCivAdaptor) Login() error { func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageID *ContainerImageIdentifier) (*ContainerImageVulnerability, error) { filter := []map[string]string{{"imageTag": imageID.Tag}} - pageSize := 100 + pageSize := 1 pageNumber := 1 request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} requestBody, _ := json.Marshal(request) From f34f6dc51ed8d85d7dfb4f9f397cc749b8c7d42b Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Tue, 11 Jan 2022 00:42:26 +0200 Subject: [PATCH 16/22] GetImageVulnerabilty is working --- container-images/civ-adaptor-if.go | 32 +- container-images/civ-armo-adaptor.go | 119 +++- container-images/civ-armo-adaptor_test.go | 30 + containerscan/containerscan_mock.go | 90 +++ containerscan/containerscan_test.go | 90 +++ containerscan/datastructures.go | 37 ++ containerscan/datastructuresmethods.go | 51 ++ containerscan/elasticadapters.go | 141 +++++ containerscan/elasticdatastructures.go | 89 +++ containerscan/gojayunmarshaller.go | 246 ++++++++ containerscan/jsonrawscan.go | 525 ++++++++++++++++++ containerscan/rawdatastrucutres.go | 93 ++++ .../container-image-vulnerability-adaptor.md | 4 +- 13 files changed, 1527 insertions(+), 20 deletions(-) create mode 100644 container-images/civ-armo-adaptor_test.go create mode 100644 containerscan/containerscan_mock.go create mode 100644 containerscan/containerscan_test.go create mode 100644 containerscan/datastructures.go create mode 100644 containerscan/datastructuresmethods.go create mode 100644 containerscan/elasticadapters.go create mode 100644 containerscan/elasticdatastructures.go create mode 100644 containerscan/gojayunmarshaller.go create mode 100644 containerscan/jsonrawscan.go create mode 100644 containerscan/rawdatastrucutres.go diff --git a/container-images/civ-adaptor-if.go b/container-images/civ-adaptor-if.go index ab11bb4b..1fa4ee1b 100644 --- a/container-images/civ-adaptor-if.go +++ b/container-images/civ-adaptor-if.go @@ -1,6 +1,8 @@ package containerimages -import "time" +import ( + "time" +) type ContainerImageIdentifier struct { Registry string @@ -16,9 +18,29 @@ type ContainerImageScanStatus struct { LastScanDate time.Time } -type ContainerImageVulnerability struct { - ImageID ContainerImageIdentifier - // TBD +type FixedIn struct { + Name string `json:"name"` + ImgTag string `json:"imageTag"` + Version string `json:"version"` +} +type Vulnerability struct { + Name string `json:"name"` + RelatedPackageName string `json:"packageName"` + PackageVersion string `json:"packageVersion"` + Link string `json:"link"` + Description string `json:"description"` + Severity string `json:"severity"` + Metadata interface{} `json:"metadata"` + Fixes []FixedIn `json:"fixedIn"` + Relevancy string `json:"relevant"` // use the related enum + UrgentCount int `json:"urgent"` + NeglectedCount int `json:"neglected"` + HealthStatus string `json:"healthStatus"` +} + +type ContainerImageVulnerabilityReport struct { + ImageID ContainerImageIdentifier + Vulnerabilities []Vulnerability } type ContainerImageInformation struct { @@ -37,7 +59,7 @@ type IContainerImageVulnerabilityAdaptor interface { GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) - GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) } diff --git a/container-images/civ-armo-adaptor.go b/container-images/civ-armo-adaptor.go index 155f38d1..70340fbc 100644 --- a/container-images/civ-armo-adaptor.go +++ b/container-images/civ-armo-adaptor.go @@ -7,6 +7,8 @@ import ( "io/ioutil" "net/http" "strings" + + "github.com/armosec/kubescape/containerscan" ) type FeLoginData struct { @@ -94,10 +96,6 @@ func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { } httpRequest.Header.Set("Content-Type", "application/json") httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) - /*fmt.Println(requestUrl) - fmt.Println(httpRequest.Header.Get("Content-Type")) - fmt.Println(httpRequest.Header.Get("Authorization")) - fmt.Println(string(requestBody))*/ httpResponse, err := client.Do(httpRequest) if err != nil { return "", err @@ -162,7 +160,7 @@ func (armoCivAdaptor *ArmoCivAdaptor) Login() error { return nil } -func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageID *ContainerImageIdentifier) (*ContainerImageVulnerability, error) { +func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *ContainerImageIdentifier) (string, error) { filter := []map[string]string{{"imageTag": imageID.Tag}} pageSize := 1 pageNumber := 1 @@ -174,7 +172,72 @@ func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageID *ContainerI httpRequest.Header.Set("Content-Type", "application/json") httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) - //fmt.Printf("**** token %s\n", armoCivAdaptor.feToken.Token) + resp, err := client.Do(httpRequest) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + scanSummartResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response []containerscan.ElasticContainerScanSummaryResult `json:"response"` + Cursor string `json:"cursor"` + }{} + err = json.Unmarshal(body, &scanSummartResult) + if err != nil { + return "", err + } + + if len(scanSummartResult.Response) < pageSize { + return "", fmt.Errorf("did not get response for image %s", imageID.Tag) + } + + return scanSummartResult.Response[0].ContainerScanID, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) { + resultList := make([]ContainerImageVulnerabilityReport, 0) + for _, imageID := range imageIDs { + result, err := armoCivAdaptor.GetImageVulnerabilty(&imageID) + if err == nil { + resultList = append(resultList, *result) + } + } + return resultList, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilty(imageID *ContainerImageIdentifier) (*ContainerImageVulnerabilityReport, error) { + // First + containerScanId, err := armoCivAdaptor.getImageLastScanId(imageID) + if err != nil { + return nil, err + } + filter := []map[string]string{{"containersScanID": containerScanId}} + pageSize := 300 + pageNumber := 1 + request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} + requestBody, _ := json.Marshal(request) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) + client := &http.Client{} + httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return nil, err + } + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) resp, err := client.Do(httpRequest) if err != nil { return nil, err @@ -190,17 +253,47 @@ func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageID *ContainerI return nil, err } - fmt.Println(string(body)) - - /*err = json.Unmarshal(body, &feLoginResponse) + scanDetailsResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response containerscan.VulnerabilitiesList `json:"response"` + Cursor string `json:"cursor"` + }{} + err = json.Unmarshal(body, &scanDetailsResult) if err != nil { return nil, err - }*/ + } - return nil, nil - // https://api-dev.armo.cloud/api/v1/vulnerability/scanResultsSumSummary?customerGUID=1e3a88bf-92ce-44f8-914e-cbe71830d566%22 + vulnerabilities := make([]Vulnerability, len(scanDetailsResult.Response)) + for i, vulnerabilityEntry := range scanDetailsResult.Response { + vulnerabilities[i].Description = vulnerabilityEntry.Description + vulnerabilities[i].Fixes = make([]FixedIn, len(vulnerabilityEntry.Fixes)) + for j, fix := range vulnerabilityEntry.Fixes { + vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag + vulnerabilities[i].Fixes[j].Name = fix.Name + vulnerabilities[i].Fixes[j].Version = fix.Version + } + vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus + vulnerabilities[i].Link = vulnerabilityEntry.Link + vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata + vulnerabilities[i].Name = vulnerabilityEntry.Name + vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion + vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName + vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy + vulnerabilities[i].Severity = vulnerabilityEntry.Severity + vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount + } + + resultImageVulnerabilityReport := ContainerImageVulnerabilityReport{ + ImageID: *imageID, + Vulnerabilities: vulnerabilities, + } + + return &resultImageVulnerabilityReport, nil } -func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) { +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) { return nil, nil } diff --git a/container-images/civ-armo-adaptor_test.go b/container-images/civ-armo-adaptor_test.go new file mode 100644 index 00000000..bd9f2f9a --- /dev/null +++ b/container-images/civ-armo-adaptor_test.go @@ -0,0 +1,30 @@ +package containerimages + +import ( + "fmt" + "testing" +) + +func TestSum(t *testing.T) { + credentials := make(map[string]string) + credentials["clientId"] = "378754de-e70d-4c33-a475-1818d296ff26" + credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + credentials["accountId"] = "2ce5daf4-e28d-4e6e-a239-03fda048070b" + adaptor, err := CreateArmoAdaptor("armoui-dev.eudev3.cyberarmorsoft.com", credentials) + if err != nil { + t.Fatalf("Cannot initialize adaptor: %s", err) + } + err = adaptor.Login() + if err != nil { + t.Fatalf("Cannot login with adaptor: %s", err) + } + //fmt.Printf("Login successful: %s\n", adaptor.feToken.Token) + + imageVulnerabilityReport, err := adaptor.GetImageVulnerabilty(&ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) + if err != nil { + t.Fatalf("Cannot get vulnerabilities: %s", err) + } + for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities { + fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description) + } +} diff --git a/containerscan/containerscan_mock.go b/containerscan/containerscan_mock.go new file mode 100644 index 00000000..cac3471e --- /dev/null +++ b/containerscan/containerscan_mock.go @@ -0,0 +1,90 @@ +package containerscan + +import ( + "bytes" + "math/rand" + "time" + + "github.com/francoispqt/gojay" +) + +// GenerateContainerScanReportMock - generate a scan result +func GenerateContainerScanReportMock() ScanResultReport { + ds := ScanResultReport{ + WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping", + CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08", + ImgTag: "dreg.armo.cloud:443/demoservice:v16", + ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296", + Timestamp: time.Now().UnixNano(), + } + + ds.Layers = make(LayersList, 0) + layer := ScanResultLayer{} + GenerateContainerScanLayer(&layer) + ds.Layers = append(ds.Layers, layer) + return ds +} + +// GenerateContainerScanReportMock - generate a scan result +func GenerateContainerScanReportNoVulMock() ScanResultReport { + ds := ScanResultReport{ + WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping", + CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08", + ImgTag: "dreg.armo.cloud:443/demoservice:v16", + ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296", + Timestamp: time.Now().UnixNano(), + ContainerName: "shipping", + } + + ds.Layers = make(LayersList, 0) + layer := ScanResultLayer{LayerHash: "aaa"} + ds.Layers = append(ds.Layers, layer) + return ds +} + +var hash = []rune("abcdef0123456789") +var nums = []rune("0123456789") + +func randSeq(n int, bank []rune) string { + rand.Seed(time.Now().UnixNano()) + + b := make([]rune, n) + for i := range b { + b[i] = bank[rand.Intn(len(bank))] + } + return string(b) +} + +// GenerateContainerScanLayer - generate a layer with random vuls +func GenerateContainerScanLayer(layer *ScanResultLayer) { + layer.LayerHash = randSeq(32, hash) + layer.Vulnerabilities = make(VulnerabilitiesList, 0) + layer.Packages = make(LinuxPkgs, 0) + vuls := rand.Intn(10) + 1 + + for i := 0; i < vuls; i++ { + v := Vulnerability{} + GenerateVulnerability(&v) + layer.Vulnerabilities = append(layer.Vulnerabilities, v) + } + + pkg := LinuxPackage{PackageName: "coreutils"} + pkg.Files = make(PkgFiles, 0) + pf := PackageFile{Filename: "aa"} + pkg.Files = append(pkg.Files, pf) + layer.Packages = append(layer.Packages, pkg) +} + +// GenerateVulnerability - generate a vul (just diff "cve"'s) +func GenerateVulnerability(v *Vulnerability) error { + baseVul := " { \"name\": \"CVE-2014-9471\", \"imageTag\": \"debian:8\", \"link\": \"https://security-tracker.debian.org/tracker/CVE-2014-9471\", \"description\": \"The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the sdf\", \"severity\": \"Low\", \"metadata\": { \"NVD\": { \"CVSSv2\": { \"Score\": 7.5, \"Vectors\": \"AV:N/AC:L/Au:N/C:P/I:P\" } } }, \"fixedIn\": [ { \"name\": \"coreutils\", \"imageTag\": \"debian:8\", \"version\": \"8.23-1\" } ] }" + b := []byte(baseVul) + r := bytes.NewReader(b) + er := gojay.NewDecoder(r).DecodeObject(v) + v.RelatedPackageName = "coreutils" + v.Severity = HighSeverity + v.Relevancy = Irelevant + v.Name = "CVE-" + randSeq(4, nums) + "-" + randSeq(4, nums) + return er + +} diff --git a/containerscan/containerscan_test.go b/containerscan/containerscan_test.go new file mode 100644 index 00000000..6b668bd0 --- /dev/null +++ b/containerscan/containerscan_test.go @@ -0,0 +1,90 @@ +package containerscan + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/francoispqt/gojay" +) + +func TestDecodeScanWIthDangearousArtifacts(t *testing.T) { + rhs := &ScanResultReport{} + er := gojay.NewDecoder(strings.NewReader(nginxScanJSON)).DecodeObject(rhs) + if er != nil { + t.Errorf("decode failed due to: %v", er.Error()) + } + sumObj := rhs.Summarize() + if sumObj.Registry != "" { + t.Errorf("sumObj.Registry = %v", sumObj.Registry) + } + if sumObj.VersionImage != "nginx:1.18.0" { + t.Errorf("sumObj.VersionImage = %v", sumObj.Registry) + } + if sumObj.ImgTag != "nginx:1.18.0" { + t.Errorf("sumObj.ImgTag = %v", sumObj.ImgTag) + } + if sumObj.Status != "Success" { + t.Errorf("sumObj.Status = %v", sumObj.Status) + } + if len(sumObj.ListOfDangerousArtifcats) != 3 { + t.Errorf("sumObj.ListOfDangerousArtifcats = %v", sumObj.ListOfDangerousArtifcats) + } +} + +func TestUnmarshalScanReport(t *testing.T) { + ds := GenerateContainerScanReportMock() + str1 := ds.AsFNVHash() + rhs := &ScanResultReport{} + + bolB, _ := json.Marshal(ds) + r := bytes.NewReader(bolB) + + er := gojay.NewDecoder(r).DecodeObject(rhs) + if er != nil { + t.Errorf("marshalling failed due to: %v", er.Error()) + } + + if rhs.AsFNVHash() != str1 { + t.Errorf("marshalling failed different values after marshal:\nOriginal:\n%v\nParsed:\n%v\n\n===\n", string(bolB), rhs) + } +} + +func TestUnmarshalScanReport1(t *testing.T) { + ds := Vulnerability{} + if err := GenerateVulnerability(&ds); err != nil { + t.Errorf("%v\n%v\n", ds, err) + } +} + +func TestGetByPkgNameSuccess(t *testing.T) { + ds := GenerateContainerScanReportMock() + a := ds.Layers[0].GetFilesByPackage("coreutils") + if a != nil { + + fmt.Printf("%+v\n", *a) + } + +} + +func TestGetByPkgNameMissing(t *testing.T) { + ds := GenerateContainerScanReportMock() + a := ds.Layers[0].GetFilesByPackage("s") + if a != nil && len(*a) > 0 { + t.Errorf("expected - no such package should be in that layer %v\n\n; found - %v", ds, a) + } + +} + +func TestCalculateFixed(t *testing.T) { + res := CalculateFixed([]FixedIn{{ + Name: "", + ImgTag: "", + Version: "", + }}) + if 0 != res { + t.Errorf("wrong fix status: %v", res) + } +} diff --git a/containerscan/datastructures.go b/containerscan/datastructures.go new file mode 100644 index 00000000..af4e4e1c --- /dev/null +++ b/containerscan/datastructures.go @@ -0,0 +1,37 @@ +package containerscan + +const ( + //defines Relevancy as enum-like + Unknown = "Unknown" + Relevant = "Relevant" + Irelevant = "Irelevant" + NoSP = "No signature profile to compare" + + //Clair Severities + UnknownSeverity = "Unknown" + NegligibleSeverity = "Negligible" + LowSeverity = "Low" + MediumSeverity = "Medium" + HighSeverity = "High" + CriticalSeverity = "Critical" + + ContainerScanRedisPrefix = "_containerscan" +) + +var KnownSeverities = map[string]bool{ + UnknownSeverity: true, + NegligibleSeverity: true, + LowSeverity: true, + MediumSeverity: true, + HighSeverity: true, + CriticalSeverity: true, +} + +func CalculateFixed(Fixes []FixedIn) int { + for _, fix := range Fixes { + if fix.Version != "None" && fix.Version != "" { + return 1 + } + } + return 0 +} diff --git a/containerscan/datastructuresmethods.go b/containerscan/datastructuresmethods.go new file mode 100644 index 00000000..c7cc7d2a --- /dev/null +++ b/containerscan/datastructuresmethods.go @@ -0,0 +1,51 @@ +package containerscan + +import ( + "strings" + + "github.com/armosec/armoapi-go/armotypes" +) + +func (layer *ScanResultLayer) GetFilesByPackage(pkgname string) (files *PkgFiles) { + for _, pkg := range layer.Packages { + if pkg.PackageName == pkgname { + return &pkg.Files + } + } + + return &PkgFiles{} +} + +func (layer *ScanResultLayer) GetPackagesNames() []string { + pkgsNames := []string{} + for _, pkg := range layer.Packages { + pkgsNames = append(pkgsNames, pkg.PackageName) + } + return pkgsNames +} + +func (scanresult *ScanResultReport) GetDesignatorsNContext() (*armotypes.PortalDesignator, []armotypes.ArmoContext) { + designatorsObj := armotypes.AttributesDesignatorsFromWLID(scanresult.WLID) + designatorsObj.Attributes["containerName"] = scanresult.ContainerName + designatorsObj.Attributes["customerGUID"] = scanresult.CustomerGUID + contextObj := armotypes.DesignatorToArmoContext(designatorsObj, "designators") + return designatorsObj, contextObj +} + +func (scanresult *ScanResultReport) Validate() bool { + if scanresult.CustomerGUID == "" || (scanresult.ImgHash == "" && scanresult.ImgTag == "") || scanresult.Timestamp <= 0 { + return false + } + + //TODO validate layers & vuls + + return true +} + +func (v *Vulnerability) IsRCE() bool { + desc := strings.ToLower(v.Description) + + isRCE := strings.Contains(v.Description, "RCE") + + return isRCE || strings.Contains(desc, "remote code execution") || strings.Contains(desc, "remote command execution") || strings.Contains(desc, "arbitrary code") || strings.Contains(desc, "code execution") || strings.Contains(desc, "code injection") || strings.Contains(desc, "command injection") || strings.Contains(desc, "inject arbitrary commands") +} diff --git a/containerscan/elasticadapters.go b/containerscan/elasticadapters.go new file mode 100644 index 00000000..c5bb4ef8 --- /dev/null +++ b/containerscan/elasticadapters.go @@ -0,0 +1,141 @@ +package containerscan + +import ( + "github.com/armosec/armoapi-go/armotypes" + cautils "github.com/armosec/utils-k8s-go/armometadata" +) + +// ToFlatVulnerabilities - returnsgit p +func (scanresult *ScanResultReport) ToFlatVulnerabilities() []*ElasticContainerVulnerabilityResult { + vuls := make([]*ElasticContainerVulnerabilityResult, 0) + vul2indx := make(map[string]int) + scanID := scanresult.AsFNVHash() + designatorsObj, ctxList := scanresult.GetDesignatorsNContext() + for _, layer := range scanresult.Layers { + for _, vul := range layer.Vulnerabilities { + esLayer := ESLayer{LayerHash: layer.LayerHash, ParentLayerHash: layer.ParentLayerHash} + if indx, isOk := vul2indx[vul.Name]; isOk { + vuls[indx].Layers = append(vuls[indx].Layers, esLayer) + continue + } + result := &ElasticContainerVulnerabilityResult{WLID: scanresult.WLID, + Timestamp: scanresult.Timestamp, + Designators: *designatorsObj, + Context: ctxList} + + result.Vulnerability = vul + result.Layers = make([]ESLayer, 0) + result.Layers = append(result.Layers, esLayer) + result.ContainerScanID = scanID + + result.IsFixed = CalculateFixed(vul.Fixes) + result.RelevantLinks = append(result.RelevantLinks, "https://nvd.nist.gov/vuln/detail/"+vul.Name) + result.RelevantLinks = append(result.RelevantLinks, vul.Link) + result.Vulnerability.Link = "https://nvd.nist.gov/vuln/detail/" + vul.Name + + result.Categories.IsRCE = result.IsRCE() + vuls = append(vuls, result) + vul2indx[vul.Name] = len(vuls) - 1 + + } + } + // find first introduced + for i, v := range vuls { + earlyLayer := "" + for _, layer := range v.Layers { + if layer.ParentLayerHash == earlyLayer { + earlyLayer = layer.LayerHash + } + } + vuls[i].IntroducedInLayer = earlyLayer + + } + + return vuls +} + +func (scanresult *ScanResultReport) Summarize() *ElasticContainerScanSummaryResult { + designatorsObj, ctxList := scanresult.GetDesignatorsNContext() + summary := &ElasticContainerScanSummaryResult{ + Designators: *designatorsObj, + Context: ctxList, + CustomerGUID: scanresult.CustomerGUID, + ImgTag: scanresult.ImgTag, + ImgHash: scanresult.ImgHash, + WLID: scanresult.WLID, + Timestamp: scanresult.Timestamp, + ContainerName: scanresult.ContainerName, + ContainerScanID: scanresult.AsFNVHash(), + ListOfDangerousArtifcats: scanresult.ListOfDangerousArtifcats, + } + + summary.Cluster = designatorsObj.Attributes[armotypes.AttributeCluster] + summary.Namespace = designatorsObj.Attributes[armotypes.AttributeNamespace] + + imageInfo, e2 := cautils.ImageTagToImageInfo(scanresult.ImgTag) + if e2 == nil { + summary.Registry = imageInfo.Registry + summary.VersionImage = imageInfo.VersionImage + } + + summary.PackagesName = make([]string, 0) + + severitiesStats := map[string]SeverityStats{} + + uniqueVulsMap := make(map[string]bool) + for _, layer := range scanresult.Layers { + summary.PackagesName = append(summary.PackagesName, (layer.GetPackagesNames())...) + for _, vul := range layer.Vulnerabilities { + if _, isOk := uniqueVulsMap[vul.Name]; isOk { + continue + } + uniqueVulsMap[vul.Name] = true + + // TODO: maybe add all severities just to have a placeholders + if !KnownSeverities[vul.Severity] { + vul.Severity = UnknownSeverity + } + + vulnSeverityStats, ok := severitiesStats[vul.Severity] + if !ok { + vulnSeverityStats = SeverityStats{Severity: vul.Severity} + } + + vulnSeverityStats.TotalCount++ + summary.TotalCount++ + isFixed := CalculateFixed(vul.Fixes) > 0 + if isFixed { + vulnSeverityStats.FixAvailableOfTotalCount++ + summary.FixAvailableOfTotalCount++ + } + isRCE := vul.IsRCE() + if isRCE { + vulnSeverityStats.RCECount++ + summary.RCECount++ + } + if vul.Relevancy == Relevant { + vulnSeverityStats.RelevantCount++ + summary.RelevantCount++ + if isFixed { + vulnSeverityStats.FixAvailableForRelevantCount++ + summary.FixAvailableForRelevantCount++ + } + + } + severitiesStats[vul.Severity] = vulnSeverityStats + } + } + summary.Status = "Success" + + // if criticalStats, hasCritical := severitiesStats[CriticalSeverity]; hasCritical && criticalStats.TotalCount > 0 { + // summary.Status = "Fail" + // } + // if highStats, hasHigh := severitiesStats[HighSeverity]; hasHigh && highStats.RelevantCount > 0 { + // summary.Status = "Fail" + // } + + for sever := range severitiesStats { + summary.SeveritiesStats = append(summary.SeveritiesStats, severitiesStats[sever]) + } + return summary +} diff --git a/containerscan/elasticdatastructures.go b/containerscan/elasticdatastructures.go new file mode 100644 index 00000000..46948ae4 --- /dev/null +++ b/containerscan/elasticdatastructures.go @@ -0,0 +1,89 @@ +package containerscan + +import "github.com/armosec/armoapi-go/armotypes" + +type ElasticContainerVulnerabilityResult struct { + Designators armotypes.PortalDesignator `json:"designators"` + Context []armotypes.ArmoContext `json:"context"` + + WLID string `json:"wlid"` + ContainerScanID string `json:"containersScanID"` + Layers []ESLayer `json:"layers"` + Timestamp int64 `json:"timestamp"` + IsFixed int `json:"isFixed"` + IntroducedInLayer string `json:"layerHash"` + RelevantLinks []string `json:"links"` // shitty SE practice + + Vulnerability `json:",inline"` +} + +type ESLayer struct { + LayerHash string `json:"layerHash"` + ParentLayerHash string `json:"parentLayerHash"` +} + +type SeverityStats struct { + Severity string `json:"severity,omitempty"` + TotalCount int64 `json:"total"` + FixAvailableOfTotalCount int64 `json:"fixedTotal"` + RelevantCount int64 `json:"totalRelevant"` + FixAvailableForRelevantCount int64 `json:"fixedRelevant"` + RCECount int64 `json:"rceTotal"` + UrgentCount int64 `json:"urgent"` + NeglectedCount int64 `json:"neglected"` + HealthStatus string `json:"healthStatus"` +} + +type ElasticContainerScanSeveritySummary struct { + Designators armotypes.PortalDesignator `json:"designators"` + Context []armotypes.ArmoContext `json:"context"` + + SeverityStats + CustomerGUID string `json:"customerGUID"` + ContainerScanID string `json:"containersScanID"` + Timestamp int64 `json:"timestamp"` + WLID string `json:"wlid"` + ImgTag string `json:"imageTag"` + ImgHash string `json:"imageHash"` + Cluster string `json:"cluster"` + Namespace string `json:"namespace"` + ContainerName string `json:"containerName"` + Status string `json:"status"` + Registry string `json:"registry"` + VersionImage string `json:"versionImage"` + Version string `json:"version"` + DayDate string `json:"dayDate"` +} + +type ElasticContainerScanSummaryResult struct { + SeverityStats + Designators armotypes.PortalDesignator `json:"designators"` + Context []armotypes.ArmoContext `json:"context"` + + CustomerGUID string `json:"customerGUID"` + ContainerScanID string `json:"containersScanID"` + + Timestamp int64 `json:"timestamp"` + WLID string `json:"wlid"` + ImgTag string `json:"imageTag"` + ImgHash string `json:"imageHash"` + Cluster string `json:"cluster"` + Namespace string `json:"namespace"` + ContainerName string `json:"containerName"` + PackagesName []string `json:"packages"` + + ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"` + + Status string `json:"status"` + + Registry string `json:"registry"` + VersionImage string `json:"versionImage"` + + SeveritiesStats []SeverityStats `json:"severitiesStats"` + + Version string `json:"version"` +} + +func (summary *ElasticContainerScanSummaryResult) Validate() bool { + return summary.CustomerGUID != "" && summary.ContainerScanID != "" && (summary.ImgTag != "" || summary.ImgHash != "") && summary.Timestamp > 0 +} diff --git a/containerscan/gojayunmarshaller.go b/containerscan/gojayunmarshaller.go new file mode 100644 index 00000000..a79f5c08 --- /dev/null +++ b/containerscan/gojayunmarshaller.go @@ -0,0 +1,246 @@ +package containerscan + +import ( + "github.com/francoispqt/gojay" +) + +/* + responsible on fast unmarshaling of various COMMON containerscan structures and substructures + +*/ + +// UnmarshalJSONObject - File inside a pkg +func (file *PackageFile) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "name": + err = dec.String(&(file.Filename)) + } + return err + +} + +func (files *PkgFiles) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := PackageFile{} + if err := dec.Object(&lae); err != nil { + return err + } + + *files = append(*files, lae) + return nil +} + +func (file *PackageFile) NKeys() int { + return 0 +} + +// UnmarshalJSONObject--- Package +func (pkgnx *LinuxPackage) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "packageName": + err = dec.String(&(pkgnx.PackageName)) + + case "version": + err = dec.String(&(pkgnx.PackageVersion)) + + case "files": + err = dec.Array(&(pkgnx.Files)) + } + return err +} + +func (file *LinuxPackage) NKeys() int { + return 0 +} + +func (pkgs *LinuxPkgs) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := LinuxPackage{} + if err := dec.Object(&lae); err != nil { + return err + } + + *pkgs = append(*pkgs, lae) + return nil +} + +//--------Vul fixed in---------------------------------- +func (fx *FixedIn) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "name": + err = dec.String(&(fx.Name)) + + case "imageTag": + err = dec.String(&(fx.ImgTag)) + case "version": + err = dec.String(&(fx.Version)) + } + return err +} + +func (t *VulFixes) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := FixedIn{} + if err := dec.Object(&lae); err != nil { + return err + } + + *t = append(*t, lae) + return nil +} + +func (file *FixedIn) NKeys() int { + return 0 +} + +//------ VULNERABIlITy --------------------- + +// Name string `json:"name"` +// ImgHash string `json:"imageHash"` +// ImgTag string `json:"imageTag",omitempty` +// RelatedPackageName string `json:"packageName"` +// PackageVersion string `json:"packageVersion"` +// Link string `json:"link"` +// Description string `json:"description"` +// Severity string `json:"severity"` +// Metadata interface{} `json:"metadata",omitempty` +// Fixes VulFixes `json:"fixedIn",omitempty` +// Relevancy string `json:"relevant"` // use the related enum + +func (v *Vulnerability) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "name": + err = dec.String(&(v.Name)) + + case "imageTag": + err = dec.String(&(v.ImgTag)) + case "imageHash": + err = dec.String(&(v.ImgHash)) + + case "packageName": + err = dec.String(&(v.RelatedPackageName)) + + case "packageVersion": + err = dec.String(&(v.PackageVersion)) + + case "link": + err = dec.String(&(v.Link)) + + case "description": + err = dec.String(&(v.Description)) + + case "severity": + err = dec.String(&(v.Severity)) + + case "relevant": + err = dec.String(&(v.Relevancy)) + + case "fixedIn": + err = dec.Array(&(v.Fixes)) + + case "metadata": + err = dec.Interface(&(v.Metadata)) + } + + return err +} + +func (t *VulnerabilitiesList) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := Vulnerability{} + if err := dec.Object(&lae); err != nil { + return err + } + + *t = append(*t, lae) + return nil +} + +func (v *Vulnerability) NKeys() int { + return 0 +} + +//---------Layer Object---------------------------------- +// type ScanResultLayer struct { +// LayerHash string `json:layerHash` +// Vulnerabilities []Vulnerability `json:vulnerabilities` +// Packages []LinuxPackage `json:packageToFile` +// } + +func (scan *ScanResultLayer) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + // case "timestamp": + // err = dec.Time(&(reporter.Timestamp), time.RFC3339) + // reporter.Timestamp = reporter.Timestamp.Local() + case "layerHash": + err = dec.String(&(scan.LayerHash)) + + case "parentLayerHash": + err = dec.String(&(scan.ParentLayerHash)) + + case "vulnerabilities": + err = dec.Array(&(scan.Vulnerabilities)) + case "packageToFile": + err = dec.Array(&(scan.Packages)) + } + return err +} + +func (t *LayersList) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := ScanResultLayer{} + if err := dec.Object(&lae); err != nil { + return err + } + + *t = append(*t, lae) + return nil +} + +func (scan *ScanResultLayer) NKeys() int { + return 0 +} + +//---------------------SCAN RESULT-------------------------------------------------------------------------- + +// type ScanResultReport struct { +// CustomerGUID string `json:customerGuid` +// ImgTag string `json:imageTag,omitempty` +// ImgHash string `json:imageHash` +// WLID string `json:wlid` +// Timestamp int `json:customerGuid` +// Layers []ScanResultLayer `json:layers` +// ContainerName +// } + +func (scan *ScanResultReport) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + // case "timestamp": + // err = dec.Time(&(reporter.Timestamp), time.RFC3339) + // reporter.Timestamp = reporter.Timestamp.Local() + case "customerGUID": + err = dec.String(&(scan.CustomerGUID)) + case "imageTag": + err = dec.String(&(scan.ImgTag)) + case "imageHash": + err = dec.String(&(scan.ImgHash)) + case "wlid": + err = dec.String(&(scan.WLID)) + case "containerName": + err = dec.String(&(scan.ContainerName)) + case "timestamp": + err = dec.Int64(&(scan.Timestamp)) + case "layers": + err = dec.Array(&(scan.Layers)) + + case "listOfDangerousArtifcats": + err = dec.SliceString(&(scan.ListOfDangerousArtifcats)) + + } + return err +} + +func (scan *ScanResultReport) NKeys() int { + return 0 +} diff --git a/containerscan/jsonrawscan.go b/containerscan/jsonrawscan.go new file mode 100644 index 00000000..f0c946fb --- /dev/null +++ b/containerscan/jsonrawscan.go @@ -0,0 +1,525 @@ +package containerscan + +var nginxScanJSON = ` +{ + "customerGUID": "1e3a88bf-92ce-44f8-914e-cbe71830d566", + "imageTag": "nginx:1.18.0", + "imageHash": "", + "wlid": "wlid://cluster-test/namespace-test/deployment-davidg", + "containerName": "nginx-1", + "timestamp": 1628091365, + "layers": [ + { + "layerHash": "sha256:f7ec5a41d630a33a2d1db59b95d89d93de7ae5a619a3a8571b78457e48266eba", + "parentLayerHash": "", + "vulnerabilities": [ + { + "name": "CVE-2009-0854", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "dash", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-0854", + "description": "Untrusted search path vulnerability in dash 0.5.4, when used as a login shell, allows local users to execute arbitrary code via a Trojan horse .profile file in the current working directory.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2019-13627", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libgcrypt20", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13627", + "description": "It was discovered that there was a ECDSA timing attack in the libgcrypt20 cryptographic library. Version affected: 1.8.4-5, 1.7.6-2+deb9u3, and 1.6.3-2+deb8u4. Versions fixed: 1.8.5-2 and 1.6.3-2+deb8u7.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-33560", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libgcrypt20", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-33560", + "description": "Libgcrypt before 1.8.8 and 1.9.x before 1.9.3 mishandles ElGamal encryption because it lacks exponent blinding to address a side-channel attack against mpi_powm, and the window size is not chosen appropriately. (There is also an interoperability problem because the selection of the k integer value does not properly consider the differences between basic ElGamal encryption and generalized ElGamal encryption.) This, for example, affects use of ElGamal in OpenPGP.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:1.8.4-5+deb10u1" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-3345", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libgcrypt20", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3345", + "description": "_gcry_md_block_write in cipher/hash-common.c in Libgcrypt version 1.9.0 has a heap-based buffer overflow when the digest final function sets a large count value. It is recommended to upgrade to 1.9.1 or later.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2010-0834", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "base-files", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0834", + "description": "The base-files package before 5.0.0ubuntu7.1 on Ubuntu 9.10 and before 5.0.0ubuntu20.10.04.2 on Ubuntu 10.04 LTS, as shipped on Dell Latitude 2110 netbooks, does not require authentication for package installation, which allows remote archive servers and man-in-the-middle attackers to execute arbitrary code via a crafted package.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2018-6557", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "base-files", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-6557", + "description": "The MOTD update script in the base-files package in Ubuntu 18.04 LTS before 10.1ubuntu2.2, and Ubuntu 18.10 before 10.1ubuntu6 incorrectly handled temporary files. A local attacker could use this issue to cause a denial of service, or possibly escalate privileges if kernel symlink restrictions were disabled.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2013-0223", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0223", + "description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the join command, when using the -i switch, which triggers a stack-based buffer overflow in the alloca function.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2015-4041", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-4041", + "description": "The keycompare_mb function in sort.c in sort in GNU Coreutils through 8.23 on 64-bit platforms performs a size calculation without considering the number of bytes occupied by multibyte characters, which allows attackers to cause a denial of service (heap-based buffer overflow and application crash) or possibly have unspecified other impact via long UTF-8 strings.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2009-4135", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-4135", + "description": "The distcheck rule in dist-check.mk in GNU coreutils 5.2.1 through 8.1 allows local users to gain privileges via a symlink attack on a file in a directory tree under /tmp.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2015-4042", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-4042", + "description": "Integer overflow in the keycompare_mb function in sort.c in sort in GNU Coreutils through 8.23 might allow attackers to cause a denial of service (application crash) or possibly have unspecified other impact via long strings.", + "severity": "Critical", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2013-0221", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0221", + "description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the sort command, when using the (1) -d or (2) -M switch, which triggers a stack-based buffer overflow in the alloca function.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2013-0222", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0222", + "description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the uniq command, which triggers a stack-based buffer overflow in the alloca function.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2016-2781", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2781", + "description": "chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2017-18018", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-18018", + "description": "In GNU Coreutils through 8.29, chown-core.c in chown and chgrp does not prevent replacement of a plain file with a symlink during use of the POSIX \"-R -L\" options, which allows local users to modify the ownership of arbitrary files by leveraging a race condition.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-20193", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "tar", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-20193", + "description": "A flaw was found in the src/list.c of tar 1.33 and earlier. This flaw allows an attacker who can submit a crafted input file to tar to cause uncontrolled consumption of memory. The highest threat from this vulnerability is to system availability.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2005-2541", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "tar", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-2541", + "description": "Tar 1.15.1 does not properly warn the user when extracting setuid or setgid files, which may allow local users or remote attackers to gain privileges.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2019-9923", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "tar", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9923", + "description": "pax_decode_header in sparse.c in GNU Tar before 1.32 had a NULL pointer dereference when parsing certain archives that have malformed extended headers.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2018-1000654", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libtasn1-6", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000654", + "description": "GNU Libtasn1-4.13 libtasn1-4.13 version libtasn1-4.13, libtasn1-4.12 contains a DoS, specifically CPU usage will reach 100% when running asn1Paser against the POC due to an issue in _asn1_expand_object_id(p_tree), after a long time, the program will be killed. This attack appears to be exploitable via parsing a crafted file.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2011-3374", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "apt", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3374", + "description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-37600", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "util-linux", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37600", + "description": "An integer overflow in util-linux through 2.37.1 can potentially cause a buffer overflow if an attacker were able to use system resources in a way that leads to a large number in the /proc/sysvipc/sem file.", + "severity": "Unknown", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2007-0822", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "util-linux", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0822", + "description": "umount, when running with the Linux 2.6.15 kernel on Slackware Linux 10.2, allows local users to trigger a NULL dereference and application crash by invoking the program with a pathname for a USB pen drive that was mounted and then physically removed, which might allow the users to obtain sensitive information, including core file contents.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2004-1349", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "gzip", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-1349", + "description": "gzip before 1.3 in Solaris 8, when called with the -f or -force flags, will change the permissions of files that are hard linked to the target files, which allows local users to view or modify these files.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2004-0603", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "gzip", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-0603", + "description": "gzexe in gzip 1.3.3 and earlier will execute an argument when the creation of a temp file fails instead of exiting the program, which could allow remote attackers or local users to execute arbitrary commands, a different vulnerability than CVE-1999-1332.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2010-0002", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "bash", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0002", + "description": "The /etc/profile.d/60alias.sh script in the Mandriva bash package for Bash 2.05b, 3.0, 3.2, 3.2.48, and 4.0 enables the --show-control-chars option in LS_OPTIONS, which allows local users to send escape sequences to terminal emulators, or hide the existence of a file, via a crafted filename.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2019-18276", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "bash", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276", + "description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + } + ], + "packageToFile": null + }, + { + "layerHash": "sha256:0b20d28b5eb3007f70c43cdd8efcdb04016aa193192e5911cda5b7590ffaa635", + "parentLayerHash": "sha256:f7ec5a41d630a33a2d1db59b95d89d93de7ae5a619a3a8571b78457e48266eba", + "vulnerabilities": [], + "packageToFile": null + }, + { + "layerHash": "sha256:1576642c97761adf346890bf67c43473217160a9a203ef47d0bc6020af652798", + "parentLayerHash": "sha256:0b20d28b5eb3007f70c43cdd8efcdb04016aa193192e5911cda5b7590ffaa635", + "vulnerabilities": [], + "packageToFile": null + }, + { + "layerHash": "sha256:c12a848bad84d57e3f5faafab5880484434aee3bf8bdde4d519753b7c81254fd", + "parentLayerHash": "sha256:1576642c97761adf346890bf67c43473217160a9a203ef47d0bc6020af652798", + "vulnerabilities": [], + "packageToFile": null + }, + { + "layerHash": "sha256:03f221d9cf00a7077231c6dcac3c95182727c7e7fd44fd2b2e882a01dcda2d70", + "parentLayerHash": "sha256:c12a848bad84d57e3f5faafab5880484434aee3bf8bdde4d519753b7c81254fd", + "vulnerabilities": [], + "packageToFile": null + } + ], + "listOfDangerousArtifcats": [ + "bin/dash", + "bin/bash", + "usr/bin/curl" + ] +} +` diff --git a/containerscan/rawdatastrucutres.go b/containerscan/rawdatastrucutres.go new file mode 100644 index 00000000..fd72ed71 --- /dev/null +++ b/containerscan/rawdatastrucutres.go @@ -0,0 +1,93 @@ +package containerscan + +import ( + "fmt" + "hash/fnv" +) + +//!!!!!!!!!!!!EVERY CHANGE IN THESE STRUCTURES => CHANGE gojayunmarshaller ASWELL!!!!!!!!!!!!!!!!!!!!!!!! + +// ScanResultReport - the report given from scanner to event receiver +type ScanResultReport struct { + CustomerGUID string `json:"customerGUID"` + ImgTag string `json:"imageTag"` + ImgHash string `json:"imageHash"` + WLID string `json:"wlid"` + ContainerName string `json:"containerName"` + Timestamp int64 `json:"timestamp"` + Layers LayersList `json:"layers"` + ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"` +} + +// ScanResultLayer - represents a single layer from container scan result +type ScanResultLayer struct { + LayerHash string `json:"layerHash"` + ParentLayerHash string `json:"parentLayerHash"` + Vulnerabilities VulnerabilitiesList `json:"vulnerabilities"` + Packages LinuxPkgs `json:"packageToFile"` +} + +type VulnerabilityCategory struct { + IsRCE bool `json:"isRce"` +} + +// Vulnerability - a vul object +type Vulnerability struct { + Name string `json:"name"` + ImgHash string `json:"imageHash"` + ImgTag string `json:"imageTag"` + RelatedPackageName string `json:"packageName"` + PackageVersion string `json:"packageVersion"` + Link string `json:"link"` + Description string `json:"description"` + Severity string `json:"severity"` + Metadata interface{} `json:"metadata"` + Fixes VulFixes `json:"fixedIn"` + Relevancy string `json:"relevant"` // use the related enum + UrgentCount int `json:"urgent"` + NeglectedCount int `json:"neglected"` + HealthStatus string `json:"healthStatus"` + Categories VulnerabilityCategory `json:"categories"` +} + +// FixedIn when and which pkg was fixed (which version as well) +type FixedIn struct { + Name string `json:"name"` + ImgTag string `json:"imageTag"` + Version string `json:"version"` +} + +// LinuxPackage- Linux package representation +type LinuxPackage struct { + PackageName string `json:"packageName"` + Files PkgFiles `json:"files"` + PackageVersion string `json:"version"` +} + +// PackageFile - s.e +type PackageFile struct { + Filename string `json:"name"` +} + +// types to provide unmarshalling: + +//VulnerabilitiesList -s.e +type LayersList []ScanResultLayer + +//VulnerabilitiesList -s.e +type VulnerabilitiesList []Vulnerability + +//LinuxPkgs - slice of linux pkgs +type LinuxPkgs []LinuxPackage + +//VulFixes - information bout when/how this vul was fixed +type VulFixes []FixedIn + +//PkgFiles - slice of files belong to specific pkg +type PkgFiles []PackageFile + +func (v *ScanResultReport) AsFNVHash() string { + hasher := fnv.New64a() + hasher.Write([]byte(fmt.Sprintf("%v", *v))) + return fmt.Sprintf("%v", hasher.Sum64()) +} diff --git a/docs/proposals/container-image-vulnerability-adaptor.md b/docs/proposals/container-image-vulnerability-adaptor.md index f3dc4b28..35976514 100644 --- a/docs/proposals/container-image-vulnerability-adaptor.md +++ b/docs/proposals/container-image-vulnerability-adaptor.md @@ -89,7 +89,7 @@ type ContainerImageScanStatus struct { LastScanDate time.Time } -type ContainerImageVulnerability struct { +type ContainerImageVulnerabilityReport struct { ImageID ContainerImageIdentifier // TBD } @@ -110,7 +110,7 @@ type IContainerImageVulnerabilityAdaptor interface { GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) - GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) } From 72860deb0fc58868769df658a9b7a28bf4377fa7 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 11 Jan 2022 10:43:53 +0200 Subject: [PATCH 17/22] update struct, adding mock struct --- container-images/civ-armo-adaptor.go | 299 ------------------ container-images/civ-armo-adaptor_test.go | 30 -- go.mod | 4 +- registryadaptors/armosec/v1/civarmoadaptor.go | 170 ++++++++++ .../armosec/v1/civarmoadaptor_test.go | 60 ++++ .../armosec/v1/civarmoadaptormock.go | 78 +++++ .../armosec/v1/civarmoadaptorutils.go | 122 +++++++ .../armosec/v1/datastructures.go | 32 +- .../registryvulnerabilities/datastructures.go | 17 +- .../registryvulnerabilities/interfaces.go | 17 + 10 files changed, 481 insertions(+), 348 deletions(-) delete mode 100644 container-images/civ-armo-adaptor.go delete mode 100644 container-images/civ-armo-adaptor_test.go create mode 100644 registryadaptors/armosec/v1/civarmoadaptor.go create mode 100644 registryadaptors/armosec/v1/civarmoadaptor_test.go create mode 100644 registryadaptors/armosec/v1/civarmoadaptormock.go create mode 100644 registryadaptors/armosec/v1/civarmoadaptorutils.go rename container-images/armo-datastructures.go => registryadaptors/armosec/v1/datastructures.go (67%) rename container-images/civ-adaptor-if.go => registryadaptors/registryvulnerabilities/datastructures.go (64%) create mode 100644 registryadaptors/registryvulnerabilities/interfaces.go diff --git a/container-images/civ-armo-adaptor.go b/container-images/civ-armo-adaptor.go deleted file mode 100644 index 70340fbc..00000000 --- a/container-images/civ-armo-adaptor.go +++ /dev/null @@ -1,299 +0,0 @@ -package containerimages - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" - - "github.com/armosec/kubescape/containerscan" -) - -type FeLoginData struct { - Secret string `json:"secret"` - ClientId string `json:"clientId"` -} - -type FeLoginResponse struct { - Token string `json:"accessToken"` - RefreshToken string `json:"refreshToken"` - ExpiresIn int32 `json:"expiresIn"` - Expires string `json:"expires"` -} - -type ArmoBeConfiguration struct { - BackendUrl string `json:"backend"` - AuthUrl string `json:"authUrl"` -} -type ArmoSelectCustomer struct { - SelectedCustomerGuid string `json:"selectedCustomer"` -} - -type ArmoCivAdaptor struct { - registry string - accountId string - clientId string - accessKey string - feToken FeLoginResponse - armoUrls ArmoBeConfiguration - authCookie string -} - -func CreateArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { - var accountId string - var accessKey string - var clientId string - var ok bool - if accountId, ok = credentials["accountId"]; !ok { - return nil, fmt.Errorf("define accountId in credentials") - } - if clientId, ok = credentials["clientId"]; !ok { - return nil, fmt.Errorf("define clientId in credentials") - } - if accessKey, ok = credentials["accessKey"]; !ok { - return nil, fmt.Errorf("define accessKey in credentials") - } - armoCivAdaptor := ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey} - err := armoCivAdaptor.initializeUrls() - if err != nil { - return nil, err - } - return &armoCivAdaptor, nil -} - -func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error { - configUrl := fmt.Sprintf("https://%s/assets/configs/config.json", armoCivAdaptor.registry) - resp, err := http.Get(configUrl) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("cannot retrieve backend config file %s: status %d", configUrl, resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - err = json.Unmarshal(body, &armoCivAdaptor.armoUrls) - if err != nil { - return err - } - return nil - -} - -func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { - selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountId} - requestBody, _ := json.Marshal(selectCustomer) - requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl) - client := &http.Client{} - httpRequest, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(requestBody)) - if err != nil { - return "", err - } - httpRequest.Header.Set("Content-Type", "application/json") - httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) - httpResponse, err := client.Do(httpRequest) - if err != nil { - return "", err - } - defer httpResponse.Body.Close() - if httpResponse.StatusCode != http.StatusOK { - return "", fmt.Errorf("error getting cookie at %s: status %d", requestUrl, httpResponse.StatusCode) - } - - cookies := httpResponse.Header.Get("set-cookie") - if len(cookies) == 0 { - return "", fmt.Errorf("no cookie field in response from %s", requestUrl) - } - - authCookie := "" - for _, cookie := range strings.Split(cookies, ";") { - kv := strings.Split(cookie, "=") - if kv[0] == "auth" { - authCookie = kv[1] - } - } - - if len(authCookie) == 0 { - return "", fmt.Errorf("no auth cookie field in response from %s", requestUrl) - } - - return authCookie, nil -} - -func (armoCivAdaptor *ArmoCivAdaptor) Login() error { - feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey} - body, _ := json.Marshal(feLoginData) - - authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl) - resp, err := http.Post(authApiTokenEndpoint, "application/json", bytes.NewBuffer(body)) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("error authenticating: %d", resp.StatusCode) - } - - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - var feLoginResponse FeLoginResponse - err = json.Unmarshal(responseBody, &feLoginResponse) - armoCivAdaptor.feToken = feLoginResponse - if err != nil { - return err - } - /* Now we have JWT */ - - armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie() - if err != nil { - return err - } - - return nil -} - -func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *ContainerImageIdentifier) (string, error) { - filter := []map[string]string{{"imageTag": imageID.Tag}} - pageSize := 1 - pageNumber := 1 - request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} - requestBody, _ := json.Marshal(request) - requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) - client := &http.Client{} - httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) - httpRequest.Header.Set("Content-Type", "application/json") - httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) - httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) - resp, err := client.Do(httpRequest) - if err != nil { - return "", err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - scanSummartResult := struct { - Total struct { - Value int `json:"value"` - Relation string `json:"relation"` - } `json:"total"` - Response []containerscan.ElasticContainerScanSummaryResult `json:"response"` - Cursor string `json:"cursor"` - }{} - err = json.Unmarshal(body, &scanSummartResult) - if err != nil { - return "", err - } - - if len(scanSummartResult.Response) < pageSize { - return "", fmt.Errorf("did not get response for image %s", imageID.Tag) - } - - return scanSummartResult.Response[0].ContainerScanID, nil -} - -func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) { - resultList := make([]ContainerImageVulnerabilityReport, 0) - for _, imageID := range imageIDs { - result, err := armoCivAdaptor.GetImageVulnerabilty(&imageID) - if err == nil { - resultList = append(resultList, *result) - } - } - return resultList, nil -} - -func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerabilty(imageID *ContainerImageIdentifier) (*ContainerImageVulnerabilityReport, error) { - // First - containerScanId, err := armoCivAdaptor.getImageLastScanId(imageID) - if err != nil { - return nil, err - } - filter := []map[string]string{{"containersScanID": containerScanId}} - pageSize := 300 - pageNumber := 1 - request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} - requestBody, _ := json.Marshal(request) - requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) - client := &http.Client{} - httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) - if err != nil { - return nil, err - } - httpRequest.Header.Set("Content-Type", "application/json") - httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) - httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) - resp, err := client.Do(httpRequest) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - scanDetailsResult := struct { - Total struct { - Value int `json:"value"` - Relation string `json:"relation"` - } `json:"total"` - Response containerscan.VulnerabilitiesList `json:"response"` - Cursor string `json:"cursor"` - }{} - err = json.Unmarshal(body, &scanDetailsResult) - if err != nil { - return nil, err - } - - vulnerabilities := make([]Vulnerability, len(scanDetailsResult.Response)) - for i, vulnerabilityEntry := range scanDetailsResult.Response { - vulnerabilities[i].Description = vulnerabilityEntry.Description - vulnerabilities[i].Fixes = make([]FixedIn, len(vulnerabilityEntry.Fixes)) - for j, fix := range vulnerabilityEntry.Fixes { - vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag - vulnerabilities[i].Fixes[j].Name = fix.Name - vulnerabilities[i].Fixes[j].Version = fix.Version - } - vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus - vulnerabilities[i].Link = vulnerabilityEntry.Link - vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata - vulnerabilities[i].Name = vulnerabilityEntry.Name - vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion - vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName - vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy - vulnerabilities[i].Severity = vulnerabilityEntry.Severity - vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount - } - - resultImageVulnerabilityReport := ContainerImageVulnerabilityReport{ - ImageID: *imageID, - Vulnerabilities: vulnerabilities, - } - - return &resultImageVulnerabilityReport, nil -} - -func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) { - return nil, nil -} diff --git a/container-images/civ-armo-adaptor_test.go b/container-images/civ-armo-adaptor_test.go deleted file mode 100644 index bd9f2f9a..00000000 --- a/container-images/civ-armo-adaptor_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package containerimages - -import ( - "fmt" - "testing" -) - -func TestSum(t *testing.T) { - credentials := make(map[string]string) - credentials["clientId"] = "378754de-e70d-4c33-a475-1818d296ff26" - credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - credentials["accountId"] = "2ce5daf4-e28d-4e6e-a239-03fda048070b" - adaptor, err := CreateArmoAdaptor("armoui-dev.eudev3.cyberarmorsoft.com", credentials) - if err != nil { - t.Fatalf("Cannot initialize adaptor: %s", err) - } - err = adaptor.Login() - if err != nil { - t.Fatalf("Cannot login with adaptor: %s", err) - } - //fmt.Printf("Login successful: %s\n", adaptor.feToken.Token) - - imageVulnerabilityReport, err := adaptor.GetImageVulnerabilty(&ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) - if err != nil { - t.Fatalf("Cannot get vulnerabilities: %s", err) - } - for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities { - fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description) - } -} diff --git a/go.mod b/go.mod index c22c9285..73276b8a 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,11 @@ require ( github.com/armosec/opa-utils v0.0.92 github.com/armosec/rbac-utils v0.0.11 github.com/armosec/utils-go v0.0.3 + github.com/armosec/utils-k8s-go v0.0.1 github.com/briandowns/spinner v1.18.0 github.com/enescakir/emoji v1.0.0 github.com/fatih/color v1.13.0 + github.com/francoispqt/gojay v1.2.13 github.com/gofrs/uuid v4.1.0+incompatible github.com/golang/glog v1.0.0 github.com/mattn/go-isatty v0.0.14 @@ -35,7 +37,6 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect - github.com/armosec/utils-k8s-go v0.0.1 // indirect github.com/aws/aws-sdk-go v1.41.11 // indirect github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -43,7 +44,6 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect - github.com/francoispqt/gojay v1.2.13 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-gota/gota v0.12.0 // indirect github.com/go-logr/logr v0.4.0 // indirect diff --git a/registryadaptors/armosec/v1/civarmoadaptor.go b/registryadaptors/armosec/v1/civarmoadaptor.go new file mode 100644 index 00000000..9add8096 --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptor.go @@ -0,0 +1,170 @@ +package v1 + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/armosec/kubescape/containerscan" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" +) + +func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { + var accountId string + var accessKey string + var clientId string + var ok bool + if accountId, ok = credentials["accountId"]; !ok { + return nil, fmt.Errorf("define accountId in credentials") + } + if clientId, ok = credentials["clientId"]; !ok { + return nil, fmt.Errorf("define clientId in credentials") + } + if accessKey, ok = credentials["accessKey"]; !ok { + return nil, fmt.Errorf("define accessKey in credentials") + } + armoCivAdaptor := ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey} + err := armoCivAdaptor.initializeUrls() + if err != nil { + return nil, err + } + return &armoCivAdaptor, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) Login() error { + feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey} + body, _ := json.Marshal(feLoginData) + + authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl) + resp, err := http.Post(authApiTokenEndpoint, "application/json", bytes.NewBuffer(body)) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("error authenticating: %d", resp.StatusCode) + } + + responseBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + var feLoginResponse FeLoginResponse + err = json.Unmarshal(responseBody, &feLoginResponse) + armoCivAdaptor.feToken = feLoginResponse + if err != nil { + return err + } + /* Now we have JWT */ + + armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie() + if err != nil { + return err + } + + return nil +} +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0) + for _, imageID := range imageIDs { + result, err := armoCivAdaptor.GetImageVulnerability(&imageID) + if err == nil { + resultList = append(resultList, *result) + } + } + return resultList, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + // First + containerScanId, err := armoCivAdaptor.getImageLastScanId(imageID) + if err != nil { + return nil, err + } + filter := []map[string]string{{"containersScanID": containerScanId}} + pageSize := 300 + pageNumber := 1 + request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} + requestBody, _ := json.Marshal(request) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) + client := &http.Client{} + httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return nil, err + } + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) + resp, err := client.Do(httpRequest) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + scanDetailsResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response containerscan.VulnerabilitiesList `json:"response"` + Cursor string `json:"cursor"` + }{} + err = json.Unmarshal(body, &scanDetailsResult) + if err != nil { + return nil, err + } + + vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(scanDetailsResult.Response)) + for i, vulnerabilityEntry := range scanDetailsResult.Response { + vulnerabilities[i].Description = vulnerabilityEntry.Description + vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes)) + for j, fix := range vulnerabilityEntry.Fixes { + vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag + vulnerabilities[i].Fixes[j].Name = fix.Name + vulnerabilities[i].Fixes[j].Version = fix.Version + } + vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus + vulnerabilities[i].Link = vulnerabilityEntry.Link + vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata + vulnerabilities[i].Name = vulnerabilityEntry.Name + vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion + vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName + vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy + vulnerabilities[i].Severity = vulnerabilityEntry.Severity + vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount + } + + resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{ + ImageID: *imageID, + Vulnerabilities: vulnerabilities, + } + + return &resultImageVulnerabilityReport, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) DescribeAdaptor() string { + // TODO + return "" +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) { + // TODO + return []registryvulnerabilities.ContainerImageInformation{}, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) { + // TODO + return []registryvulnerabilities.ContainerImageScanStatus{}, nil +} diff --git a/registryadaptors/armosec/v1/civarmoadaptor_test.go b/registryadaptors/armosec/v1/civarmoadaptor_test.go new file mode 100644 index 00000000..f7d3c6cd --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptor_test.go @@ -0,0 +1,60 @@ +package v1 + +import ( + "fmt" + "testing" + + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" + "github.com/stretchr/testify/assert" +) + +func TestSum(t *testing.T) { + credentials := make(map[string]string) + credentials["clientId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + credentials["accountId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + + var err error + var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor + + adaptor, err = NewArmoAdaptorMock("armoui-dev.eudev3.cyberarmorsoft.com", credentials) + assert.NoError(t, err) + + assert.NoError(t, adaptor.Login()) + //fmt.Printf("Login successful: %s\n", adaptor.feToken.Token) + + imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) + assert.NoError(t, err) + + for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities { + fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description) + } +} + +/* + +func TestSum(t *testing.T) { + credentials := make(map[string]string) + credentials["clientId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + credentials["accountId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + + var err error + var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor + + adaptor, err = NewArmoAdaptorMock("armoui-dev.eudev3.cyberarmorsoft.com", credentials) + assert.NoError(t, err) + + // TODO - create mock + assert.NoError(t, adaptor.Login()) + //fmt.Printf("Login successful: %s\n", adaptor.feToken.Token) + + imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) + assert.NoError(t, err) + + for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities { + fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description) + } +} + +*/ diff --git a/registryadaptors/armosec/v1/civarmoadaptormock.go b/registryadaptors/armosec/v1/civarmoadaptormock.go new file mode 100644 index 00000000..0ff41001 --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptormock.go @@ -0,0 +1,78 @@ +package v1 + +import ( + "fmt" + + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" +) + +type ArmoCivAdaptorMock struct { + registry string + accountId string + clientId string + accessKey string + feToken FeLoginResponse + armoUrls ArmoBeConfiguration + authCookie string +} + +func NewArmoAdaptorMock(registry string, credentials map[string]string) (*ArmoCivAdaptorMock, error) { + var accountId string + var accessKey string + var clientId string + var ok bool + if accountId, ok = credentials["accountId"]; !ok { + return nil, fmt.Errorf("define accountId in credentials") + } + if clientId, ok = credentials["clientId"]; !ok { + return nil, fmt.Errorf("define clientId in credentials") + } + if accessKey, ok = credentials["accessKey"]; !ok { + return nil, fmt.Errorf("define accessKey in credentials") + } + armoCivAdaptor := ArmoCivAdaptorMock{ + registry: registry, + accountId: accountId, + clientId: clientId, + accessKey: accessKey, + } + // err := armoCivAdaptorMock.initializeUrls() + // if err != nil { + // return nil, err + // } + return &armoCivAdaptor, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) Login() error { + + return nil +} +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0) + for _, imageID := range imageIDs { + result, err := armoCivAdaptorMock.GetImageVulnerability(&imageID) + if err == nil { + resultList = append(resultList, *result) + } + } + return resultList, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + return ®istryvulnerabilities.ContainerImageVulnerabilityReport{}, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) DescribeAdaptor() string { + // TODO + return "" +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) { + // TODO + return []registryvulnerabilities.ContainerImageInformation{}, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) { + // TODO + return []registryvulnerabilities.ContainerImageScanStatus{}, nil +} diff --git a/registryadaptors/armosec/v1/civarmoadaptorutils.go b/registryadaptors/armosec/v1/civarmoadaptorutils.go new file mode 100644 index 00000000..12ba6048 --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptorutils.go @@ -0,0 +1,122 @@ +package v1 + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/armosec/kubescape/containerscan" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" +) + +func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error { + configUrl := fmt.Sprintf("https://%s/assets/configs/config.json", armoCivAdaptor.registry) + resp, err := http.Get(configUrl) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("cannot retrieve backend config file %s: status %d", configUrl, resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + err = json.Unmarshal(body, &armoCivAdaptor.armoUrls) + if err != nil { + return err + } + return nil + +} + +func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { + selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountId} + requestBody, _ := json.Marshal(selectCustomer) + requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl) + client := &http.Client{} + httpRequest, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return "", err + } + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpResponse, err := client.Do(httpRequest) + if err != nil { + return "", err + } + defer httpResponse.Body.Close() + if httpResponse.StatusCode != http.StatusOK { + return "", fmt.Errorf("error getting cookie at %s: status %d", requestUrl, httpResponse.StatusCode) + } + + cookies := httpResponse.Header.Get("set-cookie") + if len(cookies) == 0 { + return "", fmt.Errorf("no cookie field in response from %s", requestUrl) + } + + authCookie := "" + for _, cookie := range strings.Split(cookies, ";") { + kv := strings.Split(cookie, "=") + if kv[0] == "auth" { + authCookie = kv[1] + } + } + + if len(authCookie) == 0 { + return "", fmt.Errorf("no auth cookie field in response from %s", requestUrl) + } + + return authCookie, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulnerabilities.ContainerImageIdentifier) (string, error) { + filter := []map[string]string{{"imageTag": imageID.Tag}} + pageSize := 1 + pageNumber := 1 + request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} + requestBody, _ := json.Marshal(request) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) + client := &http.Client{} + httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) + resp, err := client.Do(httpRequest) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + scanSummartResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response []containerscan.ElasticContainerScanSummaryResult `json:"response"` + Cursor string `json:"cursor"` + }{} + err = json.Unmarshal(body, &scanSummartResult) + if err != nil { + return "", err + } + + if len(scanSummartResult.Response) < pageSize { + return "", fmt.Errorf("did not get response for image %s", imageID.Tag) + } + + return scanSummartResult.Response[0].ContainerScanID, nil +} diff --git a/container-images/armo-datastructures.go b/registryadaptors/armosec/v1/datastructures.go similarity index 67% rename from container-images/armo-datastructures.go rename to registryadaptors/armosec/v1/datastructures.go index 16e94f38..0ccc492f 100644 --- a/container-images/armo-datastructures.go +++ b/registryadaptors/armosec/v1/datastructures.go @@ -1,4 +1,4 @@ -package containerimages +package v1 import "time" @@ -26,3 +26,33 @@ type V2ListRequest struct { FieldsList []string `json:"includeFields,omitempty"` FieldsReverseKeywordMap map[string]string `json:"-,omitempty"` } + +type FeLoginData struct { + Secret string `json:"secret"` + ClientId string `json:"clientId"` +} + +type FeLoginResponse struct { + Token string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int32 `json:"expiresIn"` + Expires string `json:"expires"` +} + +type ArmoBeConfiguration struct { + BackendUrl string `json:"backend"` + AuthUrl string `json:"authUrl"` +} +type ArmoSelectCustomer struct { + SelectedCustomerGuid string `json:"selectedCustomer"` +} + +type ArmoCivAdaptor struct { + registry string + accountId string + clientId string + accessKey string + feToken FeLoginResponse + armoUrls ArmoBeConfiguration + authCookie string +} diff --git a/container-images/civ-adaptor-if.go b/registryadaptors/registryvulnerabilities/datastructures.go similarity index 64% rename from container-images/civ-adaptor-if.go rename to registryadaptors/registryvulnerabilities/datastructures.go index 1fa4ee1b..429786b6 100644 --- a/container-images/civ-adaptor-if.go +++ b/registryadaptors/registryvulnerabilities/datastructures.go @@ -1,4 +1,4 @@ -package containerimages +package registryvulnerabilities import ( "time" @@ -48,18 +48,3 @@ type ContainerImageInformation struct { Bom []string //ImageManifest Manifest // will use here Docker package definition } - -type IContainerImageVulnerabilityAdaptor interface { - // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level - // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} - Login(registry string, credentials map[string]string) error - - // For "help" purposes - DescribeAdaptor() string - - GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) - - GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) - - GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) -} diff --git a/registryadaptors/registryvulnerabilities/interfaces.go b/registryadaptors/registryvulnerabilities/interfaces.go new file mode 100644 index 00000000..cbef91e5 --- /dev/null +++ b/registryadaptors/registryvulnerabilities/interfaces.go @@ -0,0 +1,17 @@ +package registryvulnerabilities + +type IContainerImageVulnerabilityAdaptor interface { + // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level + // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} + Login() error + + // For "help" purposes + DescribeAdaptor() string + + GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) + + GetImagesVulnerabilities(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) + GetImageVulnerability(imageID *ContainerImageIdentifier) (*ContainerImageVulnerabilityReport, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} From d1695b7f1075492f16a086d4a4fad86d4e74446c Mon Sep 17 00:00:00 2001 From: dwertent Date: Mon, 31 Jan 2022 18:41:45 +0200 Subject: [PATCH 18/22] support vuln scan --- clihandler/initcli.go | 7 +- clihandler/initcliutils.go | 7 +- registryadaptors/README.md | 164 ++++++++++++++++++ registryadaptors/armosec/v1/civarmoadaptor.go | 49 ++---- .../armosec/v1/civarmoadaptor_test.go | 41 +---- .../armosec/v1/civarmoadaptormock.go | 71 ++++---- .../armosec/v1/civarmoadaptorutils.go | 33 +++- registryadaptors/armosec/v1/datastructures.go | 4 +- resourcehandler/filesloader.go | 12 +- resourcehandler/k8sresources.go | 9 +- resourcehandler/registrydata.go | 147 ++++++++++++++++ 11 files changed, 420 insertions(+), 124 deletions(-) create mode 100644 registryadaptors/README.md create mode 100644 resourcehandler/registrydata.go diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 429cb854..375b2638 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -63,7 +63,12 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace()) } - resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler) + registryAdaptors, err := resourcehandler.NewRegistryAdaptors() + if err != nil { + // display warning + } + + resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors) // reporting behavior - setup reporter reportHandler := getReporter(tenantConfig, scanInfo.Submit) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index fd60a02b..1c231dc5 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -55,12 +55,13 @@ func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IRepo return reporterv1.NewReportMock() } -func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler { +func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler { if len(scanInfo.InputPatterns) > 0 || k8s == nil { - return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns) + return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns, registryAdaptors) } + getter.GetArmoAPIConnector() rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit) - return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects) + return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors) } func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor { diff --git a/registryadaptors/README.md b/registryadaptors/README.md new file mode 100644 index 00000000..abf0688e --- /dev/null +++ b/registryadaptors/README.md @@ -0,0 +1,164 @@ +# Container image vulnerability adaptor interface + +## High level design of Kubescape + +### Layers + +* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego +* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter +* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running +* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration +* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to. +* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption +``` + ----------------------- +| Controls/Rules (rego) | + ----------------------- + | + ----------------------- +| OPA engine | + ----------------------- + | + ----------------------- +| Rules processor | + ----------------------- + | + ----------------------- +| Data sources | + ----------------------- + | + ======================= +| CIV adaption interface| <- Adding this layer in this proposal + ======================= + | + ----------------------- +| Specific CIV adaptors | <- Will be implemented based on this proposal + ----------------------- + + + +``` + +## Functionalities to cover + +The interface needs to cover the following functionalities: + +* Authentication against the information source (abstracted login) +* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone) +* Reading image scan status (with last scan date and etc.) +* Getting vulnerability information for a given image +* Getting image information + * Image manifests + * Image BOMs (bill of material) + +## Go API proposal + +``` + +/*type ContainerImageRegistryCredentials struct { + Password string + Tag string + Hash string +}*/ + +type ContainerImageIdentifier struct { + Registry string + Repository string + Tag string + Hash string +} + +type ContainerImageScanStatus struct { + ImageID ContainerImageIdentifier + IsScanAvailable bool + IsBomAvailable bool + LastScanDate time.Time +} + +type ContainerImageVulnerabilityReport struct { + ImageID ContainerImageIdentifier + // TBD +} + +type ContainerImageInformation struct { + ImageID ContainerImageIdentifier + Bom []string + ImageManifest Manifest // will use here Docker package definition +} + +type IContainerImageVulnerabilityAdaptor interface { + // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level + // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} + Login(registry string, credentials map[string]string) error + + // For "help" purposes + DescribeAdaptor() string + + GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) + + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} +``` + + + +# Integration + +# Input + +The objects received from the interface will be converted to an IMetadata compatible objects as following + +``` +{ + "apiVersion": "image.vulnscan.com/v1", + "kind": "ImageVulnerabilities", + "metadata": { + "name": "nginx:latest" + }, + "data": { + // list of vulnerabilities + } +} +``` + + +# Output + +The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control + +``` +{ + "apiVersion": "result.vulnscan.com/v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + "namespace": "default" + + }, + "relatedObjects": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + "namespace": "default" + }, + "spec": { + // podSpec + }, + }, + { + "apiVersion": "image.vulnscan.com/v1", + "kind": "ImageVulnerabilities", + "metadata": { + "name": "nginx:latest", + }, + "data": { + // list of vulnerabilities + } + } + ] +} +``` \ No newline at end of file diff --git a/registryadaptors/armosec/v1/civarmoadaptor.go b/registryadaptors/armosec/v1/civarmoadaptor.go index 9add8096..134d53f0 100644 --- a/registryadaptors/armosec/v1/civarmoadaptor.go +++ b/registryadaptors/armosec/v1/civarmoadaptor.go @@ -12,20 +12,20 @@ import ( ) func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { - var accountId string + var accountID string var accessKey string - var clientId string + var clientID string var ok bool - if accountId, ok = credentials["accountId"]; !ok { - return nil, fmt.Errorf("define accountId in credentials") + if accountID, ok = credentials["accountID"]; !ok { + return nil, fmt.Errorf("define accountID in credentials") } - if clientId, ok = credentials["clientId"]; !ok { - return nil, fmt.Errorf("define clientId in credentials") + if clientID, ok = credentials["clientID"]; !ok { + return nil, fmt.Errorf("define clientID in credentials") } if accessKey, ok = credentials["accessKey"]; !ok { return nil, fmt.Errorf("define accessKey in credentials") } - armoCivAdaptor := ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey} + armoCivAdaptor := ArmoCivAdaptor{registry: registry, clientID: clientID, accountID: accountID, accessKey: accessKey} err := armoCivAdaptor.initializeUrls() if err != nil { return nil, err @@ -34,7 +34,7 @@ func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAda } func (armoCivAdaptor *ArmoCivAdaptor) Login() error { - feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey} + feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientID, Secret: armoCivAdaptor.accessKey} body, _ := json.Marshal(feLoginData) authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl) @@ -53,11 +53,12 @@ func (armoCivAdaptor *ArmoCivAdaptor) Login() error { return err } var feLoginResponse FeLoginResponse - err = json.Unmarshal(responseBody, &feLoginResponse) - armoCivAdaptor.feToken = feLoginResponse - if err != nil { + + if err = json.Unmarshal(responseBody, &feLoginResponse); err != nil { return err } + armoCivAdaptor.feToken = feLoginResponse + /* Now we have JWT */ armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie() @@ -84,12 +85,16 @@ func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvul if err != nil { return nil, err } + if containerScanId == "" { + return nil, fmt.Errorf("last scan ID is empty") + } + filter := []map[string]string{{"containersScanID": containerScanId}} pageSize := 300 pageNumber := 1 request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} requestBody, _ := json.Marshal(request) - requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID) client := &http.Client{} httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) if err != nil { @@ -126,25 +131,7 @@ func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvul return nil, err } - vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(scanDetailsResult.Response)) - for i, vulnerabilityEntry := range scanDetailsResult.Response { - vulnerabilities[i].Description = vulnerabilityEntry.Description - vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes)) - for j, fix := range vulnerabilityEntry.Fixes { - vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag - vulnerabilities[i].Fixes[j].Name = fix.Name - vulnerabilities[i].Fixes[j].Version = fix.Version - } - vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus - vulnerabilities[i].Link = vulnerabilityEntry.Link - vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata - vulnerabilities[i].Name = vulnerabilityEntry.Name - vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion - vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName - vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy - vulnerabilities[i].Severity = vulnerabilityEntry.Severity - vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount - } + vulnerabilities := responseObjectToVulnerabilities(scanDetailsResult.Response) resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{ ImageID: *imageID, diff --git a/registryadaptors/armosec/v1/civarmoadaptor_test.go b/registryadaptors/armosec/v1/civarmoadaptor_test.go index f7d3c6cd..fb13dd6f 100644 --- a/registryadaptors/armosec/v1/civarmoadaptor_test.go +++ b/registryadaptors/armosec/v1/civarmoadaptor_test.go @@ -1,7 +1,6 @@ package v1 import ( - "fmt" "testing" "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" @@ -9,52 +8,16 @@ import ( ) func TestSum(t *testing.T) { - credentials := make(map[string]string) - credentials["clientId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - credentials["accountId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - var err error var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor - adaptor, err = NewArmoAdaptorMock("armoui-dev.eudev3.cyberarmorsoft.com", credentials) + adaptor, err = NewArmoAdaptorMock() assert.NoError(t, err) assert.NoError(t, adaptor.Login()) - //fmt.Printf("Login successful: %s\n", adaptor.feToken.Token) imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) assert.NoError(t, err) - for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities { - fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description) - } + assert.Equal(t, 25, len(imageVulnerabilityReport.Vulnerabilities)) } - -/* - -func TestSum(t *testing.T) { - credentials := make(map[string]string) - credentials["clientId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - credentials["accountId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - - var err error - var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor - - adaptor, err = NewArmoAdaptorMock("armoui-dev.eudev3.cyberarmorsoft.com", credentials) - assert.NoError(t, err) - - // TODO - create mock - assert.NoError(t, adaptor.Login()) - //fmt.Printf("Login successful: %s\n", adaptor.feToken.Token) - - imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) - assert.NoError(t, err) - - for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities { - fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description) - } -} - -*/ diff --git a/registryadaptors/armosec/v1/civarmoadaptormock.go b/registryadaptors/armosec/v1/civarmoadaptormock.go index 0ff41001..dac0726d 100644 --- a/registryadaptors/armosec/v1/civarmoadaptormock.go +++ b/registryadaptors/armosec/v1/civarmoadaptormock.go @@ -1,65 +1,48 @@ package v1 import ( - "fmt" + "encoding/json" + "github.com/armosec/kubescape/containerscan" "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" ) type ArmoCivAdaptorMock struct { - registry string - accountId string - clientId string - accessKey string - feToken FeLoginResponse - armoUrls ArmoBeConfiguration - authCookie string + resultList *registryvulnerabilities.ContainerImageVulnerabilityReport } -func NewArmoAdaptorMock(registry string, credentials map[string]string) (*ArmoCivAdaptorMock, error) { - var accountId string - var accessKey string - var clientId string - var ok bool - if accountId, ok = credentials["accountId"]; !ok { - return nil, fmt.Errorf("define accountId in credentials") +func NewArmoAdaptorMock() (*ArmoCivAdaptorMock, error) { + scanDetailsResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response containerscan.VulnerabilitiesList `json:"response"` + Cursor string `json:"cursor"` + }{} + + if err := json.Unmarshal([]byte(minikubeReponseMock), &scanDetailsResult); err != nil { + return nil, err } - if clientId, ok = credentials["clientId"]; !ok { - return nil, fmt.Errorf("define clientId in credentials") + + vulnerabilities := responseObjectToVulnerabilities(scanDetailsResult.Response) + + resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{ + ImageID: registryvulnerabilities.ContainerImageIdentifier{Tag: vulnerabilities[0].Name}, + Vulnerabilities: vulnerabilities, } - if accessKey, ok = credentials["accessKey"]; !ok { - return nil, fmt.Errorf("define accessKey in credentials") - } - armoCivAdaptor := ArmoCivAdaptorMock{ - registry: registry, - accountId: accountId, - clientId: clientId, - accessKey: accessKey, - } - // err := armoCivAdaptorMock.initializeUrls() - // if err != nil { - // return nil, err - // } - return &armoCivAdaptor, nil + return &ArmoCivAdaptorMock{resultList: &resultImageVulnerabilityReport}, nil } func (armoCivAdaptorMock *ArmoCivAdaptorMock) Login() error { - return nil } func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) { - resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0) - for _, imageID := range imageIDs { - result, err := armoCivAdaptorMock.GetImageVulnerability(&imageID) - if err == nil { - resultList = append(resultList, *result) - } - } - return resultList, nil + return []registryvulnerabilities.ContainerImageVulnerabilityReport{*armoCivAdaptorMock.resultList}, nil } func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) { - return ®istryvulnerabilities.ContainerImageVulnerabilityReport{}, nil + return armoCivAdaptorMock.resultList, nil } func (armoCivAdaptorMock *ArmoCivAdaptorMock) DescribeAdaptor() string { @@ -76,3 +59,9 @@ func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesScanStatus(imageIDs []reg // TODO return []registryvulnerabilities.ContainerImageScanStatus{}, nil } + +//============================================================================================================================== +//============================================================================================================================== +//============================================================================================================================== + +var minikubeReponseMock = `{"total":{"value":73,"relation":"eq"},"response":[{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2005-2541","https://security-tracker.debian.org/tracker/CVE-2005-2541"],"name":"CVE-2005-2541","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"tar","packageVersion":"1.30+dfsg-6","link":"https://nvd.nist.gov/vuln/detail/CVE-2005-2541","description":"Tar 1.15.1 does not properly warn the user when extracting setuid or setgid files, which may allow local users or remote attackers to gain privileges.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2007-5686","https://security-tracker.debian.org/tracker/CVE-2007-5686"],"name":"CVE-2007-5686","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"login","packageVersion":"1:4.5-1.1","link":"https://nvd.nist.gov/vuln/detail/CVE-2007-5686","description":"initscripts in rPath Linux 1 sets insecure permissions for the /var/log/btmp file, which allows local users to obtain sensitive information regarding authentication attempts. NOTE: because sshd detects the insecure permissions and does not log certain events, this also prevents sshd from logging failed authentication attempts by remote attackers.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2007-6755","https://security-tracker.debian.org/tracker/CVE-2007-6755"],"name":"CVE-2007-6755","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libssl1.1","packageVersion":"1.1.1d-0+deb10u6","link":"https://nvd.nist.gov/vuln/detail/CVE-2007-6755","description":"The NIST SP 800-90A default statement of the Dual Elliptic Curve Deterministic Random Bit Generation (Dual_EC_DRBG) algorithm contains point Q constants with a possible relationship to certain \"skeleton key\" values, which might allow context-dependent attackers to defeat cryptographic protection mechanisms by leveraging knowledge of those values. NOTE: this is a preliminary CVE for Dual_EC_DRBG; future research may provide additional details about point Q and associated attacks, and could potentially lead to a RECAST or REJECT of this CVE.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2010-0928","https://security-tracker.debian.org/tracker/CVE-2010-0928"],"name":"CVE-2010-0928","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libssl1.1","packageVersion":"1.1.1d-0+deb10u6","link":"https://nvd.nist.gov/vuln/detail/CVE-2010-0928","description":"OpenSSL 0.9.8i on the Gaisler Research LEON3 SoC on the Xilinx Virtex-II Pro FPGA uses a Fixed Width Exponentiation (FWE) algorithm for certain signature calculations, and does not verify the signature before providing it to a caller, which makes it easier for physically proximate attackers to determine the private key via a modified supply voltage for the microprocessor, related to a \"fault-based attack.\"","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2010-4756","https://security-tracker.debian.org/tracker/CVE-2010-4756"],"name":"CVE-2010-4756","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2010-4756","description":"The glob implementation in the GNU C Library (aka glibc or libc6) allows remote authenticated users to cause a denial of service (CPU and memory consumption) via crafted glob expressions that do not match any pathnames, as demonstrated by glob expressions in STAT commands to an FTP daemon, a different vulnerability than CVE-2010-2632.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2011-3374","https://security-tracker.debian.org/tracker/CVE-2011-3374"],"name":"CVE-2011-3374","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"apt","packageVersion":"1.8.2.3","link":"https://nvd.nist.gov/vuln/detail/CVE-2011-3374","description":"It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2011-3389","https://security-tracker.debian.org/tracker/CVE-2011-3389"],"name":"CVE-2011-3389","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libgnutls30","packageVersion":"3.6.7-4+deb10u7","link":"https://nvd.nist.gov/vuln/detail/CVE-2011-3389","description":"The SSL protocol, as used in certain configurations in Microsoft Windows and Microsoft Internet Explorer, Mozilla Firefox, Google Chrome, Opera, and other products, encrypts data by using CBC mode with chained initialization vectors, which allows man-in-the-middle attackers to obtain plaintext HTTP headers via a blockwise chosen-boundary attack (BCBA) on an HTTPS session, in conjunction with JavaScript code that uses (1) the HTML5 WebSocket API, (2) the Java URLConnection API, or (3) the Silverlight WebClient API, aka a \"BEAST\" attack.","severity":"Medium","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2011-4116","https://security-tracker.debian.org/tracker/CVE-2011-4116"],"name":"CVE-2011-4116","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"perl-base","packageVersion":"5.28.1-6+deb10u1","link":"https://nvd.nist.gov/vuln/detail/CVE-2011-4116","description":"_is_safe in the File::Temp module for Perl does not properly handle symlinks.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2012-2663","https://security-tracker.debian.org/tracker/CVE-2012-2663"],"name":"CVE-2012-2663","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"iptables","packageVersion":"1.8.5-3~bpo10+1","link":"https://nvd.nist.gov/vuln/detail/CVE-2012-2663","description":"extensions/libxt_tcp.c in iptables through 1.4.21 does not match TCP SYN+FIN packets in --syn rules, which might allow remote attackers to bypass intended firewall restrictions via crafted packets. NOTE: the CVE-2012-6638 fix makes this issue less relevant.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2013-4235","https://security-tracker.debian.org/tracker/CVE-2013-4235"],"name":"CVE-2013-4235","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"login","packageVersion":"1:4.5-1.1","link":"https://nvd.nist.gov/vuln/detail/CVE-2013-4235","description":"shadow: TOCTOU (time-of-check time-of-use) race condition when copying and removing directory trees","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2013-4392","https://security-tracker.debian.org/tracker/CVE-2013-4392"],"name":"CVE-2013-4392","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libsystemd0","packageVersion":"241-7~deb10u7","link":"https://nvd.nist.gov/vuln/detail/CVE-2013-4392","description":"systemd, when updating file permissions, allows local users to change the permissions and SELinux security contexts for arbitrary files via a symlink attack on unspecified files.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2016-10228","https://security-tracker.debian.org/tracker/CVE-2016-10228"],"name":"CVE-2016-10228","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2016-10228","description":"The iconv program in the GNU C Library (aka glibc or libc6) 2.31 and earlier, when invoked with multiple suffixes in the destination encoding (TRANSLATE or IGNORE) along with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.","severity":"Low","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2016-2781","https://security-tracker.debian.org/tracker/CVE-2016-2781"],"name":"CVE-2016-2781","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"coreutils","packageVersion":"8.30-3","link":"https://nvd.nist.gov/vuln/detail/CVE-2016-2781","description":"chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.","severity":"Low","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-11164","https://security-tracker.debian.org/tracker/CVE-2017-11164"],"name":"CVE-2017-11164","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-11164","description":"In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-16231","https://security-tracker.debian.org/tracker/CVE-2017-16231"],"name":"CVE-2017-16231","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-16231","description":"** DISPUTED ** In PCRE 8.41, after compiling, a pcretest load test PoC produces a crash overflow in the function match() in pcre_exec.c because of a self-recursive call. NOTE: third parties dispute the relevance of this report, noting that there are options that can be used to limit the amount of stack that is used.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-18018","https://security-tracker.debian.org/tracker/CVE-2017-18018"],"name":"CVE-2017-18018","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"coreutils","packageVersion":"8.30-3","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-18018","description":"In GNU Coreutils through 8.29, chown-core.c in chown and chgrp does not prevent replacement of a plain file with a symlink during use of the POSIX \"-R -L\" options, which allows local users to modify the ownership of arbitrary files by leveraging a race condition.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-7245","https://security-tracker.debian.org/tracker/CVE-2017-7245"],"name":"CVE-2017-7245","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-7245","description":"Stack-based buffer overflow in the pcre32_copy_substring function in pcre_get.c in libpcre1 in PCRE 8.40 allows remote attackers to cause a denial of service (WRITE of size 4) or possibly have unspecified other impact via a crafted file.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-7246","https://security-tracker.debian.org/tracker/CVE-2017-7246"],"name":"CVE-2017-7246","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-7246","description":"Stack-based buffer overflow in the pcre32_copy_substring function in pcre_get.c in libpcre1 in PCRE 8.40 allows remote attackers to cause a denial of service (WRITE of size 268) or possibly have unspecified other impact via a crafted file.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-1000654","https://security-tracker.debian.org/tracker/CVE-2018-1000654"],"name":"CVE-2018-1000654","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libtasn1-6","packageVersion":"4.13-3","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-1000654","description":"GNU Libtasn1-4.13 libtasn1-4.13 version libtasn1-4.13, libtasn1-4.12 contains a DoS, specifically CPU usage will reach 100% when running asn1Paser against the POC due to an issue in _asn1_expand_object_id(p_tree), after a long time, the program will be killed. This attack appears to be exploitable via parsing a crafted file.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-12886","https://security-tracker.debian.org/tracker/CVE-2018-12886"],"name":"CVE-2018-12886","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"gcc-8-base","packageVersion":"8.3.0-6","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-12886","description":"stack_protect_prologue in cfgexpand.c and stack_protect_epilogue in function.c in GNU Compiler Collection (GCC) 4.1 through 8 (under certain circumstances) generate instruction sequences when targeting ARM targets that spill the address of the stack protector guard, which allows an attacker to bypass the protection of -fstack-protector, -fstack-protector-all, -fstack-protector-strong, and -fstack-protector-explicit against stack overflow by controlling what the stack canary is compared against.","severity":"High","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-20796","https://security-tracker.debian.org/tracker/CVE-2018-20796"],"name":"CVE-2018-20796","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-20796","description":"In the GNU C Library (aka glibc or libc6) through 2.29, check_dst_limits_calc_pos_1 in posix/regexec.c has Uncontrolled Recursion, as demonstrated by '(\\227|)(\\\\1\\\\1|t1|\\\\\\2537)+' in grep.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-6829","https://security-tracker.debian.org/tracker/CVE-2018-6829"],"name":"CVE-2018-6829","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libgcrypt20","packageVersion":"1.8.4-5+deb10u1","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-6829","description":"cipher/elgamal.c in Libgcrypt through 1.8.2, when used to encrypt messages directly, improperly encodes plaintexts, which allows attackers to obtain sensitive information by reading ciphertext data (i.e., it does not have semantic security in face of a ciphertext-only attack). The Decisional Diffie-Hellman (DDH) assumption does not hold for Libgcrypt's ElGamal implementation.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-7169","https://security-tracker.debian.org/tracker/CVE-2018-7169"],"name":"CVE-2018-7169","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"login","packageVersion":"1:4.5-1.1","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-7169","description":"An issue was discovered in shadow 4.5. newgidmap (in shadow-utils) is setuid and allows an unprivileged user to be placed in a user namespace where setgroups(2) is permitted. This allows an attacker to remove themselves from a supplementary group, which may allow access to certain filesystem paths if the administrator has used \"group blacklisting\" (e.g., chmod g-rwx) to restrict access to paths. This flaw effectively reverts a security feature in the kernel (in particular, the /proc/self/setgroups knob) to prevent this sort of privilege escalation.","severity":"Low","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2019-1010022","https://security-tracker.debian.org/tracker/CVE-2019-1010022"],"name":"CVE-2019-1010022","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2019-1010022","description":"** DISPUTED ** GNU Libc current is affected by: Mitigation bypass. The impact is: Attacker may bypass stack guard protection. The component is: nptl. The attack vector is: Exploit stack buffer overflow vulnerability and use this bypass vulnerability to bypass stack guard. NOTE: Upstream comments indicate \"this is being treated as a non-security bug and no real threat.\"","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2019-1010023","https://security-tracker.debian.org/tracker/CVE-2019-1010023"],"name":"CVE-2019-1010023","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2019-1010023","description":"** DISPUTED ** GNU Libc current is affected by: Re-mapping current loaded library with malicious ELF file. The impact is: In worst case attacker may evaluate privileges. The component is: libld. The attack vector is: Attacker sends 2 ELF files to victim and asks to run ldd on it. ldd execute code. NOTE: Upstream comments indicate \"this is being treated as a non-security bug and no real threat.\"","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}}],"cursor":""}` diff --git a/registryadaptors/armosec/v1/civarmoadaptorutils.go b/registryadaptors/armosec/v1/civarmoadaptorutils.go index 12ba6048..b019ed5b 100644 --- a/registryadaptors/armosec/v1/civarmoadaptorutils.go +++ b/registryadaptors/armosec/v1/civarmoadaptorutils.go @@ -35,7 +35,7 @@ func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error { } func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { - selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountId} + selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountID} requestBody, _ := json.Marshal(selectCustomer) requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl) client := &http.Client{} @@ -75,14 +75,18 @@ func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { } func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulnerabilities.ContainerImageIdentifier) (string, error) { - filter := []map[string]string{{"imageTag": imageID.Tag}} + filter := []map[string]string{{"imageTag": imageID.Tag, "status": "Success"}} pageSize := 1 pageNumber := 1 request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} requestBody, _ := json.Marshal(request) - requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID) client := &http.Client{} httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return "", err + } + httpRequest.Header.Set("Content-Type", "application/json") httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) @@ -120,3 +124,26 @@ func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulner return scanSummartResult.Response[0].ContainerScanID, nil } + +func responseObjectToVulnerabilities(vulnerabilitiesList containerscan.VulnerabilitiesList) []registryvulnerabilities.Vulnerability { + vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(vulnerabilitiesList)) + for i, vulnerabilityEntry := range vulnerabilitiesList { + vulnerabilities[i].Description = vulnerabilityEntry.Description + vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes)) + for j, fix := range vulnerabilityEntry.Fixes { + vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag + vulnerabilities[i].Fixes[j].Name = fix.Name + vulnerabilities[i].Fixes[j].Version = fix.Version + } + vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus + vulnerabilities[i].Link = vulnerabilityEntry.Link + vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata + vulnerabilities[i].Name = vulnerabilityEntry.Name + vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion + vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName + vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy + vulnerabilities[i].Severity = vulnerabilityEntry.Severity + vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount + } + return vulnerabilities +} diff --git a/registryadaptors/armosec/v1/datastructures.go b/registryadaptors/armosec/v1/datastructures.go index 0ccc492f..79ab8f5c 100644 --- a/registryadaptors/armosec/v1/datastructures.go +++ b/registryadaptors/armosec/v1/datastructures.go @@ -49,8 +49,8 @@ type ArmoSelectCustomer struct { type ArmoCivAdaptor struct { registry string - accountId string - clientId string + accountID string + clientID string accessKey string feToken FeLoginResponse armoUrls ArmoBeConfiguration diff --git a/resourcehandler/filesloader.go b/resourcehandler/filesloader.go index c4052fe2..86fe4434 100644 --- a/resourcehandler/filesloader.go +++ b/resourcehandler/filesloader.go @@ -34,13 +34,15 @@ const ( // FileResourceHandler handle resources from files and URLs type FileResourceHandler struct { - inputPatterns []string + inputPatterns []string + registryAdaptors *RegistryAdaptors } -func NewFileResourceHandler(inputPatterns []string) *FileResourceHandler { +func NewFileResourceHandler(inputPatterns []string, registryAdaptors *RegistryAdaptors) *FileResourceHandler { k8sinterface.InitializeMapResourcesMock() // initialize the resource map return &FileResourceHandler{ - inputPatterns: inputPatterns, + inputPatterns: inputPatterns, + registryAdaptors: registryAdaptors, } } @@ -90,6 +92,10 @@ func (fileHandler *FileResourceHandler) GetResources(frameworks []reporthandling } } + if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources); err != nil { + cautils.WarningDisplay(os.Stderr, "Warning: failed to collect images vulnerabilities: %s\n", err.Error()) + } + return k8sResources, allResources, nil } diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 6a21f1fd..7198613c 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -30,14 +30,16 @@ type K8sResourceHandler struct { hostSensorHandler hostsensorutils.IHostSensor fieldSelector IFieldSelector rbacObjectsAPI *cautils.RBACObjects + registryAdaptors *RegistryAdaptors } -func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects) *K8sResourceHandler { +func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors) *K8sResourceHandler { return &K8sResourceHandler{ k8s: k8s, fieldSelector: fieldSelector, hostSensorHandler: hostSensorHandler, rbacObjectsAPI: rbacObjects, + registryAdaptors: registryAdaptors, } } @@ -60,6 +62,11 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F if err := k8sHandler.pullResources(k8sResourcesMap, allResources, namespace, labels); err != nil { return k8sResourcesMap, allResources, err } + + if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources); err != nil { + cautils.WarningDisplay(os.Stderr, "Warning: failed to collect image vulnerabilities: %s\n", err.Error()) + } + if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil { cautils.WarningDisplay(os.Stderr, "Warning: failed to collect host sensor resources\n") } diff --git a/resourcehandler/registrydata.go b/resourcehandler/registrydata.go new file mode 100644 index 00000000..65df1165 --- /dev/null +++ b/resourcehandler/registrydata.go @@ -0,0 +1,147 @@ +package resourcehandler + +import ( + "github.com/armosec/k8s-interface/k8sinterface" + "github.com/armosec/k8s-interface/workloadinterface" + "github.com/armosec/kubescape/cautils" + armosecadaptorv1 "github.com/armosec/kubescape/registryadaptors/armosec/v1" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" + "github.com/armosec/opa-utils/shared" +) + +const ( + ImagevulnerabilitiesObjectGroup = "image.vulnscan.com" + ImagevulnerabilitiesObjectVersion = "v1" + ImagevulnerabilitiesObjectKind = "ImageVulnerabilities" +) + +type RegistryAdaptors struct { + adaptors []registryvulnerabilities.IContainerImageVulnerabilityAdaptor +} + +func NewRegistryAdaptors() (*RegistryAdaptors, error) { + // list supported adaptors + registryAdaptors := &RegistryAdaptors{} + adaptors, err := listAdaptores() + if err != nil { + return registryAdaptors, err + } + registryAdaptors.adaptors = adaptors + return registryAdaptors, nil +} + +func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) error { + + // list cluster images + images := listImagesTags(k8sResourcesMap, allResources) + imagesIdentifiers := imageTagsToContainerImageIdentifier(images) + + imagesVulnerability := map[string][]registryvulnerabilities.Vulnerability{} + for i := range registryAdaptors.adaptors { // login and and get vulnerabilities + + if err := registryAdaptors.adaptors[i].Login(); err != nil { + return err + } + vulnerabilities, err := registryAdaptors.adaptors[i].GetImagesVulnerabilities(imagesIdentifiers) + if err != nil { + return err + } + for j := range vulnerabilities { + imagesVulnerability[vulnerabilities[j].ImageID.Tag] = vulnerabilities[j].Vulnerabilities + } + } + + // convert result to IMetadata object + metaObjs := vulnerabilitiesToIMetadata(imagesVulnerability) + + // save in resources map + for i := range metaObjs { + allResources[metaObjs[i].GetID()] = metaObjs[i] + } + (*k8sResourcesMap)[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs) + + return nil +} + +func vulnerabilitiesToIMetadata(vulnerabilities map[string][]registryvulnerabilities.Vulnerability) []workloadinterface.IMetadata { + objs := []workloadinterface.IMetadata{} + for i := range vulnerabilities { + objs = append(objs, vulnerabilityToIMetadata(i, vulnerabilities[i])) + } + return objs +} + +func vulnerabilityToIMetadata(imageTag string, vulnerabilities []registryvulnerabilities.Vulnerability) workloadinterface.IMetadata { + obj := map[string]interface{}{} + metadata := map[string]interface{}{} + metadata["name"] = imageTag // store image tag as object name + obj["kind"] = ImagevulnerabilitiesObjectKind + obj["apiVersion"] = k8sinterface.JoinGroupVersion(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion) + obj["data"] = vulnerabilities + obj["metadata"] = metadata + + return workloadinterface.NewWorkloadObj(obj) +} + +// list all images tags +func listImagesTags(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) []string { + images := []string{} + for _, resources := range *k8sResourcesMap { + for j := range resources { + if resource, ok := allResources[resources[j]]; ok { + if resource.GetObjectType() == workloadinterface.TypeWorkloadObject { + workload := workloadinterface.NewWorkloadObj(resource.GetObject()) + if contianers, err := workload.GetContainers(); err == nil { + for i := range contianers { + images = append(images, contianers[i].Image) + } + } + if contianers, err := workload.GetInitContainers(); err == nil { + for i := range contianers { + images = append(images, contianers[i].Image) + } + } + } + } + } + } + + return shared.SliceStringToUnique(images) +} + +func imageTagsToContainerImageIdentifier(images []string) []registryvulnerabilities.ContainerImageIdentifier { + imagesIdentifiers := make([]registryvulnerabilities.ContainerImageIdentifier, len(images)) + for i := range images { + imageIdentifier := registryvulnerabilities.ContainerImageIdentifier{ + Tag: images[i], + } + // splitted := strings.Split(images[i], "/") + // if len(splitted) == 1 { + // imageIdentifier.Tag = splitted[0] + // } else if len(splitted) == 2 { + // imageIdentifier.Registry = splitted[0] + // imageIdentifier.Tag = splitted[1] + // } else if len(splitted) >= 3 { + // imageIdentifier.Registry = splitted[0] + // imageIdentifier.Repository = strings.Join(splitted[1:len(splitted)-1], "/") + // imageIdentifier.Tag = splitted[len(splitted)-1] + // } + imagesIdentifiers[i] = imageIdentifier + } + return imagesIdentifiers +} +func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) { + customerGUID := " " + clientID := " " + accessKey := " " + registry := "armoui-dev.eudev3.cyberarmorsoft.com" + + adaptors := []registryvulnerabilities.IContainerImageVulnerabilityAdaptor{} + armosecAdaptor, err := armosecadaptorv1.NewArmoAdaptor(registry, map[string]string{"accountID": customerGUID, "clientID": clientID, "accessKey": accessKey}) + if err != nil { + return nil, err + } + + adaptors = append(adaptors, armosecAdaptor) + return adaptors, nil +} From 2ffb7fcdb4798e6c2db0c1b310065a22ae85d9bc Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 1 Feb 2022 17:16:27 +0200 Subject: [PATCH 19/22] support view/set/delete commands --- cautils/customerloader.go | 155 ++++++++++++------ cautils/getter/armoapi.go | 50 +++--- cautils/getter/armoapiutils.go | 10 +- cautils/getter/getpolicies.go | 11 +- cautils/getter/getpoliciesutils.go | 2 +- clihandler/clidelete.go | 7 + clihandler/clidownload.go | 8 +- clihandler/clilist.go | 14 +- .../cliobjects}/listpolicies.go | 2 +- clihandler/cliobjects/set.go | 7 + clihandler/cliobjects/submit.go | 5 + clihandler/cliset.go | 22 +++ clihandler/cliview.go | 9 + clihandler/cmd/cluster_set.go | 4 +- clihandler/cmd/config.go | 7 +- clihandler/cmd/delete.go | 34 ++++ clihandler/cmd/download.go | 2 +- clihandler/cmd/list.go | 3 +- clihandler/cmd/rbac.go | 2 +- clihandler/cmd/results.go | 2 +- clihandler/cmd/root.go | 3 +- clihandler/cmd/scan.go | 4 +- clihandler/cmd/set.go | 53 ++++++ clihandler/cmd/submit.go | 8 +- clihandler/cmd/view.go | 36 ++++ clihandler/initcli.go | 10 +- clihandler/initcliutils.go | 4 +- resourcehandler/registrydata.go | 24 ++- .../reporter/v1/reporteventreceiver.go | 2 +- .../reporter/v2/reporteventreceiver.go | 2 +- 30 files changed, 374 insertions(+), 128 deletions(-) create mode 100644 clihandler/clidelete.go rename {cautils => clihandler/cliobjects}/listpolicies.go (80%) create mode 100644 clihandler/cliobjects/set.go create mode 100644 clihandler/cliobjects/submit.go create mode 100644 clihandler/cliset.go create mode 100644 clihandler/cliview.go create mode 100644 clihandler/cmd/delete.go create mode 100644 clihandler/cmd/set.go create mode 100644 clihandler/cmd/view.go diff --git a/cautils/customerloader.go b/cautils/customerloader.go index f0bb0334..56770636 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -23,10 +23,12 @@ func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + // ====================================================================================== type ConfigObj struct { - CustomerGUID string `json:"customerGUID"` - Token string `json:"invitationParam"` - CustomerAdminEMail string `json:"adminMail"` - ClusterName string `json:"clusterName"` + AccountID string `json:"customerGUID,omitempty"` + Token string `json:"invitationParam,omitempty"` + CustomerAdminEMail string `json:"adminMail,omitempty"` + ClusterName string `json:"clusterName,omitempty"` + ClientID string `json:"clientID,omitempty"` + AccessKey string `json:"accessKey,omitempty"` } func (co *ConfigObj) Json() []byte { @@ -38,10 +40,20 @@ func (co *ConfigObj) Json() []byte { // Config - convert ConfigObj to config file func (co *ConfigObj) Config() []byte { + + // remove cluster name before saving to file clusterName := co.ClusterName - co.ClusterName = "" // remove cluster name before saving to file - b, err := json.Marshal(co) + customerAdminEMail := co.CustomerAdminEMail + token := co.Token + co.ClusterName = "" + co.Token = "" + co.CustomerAdminEMail = "" + + b, err := json.MarshalIndent(co, "", " ") + co.ClusterName = clusterName + co.CustomerAdminEMail = customerAdminEMail + co.Token = token if err == nil { return b @@ -56,10 +68,12 @@ func (co *ConfigObj) Config() []byte { type ITenantConfig interface { // set SetTenant() error + UpdateCachedConfig() error + DeleteCachedConfig() error // getters GetClusterName() string - GetCustomerGUID() string + GetAccountID() string GetConfigObj() *ConfigObj // GetBackendAPI() getter.IBackend // GenerateURL() @@ -93,12 +107,18 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string lc.configObj = configObj } if customerGUID != "" { - lc.configObj.CustomerGUID = customerGUID // override config customerGUID + lc.configObj.AccountID = customerGUID // override config customerGUID } if clusterName != "" { lc.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName } - if lc.configObj.CustomerGUID != "" { + getAccountFromEnv(lc.configObj) + + lc.backendAPI.SetAccountID(lc.configObj.AccountID) + lc.backendAPI.SetClientID(lc.configObj.ClientID) + lc.backendAPI.SetAccessKey(lc.configObj.AccessKey) + + if lc.configObj.AccountID != "" { if err := lc.SetTenant(); err != nil { fmt.Println(err) } @@ -107,32 +127,38 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string return lc } -func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj } -func (lc *LocalConfig) GetCustomerGUID() string { return lc.configObj.CustomerGUID } -func (lc *LocalConfig) SetCustomerGUID(customerGUID string) { lc.configObj.CustomerGUID = customerGUID } -func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName } -func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() } +func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj } +func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID } +func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName } +func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() } func (lc *LocalConfig) SetTenant() error { + // ARMO tenant GUID if err := getTenantConfigFromBE(lc.backendAPI, lc.configObj); err != nil { return err } - updateConfigFile(lc.configObj) + lc.UpdateCachedConfig() return nil } +func (lc *LocalConfig) UpdateCachedConfig() error { + return updateConfigFile(lc.configObj) +} + +func (lc *LocalConfig) DeleteCachedConfig() error { + return DeleteConfigFile() +} func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) error { // get from armoBE - backendAPI.SetCustomerGUID(configObj.CustomerGUID) - tenantResponse, err := backendAPI.GetCustomerGUID() + tenantResponse, err := backendAPI.GetTenant() if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // registered tenant configObj.CustomerAdminEMail = tenantResponse.AdminMail } else { // new tenant configObj.Token = tenantResponse.Token - configObj.CustomerGUID = tenantResponse.TenantID + configObj.AccountID = tenantResponse.TenantID } } else { if err != nil && !strings.Contains(err.Error(), "already exists") { @@ -154,8 +180,11 @@ Supported environments variables: KS_DEFAULT_CONFIGMAP_NAME // name of configmap, if not set default is 'kubescape' KS_DEFAULT_CONFIGMAP_NAMESPACE // configmap namespace, if not set default is 'default' +KS_ACCOUNT_ID +KS_CLIENT_ID +KS_ACCESS_KEY + TODO - supprot: -KS_ACCOUNT // Account ID KS_CACHE // path to cached files */ type ClusterConfig struct { @@ -187,32 +216,36 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken c.configObj = configObj } if customerGUID != "" { - c.configObj.CustomerGUID = customerGUID // override config customerGUID + c.configObj.AccountID = customerGUID // override config customerGUID } if clusterName != "" { c.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName } - if c.configObj.CustomerGUID != "" { - if err := c.SetTenant(); err != nil { - fmt.Println(err) - } - } + getAccountFromEnv(c.configObj) + if c.configObj.ClusterName == "" { c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName()) } else { // override the cluster name if it has unwanted characters c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName) } + c.backendAPI.SetAccountID(c.configObj.AccountID) + c.backendAPI.SetClientID(c.configObj.ClientID) + c.backendAPI.SetAccessKey(c.configObj.AccessKey) + + if c.configObj.AccountID != "" { + if err := c.SetTenant(); err != nil { + fmt.Println(err) // TODO: print to log + } + } + return c } -func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj } -func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace } -func (c *ClusterConfig) GetCustomerGUID() string { return c.configObj.CustomerGUID } -func (c *ClusterConfig) SetCustomerGUID(customerGUID string) { c.configObj.CustomerGUID = customerGUID } -func (c *ClusterConfig) IsConfigFound() bool { - return existsConfigFile() || c.existsConfigMap() -} +func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj } +func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace } +func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID } +func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() } func (c *ClusterConfig) SetTenant() error { @@ -220,17 +253,34 @@ func (c *ClusterConfig) SetTenant() error { if err := getTenantConfigFromBE(c.backendAPI, c.configObj); err != nil { return err } - // update/create config - if c.existsConfigMap() { - c.updateConfigMap() - } else { - c.createConfigMap() - } - updateConfigFile(c.configObj) + c.UpdateCachedConfig() return nil } +func (c *ClusterConfig) UpdateCachedConfig() error { + // update/create config + if c.existsConfigMap() { + if err := c.updateConfigMap(); err != nil { + return err + } + } else { + if err := c.createConfigMap(); err != nil { + return err + } + } + return updateConfigFile(c.configObj) +} + +func (c *ClusterConfig) DeleteCachedConfig() error { + if err := c.deleteConfigMap(); err != nil { + return err + } + if err := DeleteConfigFile(); err != nil { + return err + } + return nil +} func (c *ClusterConfig) GetClusterName() string { return c.configObj.ClusterName } @@ -421,8 +471,7 @@ func (clusterConfig *ClusterConfig) IsSubmitted() bool { func (clusterConfig *ClusterConfig) IsRegistered() bool { // get from armoBE - clusterConfig.backendAPI.SetCustomerGUID(clusterConfig.GetCustomerGUID()) - tenantResponse, err := clusterConfig.backendAPI.GetCustomerGUID() + tenantResponse, err := clusterConfig.backendAPI.GetTenant() if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // this customer already belongs to some user return true @@ -431,16 +480,7 @@ func (clusterConfig *ClusterConfig) IsRegistered() bool { return false } -func (clusterConfig *ClusterConfig) DeleteConfig() error { - if err := clusterConfig.DeleteConfigMap(); err != nil { - return err - } - if err := DeleteConfigFile(); err != nil { - return err - } - return nil -} -func (clusterConfig *ClusterConfig) DeleteConfigMap() error { +func (clusterConfig *ClusterConfig) deleteConfigMap() error { return clusterConfig.k8s.KubernetesClient.CoreV1().ConfigMaps(clusterConfig.configMapNamespace).Delete(context.Background(), clusterConfig.configMapName, metav1.DeleteOptions{}) } @@ -465,3 +505,16 @@ func getConfigMapNamespace() string { } return "default" } + +func getAccountFromEnv(configObj *ConfigObj) { + // load from env + if accountID := os.Getenv("KS_ACCOUNT_ID"); accountID != "" { + configObj.AccountID = accountID + } + if clientID := os.Getenv("KS_CLIENT_ID"); clientID != "" { + configObj.ClientID = clientID + } + if accessKey := os.Getenv("KS_ACCESS_KEY"); accessKey != "" { + configObj.AccessKey = accessKey + } +} diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index f194f4f1..f70480da 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -30,24 +30,26 @@ var ( // Armo API for downloading policies type ArmoAPI struct { - httpClient *http.Client - apiURL string - erURL string - feURL string - customerGUID string + httpClient *http.Client + apiURL string + erURL string + feURL string + accountID string + clientID string + accessKey string } -var globalArmoAPIConnecctor *ArmoAPI +var globalArmoAPIConnector *ArmoAPI func SetARMOAPIConnector(armoAPI *ArmoAPI) { - globalArmoAPIConnecctor = armoAPI + globalArmoAPIConnector = armoAPI } func GetArmoAPIConnector() *ArmoAPI { - if globalArmoAPIConnecctor == nil { + if globalArmoAPIConnector == nil { glog.Error("returning nil API connector") } - return globalArmoAPIConnecctor + return globalArmoAPIConnector } func NewARMOAPIDev() *ArmoAPI { @@ -85,17 +87,15 @@ func newArmoAPI() *ArmoAPI { httpClient: &http.Client{Timeout: time.Duration(61) * time.Second}, } } -func (armoAPI *ArmoAPI) SetCustomerGUID(customerGUID string) { - armoAPI.customerGUID = customerGUID -} -func (armoAPI *ArmoAPI) GetFrontendURL() string { - return armoAPI.feURL -} - -func (armoAPI *ArmoAPI) GetReportReceiverURL() string { - return armoAPI.erURL -} +func (armoAPI *ArmoAPI) GetAccountID() string { return armoAPI.accountID } +func (armoAPI *ArmoAPI) GetClientID() string { return armoAPI.clientID } +func (armoAPI *ArmoAPI) GetAccessKey() string { return armoAPI.accessKey } +func (armoAPI *ArmoAPI) GetFrontendURL() string { return armoAPI.feURL } +func (armoAPI *ArmoAPI) GetReportReceiverURL() string { return armoAPI.erURL } +func (armoAPI *ArmoAPI) SetAccountID(accountID string) { armoAPI.accountID = accountID } +func (armoAPI *ArmoAPI) SetClientID(clientID string) { armoAPI.clientID = clientID } +func (armoAPI *ArmoAPI) SetAccessKey(accessKey string) { armoAPI.accessKey = accessKey } func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, error) { respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name), nil) @@ -146,10 +146,10 @@ func (armoAPI *ArmoAPI) GetExceptions(clusterName string) ([]armotypes.PostureEx return exceptions, nil } -func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) { - url := armoAPI.getCustomerURL() - if armoAPI.customerGUID != "" { - url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.customerGUID) +func (armoAPI *ArmoAPI) GetTenant() (*TenantResponse, error) { + url := armoAPI.getAccountURL() + if armoAPI.accountID != "" { + url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.accountID) } respStr, err := HttpGetter(armoAPI.httpClient, url, nil) if err != nil { @@ -159,14 +159,14 @@ func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) { if err = JSONDecoder(respStr).Decode(tenant); err != nil { return nil, err } - + armoAPI.accountID = tenant.TenantID return tenant, nil } // ControlsInputs // map[][] func (armoAPI *ArmoAPI) GetAccountConfig(clusterName string) (*armotypes.CustomerConfig, error) { accountConfig := &armotypes.CustomerConfig{} - if armoAPI.customerGUID == "" { + if armoAPI.accountID == "" { return accountConfig, nil } respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getAccountConfig(clusterName), nil) diff --git a/cautils/getter/armoapiutils.go b/cautils/getter/armoapiutils.go index 3c14040f..ab326010 100644 --- a/cautils/getter/armoapiutils.go +++ b/cautils/getter/armoapiutils.go @@ -13,7 +13,7 @@ func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string { u.Host = armoAPI.apiURL u.Path = "api/v1/armoFrameworks" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) if isNativeFramework(frameworkName) { q.Add("frameworkName", strings.ToUpper(frameworkName)) } else { @@ -31,7 +31,7 @@ func (armoAPI *ArmoAPI) getListFrameworkURL() string { u.Host = armoAPI.apiURL u.Path = "api/v1/armoFrameworks" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) u.RawQuery = q.Encode() return u.String() @@ -43,7 +43,7 @@ func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string { u.Path = "api/v1/armoPostureExceptions" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) // if clusterName != "" { // TODO - fix customer name support in Armo BE // q.Add("clusterName", clusterName) // } @@ -59,7 +59,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string { u.Path = "api/v1/armoCustomerConfiguration" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) if clusterName != "" { // TODO - fix customer name support in Armo BE q.Add("clusterName", clusterName) } @@ -68,7 +68,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string { return u.String() } -func (armoAPI *ArmoAPI) getCustomerURL() string { +func (armoAPI *ArmoAPI) getAccountURL() string { u := url.URL{} u.Scheme = "https" u.Host = armoAPI.apiURL diff --git a/cautils/getter/getpolicies.go b/cautils/getter/getpolicies.go index cb3abdf8..572aed32 100644 --- a/cautils/getter/getpolicies.go +++ b/cautils/getter/getpolicies.go @@ -24,8 +24,15 @@ type IExceptionsGetter interface { GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) } type IBackend interface { - GetCustomerGUID() (*TenantResponse, error) - SetCustomerGUID(customerGUID string) + GetAccountID() string + GetClientID() string + GetAccessKey() string + + SetAccountID(accountID string) + SetClientID(clientID string) + SetAccessKey(accessKey string) + + GetTenant() (*TenantResponse, error) } type IControlsInputsGetter interface { diff --git a/cautils/getter/getpoliciesutils.go b/cautils/getter/getpoliciesutils.go index fa859594..a88ddcf0 100644 --- a/cautils/getter/getpoliciesutils.go +++ b/cautils/getter/getpoliciesutils.go @@ -21,7 +21,7 @@ func GetDefaultPath(name string) string { } func SaveInFile(policy interface{}, pathStr string) error { - encodedData, err := json.Marshal(policy) + encodedData, err := json.MarshalIndent(policy, "", " ") if err != nil { return err } diff --git a/clihandler/clidelete.go b/clihandler/clidelete.go new file mode 100644 index 00000000..77e9f7ba --- /dev/null +++ b/clihandler/clidelete.go @@ -0,0 +1,7 @@ +package clihandler + +func CliDelete() error { + + tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface + return tenant.DeleteCachedConfig() +} diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go index ca07847a..74778552 100644 --- a/clihandler/clidownload.go +++ b/clihandler/clidownload.go @@ -75,7 +75,7 @@ func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error { func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) - controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetCustomerGUID(), nil) + controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetAccountID(), nil) controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName()) if err != nil { return err @@ -97,7 +97,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) exceptionsGetter := getExceptionsGetter("") exceptions := []armotypes.PostureExceptionPolicy{} - if tenant.GetCustomerGUID() != "" { + if tenant.GetAccountID() != "" { exceptions, err = exceptionsGetter.GetExceptions(tenant.GetClusterName()) if err != nil { return err @@ -118,7 +118,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { func downloadFramework(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil) if downloadInfo.Name == "" { // if framework name not specified - download all frameworks @@ -154,7 +154,7 @@ func downloadFramework(downloadInfo *cautils.DownloadInfo) error { func downloadControl(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil) if downloadInfo.Name == "" { // TODO - support diff --git a/clihandler/clilist.go b/clihandler/clilist.go index e0c4878e..dda856ee 100644 --- a/clihandler/clilist.go +++ b/clihandler/clilist.go @@ -5,11 +5,11 @@ import ( "sort" "strings" - "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" + "github.com/armosec/kubescape/clihandler/cliobjects" ) -var listFunc = map[string]func(*cautils.ListPolicies) ([]string, error){ +var listFunc = map[string]func(*cliobjects.ListPolicies) ([]string, error){ "controls": listControls, "frameworks": listFrameworks, } @@ -21,7 +21,7 @@ func ListSupportCommands() []string { } return commands } -func CliList(listPolicies *cautils.ListPolicies) error { +func CliList(listPolicies *cliobjects.ListPolicies) error { if f, ok := listFunc[listPolicies.Target]; ok { policies, err := f(listPolicies) if err != nil { @@ -40,16 +40,16 @@ func CliList(listPolicies *cautils.ListPolicies) error { return fmt.Errorf("unknown command to download") } -func listFrameworks(listPolicies *cautils.ListPolicies) ([]string, error) { +func listFrameworks(listPolicies *cliobjects.ListPolicies) ([]string, error) { tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil) return listFrameworksNames(g), nil } -func listControls(listPolicies *cautils.ListPolicies) ([]string, error) { +func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) { tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil) l := getter.ListName if listPolicies.ListIDs { l = getter.ListID diff --git a/cautils/listpolicies.go b/clihandler/cliobjects/listpolicies.go similarity index 80% rename from cautils/listpolicies.go rename to clihandler/cliobjects/listpolicies.go index 044c2c00..c8385b0c 100644 --- a/cautils/listpolicies.go +++ b/clihandler/cliobjects/listpolicies.go @@ -1,4 +1,4 @@ -package cautils +package cliobjects type ListPolicies struct { Target string diff --git a/clihandler/cliobjects/set.go b/clihandler/cliobjects/set.go new file mode 100644 index 00000000..53ab2940 --- /dev/null +++ b/clihandler/cliobjects/set.go @@ -0,0 +1,7 @@ +package cliobjects + +type SetConfig struct { + Account string + ClientID string + AccessKey string +} diff --git a/clihandler/cliobjects/submit.go b/clihandler/cliobjects/submit.go new file mode 100644 index 00000000..e250e880 --- /dev/null +++ b/clihandler/cliobjects/submit.go @@ -0,0 +1,5 @@ +package cliobjects + +type Submit struct { + Account string +} diff --git a/clihandler/cliset.go b/clihandler/cliset.go new file mode 100644 index 00000000..3ff6d3da --- /dev/null +++ b/clihandler/cliset.go @@ -0,0 +1,22 @@ +package clihandler + +import ( + "github.com/armosec/kubescape/clihandler/cliobjects" +) + +func CliSetConfig(setConfig *cliobjects.SetConfig) error { + + tenant := getTenantConfig("", "", getKubernetesApi()) + + if setConfig.Account != "" { + tenant.GetConfigObj().AccountID = setConfig.Account + } + if setConfig.AccessKey != "" { + tenant.GetConfigObj().AccessKey = setConfig.AccessKey + } + if setConfig.ClientID != "" { + tenant.GetConfigObj().ClientID = setConfig.ClientID + } + + return tenant.UpdateCachedConfig() +} diff --git a/clihandler/cliview.go b/clihandler/cliview.go new file mode 100644 index 00000000..bd76ecb7 --- /dev/null +++ b/clihandler/cliview.go @@ -0,0 +1,9 @@ +package clihandler + +import "fmt" + +func CliView() error { + tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface + fmt.Printf("%s\n", tenant.GetConfigObj().Config()) + return nil +} diff --git a/clihandler/cmd/cluster_set.go b/clihandler/cmd/cluster_set.go index 609d0ca5..5103c7ea 100644 --- a/clihandler/cmd/cluster_set.go +++ b/clihandler/cmd/cluster_set.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" ) -var setCmd = &cobra.Command{ +var setClusterCmd = &cobra.Command{ Use: "set =", Short: "Set configuration in cluster", Long: ``, @@ -40,5 +40,5 @@ var setCmd = &cobra.Command{ } func init() { - clusterCmd.AddCommand(setCmd) + clusterCmd.AddCommand(setClusterCmd) } diff --git a/clihandler/cmd/config.go b/clihandler/cmd/config.go index 10b0f3ec..cedcd969 100644 --- a/clihandler/cmd/config.go +++ b/clihandler/cmd/config.go @@ -6,9 +6,10 @@ import ( // configCmd represents the config command var configCmd = &cobra.Command{ - Use: "config", - Short: "Set configuration", - Long: ``, + Use: "config", + Short: "Set configuration", + Long: ``, + Deprecated: "use the 'set' command instead", Run: func(cmd *cobra.Command, args []string) { }, } diff --git a/clihandler/cmd/delete.go b/clihandler/cmd/delete.go new file mode 100644 index 00000000..5caf85e2 --- /dev/null +++ b/clihandler/cmd/delete.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/armosec/kubescape/clihandler" + "github.com/spf13/cobra" +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete cached configurations and other data", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + }, +} + +var deleteConfigCmd = &cobra.Command{ + Use: "config", + Short: "Delete cached configurations", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliDelete(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + }, +} + +func init() { + rootCmd.AddCommand(deleteCmd) + deleteCmd.AddCommand(deleteConfigCmd) +} diff --git a/clihandler/cmd/download.go b/clihandler/cmd/download.go index 412c3733..77d9166f 100644 --- a/clihandler/cmd/download.go +++ b/clihandler/cmd/download.go @@ -69,7 +69,7 @@ func init() { // cobra.OnInitialize(initConfig) rootCmd.AddCommand(downloadCmd) - downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/.json`") downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") + downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/.json`") } diff --git a/clihandler/cmd/list.go b/clihandler/cmd/list.go index 06befd99..0c9706b8 100644 --- a/clihandler/cmd/list.go +++ b/clihandler/cmd/list.go @@ -7,6 +7,7 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/clihandler" + "github.com/armosec/kubescape/clihandler/cliobjects" "github.com/spf13/cobra" ) @@ -28,7 +29,7 @@ var ( https://hub.armo.cloud/docs/controls ` ) -var listPolicies = cautils.ListPolicies{} +var listPolicies = cliobjects.ListPolicies{} var listCmd = &cobra.Command{ Use: "list [flags]", diff --git a/clihandler/cmd/rbac.go b/clihandler/cmd/rbac.go index 9ece1ba4..2762ddf7 100644 --- a/clihandler/cmd/rbac.go +++ b/clihandler/cmd/rbac.go @@ -29,7 +29,7 @@ var rabcCmd = &cobra.Command{ } // list RBAC - rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetCustomerGUID(), clusterConfig.GetClusterName())) + rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetClusterName())) // submit resources r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) diff --git a/clihandler/cmd/results.go b/clihandler/cmd/results.go index 081e9913..1932ccb9 100644 --- a/clihandler/cmd/results.go +++ b/clihandler/cmd/results.go @@ -67,7 +67,7 @@ var resultsCmd = &cobra.Command{ return err } - resultsObjects := NewResultsObject(clusterConfig.GetCustomerGUID(), clusterConfig.GetClusterName(), args[0]) + resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetClusterName(), args[0]) // submit resources r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) diff --git a/clihandler/cmd/root.go b/clihandler/cmd/root.go index 4fd5792f..c035e216 100644 --- a/clihandler/cmd/root.go +++ b/clihandler/cmd/root.go @@ -10,7 +10,6 @@ import ( "github.com/spf13/cobra" ) -var cfgFile string var armoBEURLs = "" const envFlagUsage = "Send report results to specific URL. Format:,,.\n\t\tExample:report.armo.cloud,api.armo.cloud,portal.armo.cloud" @@ -31,7 +30,7 @@ func Execute() { } func init() { - rootCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") + flag.CommandLine.StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().MarkHidden("environment") diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 76761bc7..20469c3e 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -42,7 +42,9 @@ func init() { cobra.OnInitialize(frameworkInitConfig) rootCmd.AddCommand(scanCmd) - rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context") + + scanCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") + scanCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context") scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal") scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal") scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them") diff --git a/clihandler/cmd/set.go b/clihandler/cmd/set.go new file mode 100644 index 00000000..a54d4d2c --- /dev/null +++ b/clihandler/cmd/set.go @@ -0,0 +1,53 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/armosec/kubescape/clihandler" + "github.com/armosec/kubescape/clihandler/cliobjects" + "github.com/spf13/cobra" +) + +var ( + setConfigExample = ` + # Set account credentials + kubescape set config --account --client-id --access-key +` +) +var setConfig = cliobjects.SetConfig{} + +// configCmd represents the config command +var setCmd = &cobra.Command{ + Use: "set", + Short: "Set configurations and other data", + Long: ``, + Example: setConfigExample, + Run: func(cmd *cobra.Command, args []string) { + }, +} + +// configCmd represents the config command +var setConfigCmd = &cobra.Command{ + Use: "config", + Short: "Set cached configurations", + Long: ``, + Example: setConfigExample, + Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliSetConfig(&setConfig); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + }, +} + +func init() { + + setConfigCmd.PersistentFlags().StringVarP(&setConfig.Account, "account", "", "", "Set Armo account ID") + setConfigCmd.PersistentFlags().StringVarP(&setConfig.ClientID, "client-id", "", "", "Set Armo client ID") + setConfigCmd.PersistentFlags().StringVarP(&setConfig.AccessKey, "access-key", "", "", "Set Armo access key") + + rootCmd.AddCommand(setCmd) + setCmd.AddCommand(setConfigCmd) + +} diff --git a/clihandler/cmd/submit.go b/clihandler/cmd/submit.go index 1f27b4eb..7677fa84 100644 --- a/clihandler/cmd/submit.go +++ b/clihandler/cmd/submit.go @@ -4,9 +4,12 @@ import ( "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" + "github.com/armosec/kubescape/clihandler/cliobjects" "github.com/spf13/cobra" ) +var submitInfo cliobjects.Submit + var submitCmd = &cobra.Command{ Use: "submit ", Short: "Submit an object to the Kubescape SaaS version", @@ -16,12 +19,13 @@ var submitCmd = &cobra.Command{ } func init() { + submitCmd.PersistentFlags().StringVarP(&submitInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") rootCmd.AddCommand(submitCmd) } func getSubmittedClusterConfig(k8s *k8sinterface.KubernetesApi) (*cautils.ClusterConfig, error) { - clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit - if clusterConfig.GetCustomerGUID() != "" { + clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), submitInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit + if clusterConfig.GetAccountID() != "" { if err := clusterConfig.SetTenant(); err != nil { return clusterConfig, err } diff --git a/clihandler/cmd/view.go b/clihandler/cmd/view.go new file mode 100644 index 00000000..77cbdf2c --- /dev/null +++ b/clihandler/cmd/view.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/armosec/kubescape/clihandler" + "github.com/spf13/cobra" +) + +// configCmd represents the config command +var viewCmd = &cobra.Command{ + Use: "view", + Short: "View configurations and other data", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + }, +} + +// configCmd represents the config command +var viewConfigCmd = &cobra.Command{ + Use: "config", + Short: "View cached configurations", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliView(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + }, +} + +func init() { + rootCmd.AddCommand(viewCmd) + viewCmd.AddCommand(viewConfigCmd) +} diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 375b2638..81d7ddf5 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -99,16 +99,16 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { processNotification := make(chan *cautils.OPASessionObj) reportResults := make(chan *cautils.OPASessionObj) - cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated - cautils.CustomerGUID = interfaces.tenantConfig.GetCustomerGUID() // TODO - Deprecated + cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated + cautils.CustomerGUID = interfaces.tenantConfig.GetAccountID() // TODO - Deprecated interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName()) - interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID()) + interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetAccountID()) downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release // set policy getter only after setting the customerGUID - scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetCustomerGUID(), scanInfo.FrameworkScan, downloadReleasedPolicy) - scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy) + scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy) + scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy) scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions) // TODO - list supported frameworks/controls diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 1c231dc5..56c122e5 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -42,7 +42,7 @@ func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter { func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects { if submit { - return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName())) + return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetAccountID(), tenantConfig.GetClusterName())) } return nil } @@ -153,7 +153,6 @@ func getPolicyGetter(loadPoliciesFromFile []string, accountID string, frameworkS } if accountID != "" && frameworkScope { g := getter.GetArmoAPIConnector() // download policy from ARMO backend - g.SetCustomerGUID(accountID) return g } if downloadReleasedPolicy == nil { @@ -184,7 +183,6 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele } if accountID != "" { g := getter.GetArmoAPIConnector() // download config from ARMO backend - g.SetCustomerGUID(accountID) return g } if downloadReleasedPolicy == nil { diff --git a/resourcehandler/registrydata.go b/resourcehandler/registrydata.go index 65df1165..7805f1a9 100644 --- a/resourcehandler/registrydata.go +++ b/resourcehandler/registrydata.go @@ -4,6 +4,7 @@ import ( "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" armosecadaptorv1 "github.com/armosec/kubescape/registryadaptors/armosec/v1" "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" "github.com/armosec/opa-utils/shared" @@ -131,17 +132,24 @@ func imageTagsToContainerImageIdentifier(images []string) []registryvulnerabilit return imagesIdentifiers } func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) { - customerGUID := " " - clientID := " " - accessKey := " " - registry := "armoui-dev.eudev3.cyberarmorsoft.com" adaptors := []registryvulnerabilities.IContainerImageVulnerabilityAdaptor{} - armosecAdaptor, err := armosecadaptorv1.NewArmoAdaptor(registry, map[string]string{"accountID": customerGUID, "clientID": clientID, "accessKey": accessKey}) - if err != nil { - return nil, err + + armoAPI := getter.GetArmoAPIConnector() + if armoAPI == nil { + accountID := armoAPI.GetAccountID() + clientID := armoAPI.GetClientID() + accessKey := armoAPI.GetAccessKey() + if accountID != "" && clientID != "" && accessKey != "" { + armosecAdaptor, err := armosecadaptorv1.NewArmoAdaptor(armoAPI.GetFrontendURL(), map[string]string{"accountID": accountID, "clientID": clientID, "accessKey": accessKey}) + if err != nil { + return nil, err + } + adaptors = append(adaptors, armosecAdaptor) + } else { + // TODO - print warning + } } - adaptors = append(adaptors, armosecAdaptor) return adaptors, nil } diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go index 853700ef..88d1087b 100644 --- a/resultshandling/reporter/v1/reporteventreceiver.go +++ b/resultshandling/reporter/v1/reporteventreceiver.go @@ -31,7 +31,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive return &ReportEventReceiver{ httpClient: &http.Client{}, clusterName: tenantConfig.ClusterName, - customerGUID: tenantConfig.CustomerGUID, + customerGUID: tenantConfig.AccountID, token: tenantConfig.Token, customerAdminEMail: tenantConfig.CustomerAdminEMail, } diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 03664b97..3784eab4 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -32,7 +32,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive return &ReportEventReceiver{ httpClient: &http.Client{}, clusterName: tenantConfig.ClusterName, - customerGUID: tenantConfig.CustomerGUID, + customerGUID: tenantConfig.AccountID, token: tenantConfig.Token, customerAdminEMail: tenantConfig.CustomerAdminEMail, } From 69814039cad3d9125830652d35e43ca68e35d775 Mon Sep 17 00:00:00 2001 From: dwertent Date: Wed, 2 Feb 2022 08:12:41 +0200 Subject: [PATCH 20/22] using accountID instead of customerGUID --- cautils/customerloader.go | 14 +-- cautils/getter/armoapi.go | 4 +- policyhandler/handlepullpolicies.go | 2 +- registryadaptors/README.md | 171 ++++------------------------ registryadaptors/contribute.md | 164 ++++++++++++++++++++++++++ 5 files changed, 195 insertions(+), 160 deletions(-) create mode 100644 registryadaptors/contribute.md diff --git a/cautils/customerloader.go b/cautils/customerloader.go index 56770636..bb4ebc0c 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -23,7 +23,8 @@ func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + // ====================================================================================== type ConfigObj struct { - AccountID string `json:"customerGUID,omitempty"` + AccountID string `json:"accountID,omitempty"` + CustomerGUID string `json:"customerGUID,omitempty"` // Deprecated Token string `json:"invitationParam,omitempty"` CustomerAdminEMail string `json:"adminMail,omitempty"` ClusterName string `json:"clusterName,omitempty"` @@ -31,13 +32,6 @@ type ConfigObj struct { AccessKey string `json:"accessKey,omitempty"` } -func (co *ConfigObj) Json() []byte { - if b, err := json.Marshal(co); err == nil { - return b - } - return []byte{} -} - // Config - convert ConfigObj to config file func (co *ConfigObj) Config() []byte { @@ -459,6 +453,10 @@ func readConfig(dat []byte) (*ConfigObj, error) { if err := json.Unmarshal(dat, configObj); err != nil { return nil, err } + if configObj.AccountID == "" { + configObj.AccountID = configObj.CustomerGUID + } + configObj.CustomerGUID = "" return configObj, nil } diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index f70480da..32225519 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -159,7 +159,9 @@ func (armoAPI *ArmoAPI) GetTenant() (*TenantResponse, error) { if err = JSONDecoder(respStr).Decode(tenant); err != nil { return nil, err } - armoAPI.accountID = tenant.TenantID + if tenant.TenantID != "" { + armoAPI.accountID = tenant.TenantID + } return tenant, nil } diff --git a/policyhandler/handlepullpolicies.go b/policyhandler/handlepullpolicies.go index 51b911c8..0d9b2725 100644 --- a/policyhandler/handlepullpolicies.go +++ b/policyhandler/handlepullpolicies.go @@ -16,7 +16,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol return err } if len(frameworks) == 0 { - return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ",")) + return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ", ")) } policiesAndResources.Frameworks = frameworks diff --git a/registryadaptors/README.md b/registryadaptors/README.md index abf0688e..e57330dc 100644 --- a/registryadaptors/README.md +++ b/registryadaptors/README.md @@ -1,164 +1,35 @@ -# Container image vulnerability adaptor interface +# Integrate With Vulnerability Server -## High level design of Kubescape +There are some controls that check the relation between the kubernetes manifest and vulnerabilities. +For these controls to work properly, it is necasery to +## Supported Servers +* Armosec -### Layers +# Integrate With Armosec Server -* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego -* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter -* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running -* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration -* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to. -* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption +1. Navigate to the [armosec.io](https://portal.armo.cloud/) +2. Click Profile(top right icon)->"User Management"->"API Tokens" and Generate a token +3. Copy the clientID and accessKey and run: ``` - ----------------------- -| Controls/Rules (rego) | - ----------------------- - | - ----------------------- -| OPA engine | - ----------------------- - | - ----------------------- -| Rules processor | - ----------------------- - | - ----------------------- -| Data sources | - ----------------------- - | - ======================= -| CIV adaption interface| <- Adding this layer in this proposal - ======================= - | - ----------------------- -| Specific CIV adaptors | <- Will be implemented based on this proposal - ----------------------- - - - +kubescape set config --access-key <> --client-id <> ``` - -## Functionalities to cover - -The interface needs to cover the following functionalities: - -* Authentication against the information source (abstracted login) -* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone) -* Reading image scan status (with last scan date and etc.) -* Getting vulnerability information for a given image -* Getting image information - * Image manifests - * Image BOMs (bill of material) - -## Go API proposal - +4. Confirm the keys are set ``` - -/*type ContainerImageRegistryCredentials struct { - Password string - Tag string - Hash string -}*/ - -type ContainerImageIdentifier struct { - Registry string - Repository string - Tag string - Hash string -} - -type ContainerImageScanStatus struct { - ImageID ContainerImageIdentifier - IsScanAvailable bool - IsBomAvailable bool - LastScanDate time.Time -} - -type ContainerImageVulnerabilityReport struct { - ImageID ContainerImageIdentifier - // TBD -} - -type ContainerImageInformation struct { - ImageID ContainerImageIdentifier - Bom []string - ImageManifest Manifest // will use here Docker package definition -} - -type IContainerImageVulnerabilityAdaptor interface { - // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level - // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} - Login(registry string, credentials map[string]string) error - - // For "help" purposes - DescribeAdaptor() string - - GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) - - GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) - - GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) -} +kubescape view config ``` - - - -# Integration - -# Input - -The objects received from the interface will be converted to an IMetadata compatible objects as following - +Expecting: ``` { - "apiVersion": "image.vulnscan.com/v1", - "kind": "ImageVulnerabilities", - "metadata": { - "name": "nginx:latest" - }, - "data": { - // list of vulnerabilities - } + "accountID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "clientID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "accessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } ``` +> If you are missing the `accountID` field, set it by running `kubescape set config --account <>` - -# Output - -The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control - +For CICD, set environments variables as following: ``` -{ - "apiVersion": "result.vulnscan.com/v1", - "kind": "Pod", - "metadata": { - "name": "nginx" - "namespace": "default" - - }, - "relatedObjects": [ - { - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "name": "nginx" - "namespace": "default" - }, - "spec": { - // podSpec - }, - }, - { - "apiVersion": "image.vulnscan.com/v1", - "kind": "ImageVulnerabilities", - "metadata": { - "name": "nginx:latest", - }, - "data": { - // list of vulnerabilities - } - } - ] -} +KS_ACCOUNT_ID // account id +KS_CLIENT_ID // client id +KS_ACCESS_KEY // access key ``` \ No newline at end of file diff --git a/registryadaptors/contribute.md b/registryadaptors/contribute.md new file mode 100644 index 00000000..abf0688e --- /dev/null +++ b/registryadaptors/contribute.md @@ -0,0 +1,164 @@ +# Container image vulnerability adaptor interface + +## High level design of Kubescape + +### Layers + +* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego +* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter +* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running +* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration +* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to. +* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption +``` + ----------------------- +| Controls/Rules (rego) | + ----------------------- + | + ----------------------- +| OPA engine | + ----------------------- + | + ----------------------- +| Rules processor | + ----------------------- + | + ----------------------- +| Data sources | + ----------------------- + | + ======================= +| CIV adaption interface| <- Adding this layer in this proposal + ======================= + | + ----------------------- +| Specific CIV adaptors | <- Will be implemented based on this proposal + ----------------------- + + + +``` + +## Functionalities to cover + +The interface needs to cover the following functionalities: + +* Authentication against the information source (abstracted login) +* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone) +* Reading image scan status (with last scan date and etc.) +* Getting vulnerability information for a given image +* Getting image information + * Image manifests + * Image BOMs (bill of material) + +## Go API proposal + +``` + +/*type ContainerImageRegistryCredentials struct { + Password string + Tag string + Hash string +}*/ + +type ContainerImageIdentifier struct { + Registry string + Repository string + Tag string + Hash string +} + +type ContainerImageScanStatus struct { + ImageID ContainerImageIdentifier + IsScanAvailable bool + IsBomAvailable bool + LastScanDate time.Time +} + +type ContainerImageVulnerabilityReport struct { + ImageID ContainerImageIdentifier + // TBD +} + +type ContainerImageInformation struct { + ImageID ContainerImageIdentifier + Bom []string + ImageManifest Manifest // will use here Docker package definition +} + +type IContainerImageVulnerabilityAdaptor interface { + // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level + // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} + Login(registry string, credentials map[string]string) error + + // For "help" purposes + DescribeAdaptor() string + + GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) + + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} +``` + + + +# Integration + +# Input + +The objects received from the interface will be converted to an IMetadata compatible objects as following + +``` +{ + "apiVersion": "image.vulnscan.com/v1", + "kind": "ImageVulnerabilities", + "metadata": { + "name": "nginx:latest" + }, + "data": { + // list of vulnerabilities + } +} +``` + + +# Output + +The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control + +``` +{ + "apiVersion": "result.vulnscan.com/v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + "namespace": "default" + + }, + "relatedObjects": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + "namespace": "default" + }, + "spec": { + // podSpec + }, + }, + { + "apiVersion": "image.vulnscan.com/v1", + "kind": "ImageVulnerabilities", + "metadata": { + "name": "nginx:latest", + }, + "data": { + // list of vulnerabilities + } + } + ] +} +``` \ No newline at end of file From d1e02dc298084c80c94c3119a9c303b5cc299e10 Mon Sep 17 00:00:00 2001 From: dwertent Date: Wed, 2 Feb 2022 14:24:53 +0200 Subject: [PATCH 21/22] update config command --- build.py | 2 +- cautils/customerloader.go | 4 +- clihandler/cmd/cluster.go | 7 +- clihandler/cmd/cluster_get.go | 7 +- clihandler/cmd/cluster_set.go | 7 +- clihandler/cmd/config.go | 116 ++++++++++++++++++++++++++++++++-- clihandler/cmd/delete.go | 34 ---------- clihandler/cmd/local.go | 7 +- clihandler/cmd/local_get.go | 7 +- clihandler/cmd/local_set.go | 7 +- clihandler/cmd/set.go | 53 ---------------- clihandler/cmd/view.go | 36 ----------- clihandler/initcli.go | 17 +---- go.mod | 2 +- go.sum | 4 +- registryadaptors/README.md | 9 ++- 16 files changed, 151 insertions(+), 168 deletions(-) delete mode 100644 clihandler/cmd/delete.go delete mode 100644 clihandler/cmd/set.go delete mode 100644 clihandler/cmd/view.go diff --git a/build.py b/build.py index ec8a5497..9db7d938 100644 --- a/build.py +++ b/build.py @@ -37,7 +37,7 @@ def main(): print("Building Kubescape") # print environment variables - print(os.environ) + # print(os.environ) # Set some variables packageName = getPackageName() diff --git a/cautils/customerloader.go b/cautils/customerloader.go index bb4ebc0c..d2263a2c 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -24,12 +24,12 @@ func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + type ConfigObj struct { AccountID string `json:"accountID,omitempty"` + ClientID string `json:"clientID,omitempty"` + AccessKey string `json:"accessKey,omitempty"` CustomerGUID string `json:"customerGUID,omitempty"` // Deprecated Token string `json:"invitationParam,omitempty"` CustomerAdminEMail string `json:"adminMail,omitempty"` ClusterName string `json:"clusterName,omitempty"` - ClientID string `json:"clientID,omitempty"` - AccessKey string `json:"accessKey,omitempty"` } // Config - convert ConfigObj to config file diff --git a/clihandler/cmd/cluster.go b/clihandler/cmd/cluster.go index e81ec7d0..30f75f25 100644 --- a/clihandler/cmd/cluster.go +++ b/clihandler/cmd/cluster.go @@ -6,9 +6,10 @@ import ( // clusterCmd represents the cluster command var clusterCmd = &cobra.Command{ - Use: "cluster", - Short: "Set configuration for cluster", - Long: ``, + Use: "cluster", + Short: "Set configuration for cluster", + Long: ``, + Deprecated: "use the 'set' command instead", Run: func(cmd *cobra.Command, args []string) { }, } diff --git a/clihandler/cmd/cluster_get.go b/clihandler/cmd/cluster_get.go index a56ea117..45cab878 100644 --- a/clihandler/cmd/cluster_get.go +++ b/clihandler/cmd/cluster_get.go @@ -11,9 +11,10 @@ import ( ) var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get configuration in cluster", - Long: ``, + Use: "get ", + Short: "Get configuration in cluster", + Long: ``, + Deprecated: "use the 'view' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument") diff --git a/clihandler/cmd/cluster_set.go b/clihandler/cmd/cluster_set.go index 5103c7ea..4858e0bb 100644 --- a/clihandler/cmd/cluster_set.go +++ b/clihandler/cmd/cluster_set.go @@ -11,9 +11,10 @@ import ( ) var setClusterCmd = &cobra.Command{ - Use: "set =", - Short: "Set configuration in cluster", - Long: ``, + Use: "set =", + Short: "Set configuration in cluster", + Long: ``, + Deprecated: "use the 'set' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument: =") diff --git a/clihandler/cmd/config.go b/clihandler/cmd/config.go index cedcd969..f11f1954 100644 --- a/clihandler/cmd/config.go +++ b/clihandler/cmd/config.go @@ -1,19 +1,127 @@ package cmd import ( + "fmt" + "os" + "strings" + + "github.com/armosec/kubescape/clihandler" + "github.com/armosec/kubescape/clihandler/cliobjects" "github.com/spf13/cobra" ) +var ( + configExample = ` + # View cached configurations + kubescape config view + + # Delete cached configurations + kubescape config delete + + # Set cached configurations + kubescape config set --help +` + setConfigExample = ` + # Set account id + kubescape config set accountID + + # Set client id + kubescape config set clientID + + # Set access key + kubescape config set accessKey +` +) + // configCmd represents the config command var configCmd = &cobra.Command{ - Use: "config", - Short: "Set configuration", - Long: ``, - Deprecated: "use the 'set' command instead", + Use: "config", + Short: "handle cached configurations", + Example: configExample, +} + +var setConfig = cliobjects.SetConfig{} + +// configCmd represents the config command +var configSetCmd = &cobra.Command{ + Use: "set", + Short: fmt.Sprintf("Set configurations, supported: %s", strings.Join(stringKeysToSlice(supportConfigSet), "/")), + Example: setConfigExample, + ValidArgs: stringKeysToSlice(supportConfigSet), + RunE: func(cmd *cobra.Command, args []string) error { + if err := parseSetArgs(args); err != nil { + return err + } + if err := clihandler.CliSetConfig(&setConfig); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + return nil + }, +} + +var supportConfigSet = map[string]func(*cliobjects.SetConfig, string){ + "accountID": func(s *cliobjects.SetConfig, account string) { s.Account = account }, + "clientID": func(s *cliobjects.SetConfig, clientID string) { s.ClientID = clientID }, + "accessKey": func(s *cliobjects.SetConfig, accessKey string) { s.AccessKey = accessKey }, +} + +func stringKeysToSlice(m map[string]func(*cliobjects.SetConfig, string)) []string { + l := []string{} + for i := range m { + l = append(l, i) + } + return l +} + +func parseSetArgs(args []string) error { + var key string + var value string + if len(args) == 1 { + if keyValue := strings.Split(args[0], "="); len(keyValue) == 2 { + key = keyValue[0] + value = keyValue[1] + } + } else if len(args) == 2 { + key = args[0] + value = args[1] + } + if setConfigFunc, ok := supportConfigSet[key]; ok { + setConfigFunc(&setConfig, value) + } else { + return fmt.Errorf("key '%s' unknown . supported: %s", key, strings.Join(stringKeysToSlice(supportConfigSet), "/")) + } + return nil +} + +var configDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete cached configurations", + Long: ``, Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliDelete(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + }, +} + +// configCmd represents the config command +var configViewCmd = &cobra.Command{ + Use: "view", + Short: "View cached configurations", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliView(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } }, } func init() { rootCmd.AddCommand(configCmd) + configCmd.AddCommand(configSetCmd) + configCmd.AddCommand(configDeleteCmd) + configCmd.AddCommand(configViewCmd) } diff --git a/clihandler/cmd/delete.go b/clihandler/cmd/delete.go deleted file mode 100644 index 5caf85e2..00000000 --- a/clihandler/cmd/delete.go +++ /dev/null @@ -1,34 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/armosec/kubescape/clihandler" - "github.com/spf13/cobra" -) - -var deleteCmd = &cobra.Command{ - Use: "delete", - Short: "Delete cached configurations and other data", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -var deleteConfigCmd = &cobra.Command{ - Use: "config", - Short: "Delete cached configurations", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - if err := clihandler.CliDelete(); err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - }, -} - -func init() { - rootCmd.AddCommand(deleteCmd) - deleteCmd.AddCommand(deleteConfigCmd) -} diff --git a/clihandler/cmd/local.go b/clihandler/cmd/local.go index 75e7b680..224de282 100644 --- a/clihandler/cmd/local.go +++ b/clihandler/cmd/local.go @@ -5,9 +5,10 @@ import ( ) var localCmd = &cobra.Command{ - Use: "local", - Short: "Set configuration locally (for config.json)", - Long: ``, + Use: "local", + Short: "Set configuration locally (for config.json)", + Long: ``, + Deprecated: "use the 'set' command instead", Run: func(cmd *cobra.Command, args []string) { }, } diff --git a/clihandler/cmd/local_get.go b/clihandler/cmd/local_get.go index 9807e794..d3bde11b 100644 --- a/clihandler/cmd/local_get.go +++ b/clihandler/cmd/local_get.go @@ -9,9 +9,10 @@ import ( ) var localGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get configuration locally", - Long: ``, + Use: "get ", + Short: "Get configuration locally", + Long: ``, + Deprecated: "use the 'view' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument") diff --git a/clihandler/cmd/local_set.go b/clihandler/cmd/local_set.go index e5e8c7c4..0b2cc89a 100644 --- a/clihandler/cmd/local_set.go +++ b/clihandler/cmd/local_set.go @@ -9,9 +9,10 @@ import ( ) var localSetCmd = &cobra.Command{ - Use: "set =", - Short: "Set configuration locally", - Long: ``, + Use: "set =", + Short: "Set configuration locally", + Long: ``, + Deprecated: "use the 'set' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument: =") diff --git a/clihandler/cmd/set.go b/clihandler/cmd/set.go deleted file mode 100644 index a54d4d2c..00000000 --- a/clihandler/cmd/set.go +++ /dev/null @@ -1,53 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/armosec/kubescape/clihandler" - "github.com/armosec/kubescape/clihandler/cliobjects" - "github.com/spf13/cobra" -) - -var ( - setConfigExample = ` - # Set account credentials - kubescape set config --account --client-id --access-key -` -) -var setConfig = cliobjects.SetConfig{} - -// configCmd represents the config command -var setCmd = &cobra.Command{ - Use: "set", - Short: "Set configurations and other data", - Long: ``, - Example: setConfigExample, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -// configCmd represents the config command -var setConfigCmd = &cobra.Command{ - Use: "config", - Short: "Set cached configurations", - Long: ``, - Example: setConfigExample, - Run: func(cmd *cobra.Command, args []string) { - if err := clihandler.CliSetConfig(&setConfig); err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - }, -} - -func init() { - - setConfigCmd.PersistentFlags().StringVarP(&setConfig.Account, "account", "", "", "Set Armo account ID") - setConfigCmd.PersistentFlags().StringVarP(&setConfig.ClientID, "client-id", "", "", "Set Armo client ID") - setConfigCmd.PersistentFlags().StringVarP(&setConfig.AccessKey, "access-key", "", "", "Set Armo access key") - - rootCmd.AddCommand(setCmd) - setCmd.AddCommand(setConfigCmd) - -} diff --git a/clihandler/cmd/view.go b/clihandler/cmd/view.go deleted file mode 100644 index 77cbdf2c..00000000 --- a/clihandler/cmd/view.go +++ /dev/null @@ -1,36 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/armosec/kubescape/clihandler" - "github.com/spf13/cobra" -) - -// configCmd represents the config command -var viewCmd = &cobra.Command{ - Use: "view", - Short: "View configurations and other data", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -// configCmd represents the config command -var viewConfigCmd = &cobra.Command{ - Use: "config", - Short: "View cached configurations", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - if err := clihandler.CliView(); err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - }, -} - -func init() { - rootCmd.AddCommand(viewCmd) - viewCmd.AddCommand(viewConfigCmd) -} diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 81d7ddf5..c3ce60c7 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -11,7 +11,6 @@ import ( // printerv2 "github.com/armosec/kubescape/resultshandling/printer/v2" - "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/clihandler/cliinterfaces" @@ -158,19 +157,9 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { } func Scan(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) error { - policyNotification := &reporthandling.PolicyNotification{ - NotificationType: reporthandling.TypeExecPostureScan, - Rules: scanInfo.PolicyIdentifier, - Designators: armotypes.PortalDesignator{}, - } - switch policyNotification.NotificationType { - case reporthandling.TypeExecPostureScan: - if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil { - return err - } - - default: - return fmt.Errorf("notification type '%s' Unknown", policyNotification.NotificationType) + policyNotification := &reporthandling.PolicyNotification{Rules: scanInfo.PolicyIdentifier} + if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil { + return err } return nil } diff --git a/go.mod b/go.mod index 9c71297f..d226c90b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.41 github.com/armosec/k8s-interface v0.0.56 - github.com/armosec/opa-utils v0.0.99 + github.com/armosec/opa-utils v0.0.105 github.com/armosec/rbac-utils v0.0.12 github.com/armosec/utils-go v0.0.3 github.com/armosec/utils-k8s-go v0.0.1 diff --git a/go.sum b/go.sum index c2c78393..0a6a0e4f 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2 github.com/armosec/k8s-interface v0.0.56 h1:7dOgc3qZaI7ReLRZcJa2JZKk0rliyYi05l1vuHc6gcE= github.com/armosec/k8s-interface v0.0.56/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c= -github.com/armosec/opa-utils v0.0.99 h1:ZuoIPg6vbgO4J09xJZDO/yIRD59odwmK2Bm55uTvkU8= -github.com/armosec/opa-utils v0.0.99/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= +github.com/armosec/opa-utils v0.0.105 h1:HQdP2LpFJtBoJpsfygh4IDXLLukIkQEuETOUePF/Zas= +github.com/armosec/opa-utils v0.0.105/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.12 h1:uJpMGDyLAX129PrKHp6NPNB6lVRhE0OZIwV6ywzSDrs= github.com/armosec/rbac-utils v0.0.12/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/registryadaptors/README.md b/registryadaptors/README.md index e57330dc..ebb90565 100644 --- a/registryadaptors/README.md +++ b/registryadaptors/README.md @@ -11,11 +11,14 @@ For these controls to work properly, it is necasery to 2. Click Profile(top right icon)->"User Management"->"API Tokens" and Generate a token 3. Copy the clientID and accessKey and run: ``` -kubescape set config --access-key <> --client-id <> +kubescape config set clientID <> +``` +``` +kubescape config set accessKey <> ``` 4. Confirm the keys are set ``` -kubescape view config +kubescape config view ``` Expecting: ``` @@ -25,7 +28,7 @@ Expecting: "accessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } ``` -> If you are missing the `accountID` field, set it by running `kubescape set config --account <>` +> If you are missing the `accountID` field, set it by running `kubescape config set accountID <>` For CICD, set environments variables as following: ``` From e7a0755c25494fbfa41a9ed1e845c76a7390d08c Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 3 Feb 2022 10:32:00 +0200 Subject: [PATCH 22/22] report ClusterAPIServerInfo --- policyhandler/handlenotification.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyhandler/handlenotification.go b/policyhandler/handlenotification.go index be7cf0ab..3d05857a 100644 --- a/policyhandler/handlenotification.go +++ b/policyhandler/handlenotification.go @@ -50,7 +50,7 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *repo func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error { - opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo() + opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo() resourcesMap, allResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, ¬ification.Designators) if err != nil { return err