Compare commits

...

252 Commits

Author SHA1 Message Date
Matthias Bertschy
60d7276de3 Merge pull request #1901 from kubescape/copilot/fix-cis-framework-metrics-export
Fix CIS framework metrics not exported to Prometheus /v1/metrics endpoint
2025-12-05 09:45:06 +01:00
copilot-swe-agent[bot]
c05427ff38 Remove KS_METRICS_FRAMEWORKS environment variable support
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-12-05 09:43:49 +01:00
Matthias Bertschy
3e245da02b Merge pull request #1902 from kubescape/copilot/fix-air-gapped-configuration
Fix air-gapped mode network access attempts
2025-12-05 08:44:50 +01:00
copilot-swe-agent[bot]
cc7aae470f Fix typo in comment: fom -> from
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-04 13:47:52 +00:00
copilot-swe-agent[bot]
8d59a6074e Add frameworks query parameter to /v1/metrics endpoint
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-04 13:45:55 +00:00
copilot-swe-agent[bot]
1f7dd6e5f5 Fix incorrect comment about default output format
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-04 13:31:52 +00:00
copilot-swe-agent[bot]
bf5ca3c1f0 Add KS_METRICS_FRAMEWORKS env var to allow selecting specific frameworks
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-04 13:30:11 +00:00
copilot-swe-agent[bot]
a8574c61ea Fix: properly handle nil downloadReleasedPolicy in getters
Ensure getter functions handle nil downloadReleasedPolicy correctly by creating a new instance when needed, maintaining backward compatibility with existing code while supporting air-gapped mode.

Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 21:19:39 +00:00
copilot-swe-agent[bot]
6f9c0ae85f Address code review feedback
- Fix string field checks in isAirGappedMode (use != "" instead of len() > 0)
- Use centralized isAirGappedMode function in getResourceHandler
- Improve comment clarity to reflect all air-gapped conditions

Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 21:14:04 +00:00
copilot-swe-agent[bot]
be2c74e48a Add test for isAirGappedMode function
Add comprehensive tests to verify air-gapped mode detection logic.

Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 21:11:18 +00:00
copilot-swe-agent[bot]
68da73855f Refactor: Extract isAirGappedMode helper function
Extract complex boolean condition into a dedicated helper function for better readability and maintainability.

Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 21:06:52 +00:00
copilot-swe-agent[bot]
5b3f2d0ff9 Fix air-gapped mode to prevent network access
- Skip version check when --keep-local flag is set
- Skip DownloadReleasedPolicy initialization when in air-gapped mode
- Skip KSCloudAPIConnector initialization when --keep-local is set

Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 21:03:01 +00:00
copilot-swe-agent[bot]
02637c7a8e Initial plan 2025-12-03 20:50:40 +00:00
Matthias Bertschy
7d5b374f9d Merge pull request #1900 from kubescape/copilot/fix-severity-missing-json
Add severity field to controls in JSON output
2025-12-03 21:44:49 +01:00
copilot-swe-agent[bot]
1dd6d7a1b3 Address code review feedback: nil check and trailing whitespace
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 16:46:11 +00:00
copilot-swe-agent[bot]
6b80b85555 Add tests for results enrichment with severity
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 16:42:03 +00:00
copilot-swe-agent[bot]
d88bc067e2 Add severity to controls in results section as well
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 16:40:48 +00:00
copilot-swe-agent[bot]
ba78527c80 Enable ScanAll for prometheus metrics to include all frameworks including CIS
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 16:32:43 +00:00
copilot-swe-agent[bot]
4c8692bf8c Remove test output files and update gitignore 2025-12-03 16:32:29 +00:00
copilot-swe-agent[bot]
742e3bb67f Add severity field to controls in JSON output
Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com>
2025-12-03 16:31:57 +00:00
copilot-swe-agent[bot]
a39f36c9fb Initial plan 2025-12-03 16:18:31 +00:00
copilot-swe-agent[bot]
9bc29032e1 Initial plan 2025-12-03 16:12:04 +00:00
Matthias Bertschy
a4a290a3ce Merge pull request #1898 from kubescape/dependabot/go_modules/github.com/cilium/cilium-1.16.17
Bump github.com/cilium/cilium from 1.16.9 to 1.16.17
2025-12-02 17:12:06 +01:00
dependabot[bot]
379a3fbc27 Bump github.com/cilium/cilium from 1.16.9 to 1.16.17
Bumps [github.com/cilium/cilium](https://github.com/cilium/cilium) from 1.16.9 to 1.16.17.
- [Release notes](https://github.com/cilium/cilium/releases)
- [Changelog](https://github.com/cilium/cilium/blob/1.16.17/CHANGELOG.md)
- [Commits](https://github.com/cilium/cilium/compare/1.16.9...1.16.17)

---
updated-dependencies:
- dependency-name: github.com/cilium/cilium
  dependency-version: 1.16.17
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-12-02 12:34:17 +01:00
Matthias Bertschy
a46098c034 Merge pull request #1896 from kubescape/summaries
always save WorkloadConfigurationScanResultSummaries
2025-12-01 08:05:57 +01:00
Matthias Bertschy
440f39ba3e Merge pull request #1897 from kubescape/docs
Revamp documentation
2025-11-30 11:47:23 +01:00
Matthias Bertschy
b6a4e282f9 Revamp documentation and reduce host sensor workers
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-30 11:47:00 +01:00
Matthias Bertschy
8deff34d12 always save WorkloadConfigurationScanResultSummaries
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-28 16:45:23 +01:00
Matthias Bertschy
acc9b54b2b Merge pull request #1895 from pfarikrispy/pfarikrispy-patch-1
update from Debian 12 to 13 when building container images
2025-11-26 14:52:53 +01:00
Christian Peper
1ffa29fbaa feat(security): update golang to debian trixie
build in the same env as the final image

Signed-off-by: Christian Peper <dreamszz@gmail.com>
2025-11-26 14:34:49 +01:00
Christian Peper
2ae30a8162 feat(security): update to Debian 13
Signed-off-by: Christian Peper <dreamszz@gmail.com>
2025-11-26 14:32:41 +01:00
Christian Peper
0ca5378c6b feat(security): update to Debian 13
use debian 13 as a base image

Signed-off-by: Christian Peper <dreamszz@gmail.com>
2025-11-26 14:30:46 +01:00
Matthias Bertschy
f51a1281f7 Merge pull request #1892 from kubescape/dependabot/go_modules/httphandler/golang.org/x/crypto-0.45.0
Bump golang.org/x/crypto from 0.41.0 to 0.45.0 in /httphandler
2025-11-21 20:59:38 +01:00
dependabot[bot]
5469d8bc04 Bump golang.org/x/crypto from 0.41.0 to 0.45.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.41.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.41.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 17:10:28 +01:00
Matthias Bertschy
bd7c0c580e fix go mod tidy
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-11 17:52:55 +01:00
Matthias Bertschy
154fec1385 Allow artifact-metadata read in release workflow
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-11 16:14:32 +01:00
Matthias Bertschy
5c2275e32a Allow artifact-metadata read in release workflow
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-11 16:13:16 +01:00
Matthias Bertschy
2da4736201 fix workflow permissions
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-11 16:10:15 +01:00
Matthias Bertschy
aefafeae6f Merge pull request #1890 from kubescape/dependabot/go_modules/github.com/opencontainers/selinux-1.13.0
Bump github.com/opencontainers/selinux from 1.12.0 to 1.13.0
2025-11-11 14:10:59 +01:00
dependabot[bot]
1772b38b8c Bump github.com/opencontainers/selinux from 1.12.0 to 1.13.0
Bumps [github.com/opencontainers/selinux](https://github.com/opencontainers/selinux) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/opencontainers/selinux/releases)
- [Commits](https://github.com/opencontainers/selinux/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/selinux
  dependency-version: 1.13.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-11 12:44:20 +00:00
Matthias Bertschy
c844f42208 Merge pull request #1889 from kubescape/dependabot/go_modules/github.com/containerd/containerd-1.7.29
Bump github.com/containerd/containerd from 1.7.28 to 1.7.29
2025-11-07 08:44:37 +01:00
dependabot[bot]
b86d051998 Bump github.com/containerd/containerd from 1.7.28 to 1.7.29
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.28 to 1.7.29.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.7.28...v1.7.29)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-version: 1.7.29
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-07 08:32:59 +01:00
Matthias Bertschy
aaa8d1ed35 Merge pull request #1888 from kubescape/dependabot/go_modules/github.com/containerd/containerd/v2-2.0.7
Bump github.com/containerd/containerd/v2 from 2.0.5 to 2.0.7
2025-11-07 08:16:43 +01:00
dependabot[bot]
441d16aa08 Bump github.com/containerd/containerd/v2 from 2.0.5 to 2.0.7
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.5 to 2.0.7.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.5...v2.0.7)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.0.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-07 08:04:26 +01:00
Matthias Bertschy
b33f1c8cc7 Merge pull request #1887 from Mujib-Ahasan/fix-print-sarif
fix: --format sarif logs as expected
2025-11-05 18:17:31 +01:00
Mujib Ahasan
4929af510e fix: --format sarif logs as expected
Signed-off-by: Mujib Ahasan <ahasanmujib8@gmail.com>
2025-11-04 20:30:10 +05:30
mandronic
f28bb11c55 removed 'procMount: Unmasked' from host-scanner daemonset definition (refs kubescape/helm-charts#711) (#1886)
Signed-off-by: Mihail Andronic <104365774+mandronic@users.noreply.github.com>
2025-11-03 13:40:14 +02:00
Matthias Bertschy
8bff4a02e1 Merge pull request #1884 from Mujib-Ahasan/fix-url
fixed "404" URL issue for command $kubescape scan.
2025-11-03 11:11:55 +01:00
Matthias Bertschy
33d1e018ec fix: update documentation links to include 'controls' path
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-11-03 07:47:37 +01:00
Mujib Ahasan
0c74599314 Test file updated
Signed-off-by: Mujib Ahasan <ahasanmujib8@gmail.com>
2025-10-30 01:14:25 +05:30
Mujib Ahasan
c23b85cc84 fixed 404 url issue in kubescape scan
Signed-off-by: Mujib Ahasan <ahasanmujib8@gmail.com>
2025-10-25 17:39:28 +05:30
Matthias Bertschy
aab10d14a2 Merge pull request #1881 from kubescape/dependabot/go_modules/github.com/nwaples/rardecode/v2-2.2.0
Bump github.com/nwaples/rardecode/v2 from 2.1.1 to 2.2.0
2025-10-21 09:45:29 +02:00
dependabot[bot]
2fcbe54e4e Bump github.com/nwaples/rardecode/v2 from 2.1.1 to 2.2.0
Bumps [github.com/nwaples/rardecode/v2](https://github.com/nwaples/rardecode) from 2.1.1 to 2.2.0.
- [Commits](https://github.com/nwaples/rardecode/compare/v2.1.1...v2.2.0)

---
updated-dependencies:
- dependency-name: github.com/nwaples/rardecode/v2
  dependency-version: 2.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 09:31:33 +02:00
Matthias Bertschy
078d154ab8 chore: update runners to use ubuntu-large for improved performance
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-10-15 10:12:24 +02:00
Matthias Bertschy
cc9dcf827e Revert "use ubuntu-latest-16-cores runner for docker build"
This reverts commit 76943d05fb.
2025-10-15 10:01:39 +02:00
Matthias Bertschy
76943d05fb use ubuntu-latest-16-cores runner for docker build
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-10-13 10:23:07 +02:00
Matthias Bertschy
621ac111cb Merge pull request #1880 from kubescape/hostagent-block
fix: improve error handling in hostscanner pod validation
2025-10-12 11:52:16 +02:00
Matthias Bertschy
3f80bce811 fix: improve error handling in hostscanner pod validation
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-10-10 16:13:43 +02:00
Matthias Bertschy
cc6895fc50 chore: update syft action to use latest version
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-10-10 15:00:35 +02:00
Matthias Bertschy
7d9d8e4b59 Merge pull request #1879 from kubescape/fix-post-release
fix: post release action does not take tag from GITHUB_REF env var
2025-10-08 15:57:44 +02:00
Amir Malka
f8d4bf515d fix: post release action does not take tag from GITHUB_REF env var
Signed-off-by: Amir Malka <amirm@armosec.io>
2025-10-08 16:18:35 +03:00
Matthias Bertschy
774ebe4a5f bump python to 3.9
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-29 17:44:13 +02:00
Matthias Bertschy
45a07a8046 Merge pull request #1877 from kubescape/grype
migrate to grype v0.99.1
2025-09-17 11:35:30 +02:00
Matthias Bertschy
ff96edae4d use grype v0.99.1
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-17 10:11:19 +02:00
Matthias Bertschy
34b82cad27 Merge pull request #1875 from kkrypt0nn/master
fix: Don't run scan in installation script
2025-09-16 15:13:31 +02:00
Matthias Bertschy
1a4c979ab8 update golangci-lint config
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-16 13:54:23 +02:00
Matthias Bertschy
3481af4a5b bump golangci-lint action
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-16 11:48:02 +02:00
Ben Hirschberg
71dc7a702c Merge pull request #1876 from cx-anjali-deore/fix/policy-binding-param
Fixed issue #1800 : Added parameterNotFoundAction in spec.paramRef while creating policy binding
2025-09-15 10:17:52 +03:00
anjali-deore
6d92389285 Fixed issue 1800
Signed-off-by: anjali-deore <200181980+cx-anjali-deore@users.noreply.github.com>
2025-09-13 18:06:37 +05:30
Krypton
bd0be45c0b fix: Don't run scan in installation script
Signed-off-by: Krypton <root@krypton.ninja>
2025-09-12 19:54:31 +02:00
Amir Malka
7ebf078d0c fix post-release workflow (#1873)
Signed-off-by: Amir Malka <amirm@armosec.io>
2025-09-02 19:51:20 +03:00
Matthias Bertschy
1bd729cf83 fix go tidy
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-02 14:45:12 +02:00
Matthias Bertschy
88b9b22bca Merge pull request #1857 from aadarsh-nagrath/default-matchers
feat: add default matchers option to image scanning
2025-09-02 14:25:29 +02:00
Matthias Bertschy
182162d521 gofmt
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-02 13:51:45 +02:00
Matthias Bertschy
1c02191bb1 Merge pull request #1866 from kubescape/tablewriter
replace olekukonko/tablewriter with jedib0t/go-pretty
2025-09-01 17:38:18 +02:00
Matthias Bertschy
ca66ccb33d replace olekukonko/tablewriter with jedib0t/go-pretty
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-01 16:38:42 +02:00
Matthias Bertschy
07eda20b88 Merge pull request #1869 from htsr/fix-imagescan-use-all-targets-exceptions
fix(imagescan): use all targets in exceptions
2025-09-01 16:29:04 +02:00
Matthias Bertschy
108c84d97d Merge pull request #1867 from cx-anjali-deore/feature/ListcontainerName
Issue 1817 fix: Show container name in Assisted remediation
2025-09-01 16:28:51 +02:00
Matthias Bertschy
35e7fa2b94 fix imports
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-09-01 15:49:31 +02:00
Hugo Thiessard
abb7917b29 fix(imagescan): use all targets in exceptions
Previously, kubescape only used the first target in scan image exceptions.
Added a test to verify the fix.

Signed-off-by: Hugo Thiessard <htsr@pm.me>
2025-09-01 15:05:00 +02:00
Matthias Bertschy
31ba56a0cf Merge pull request #1868 from kubescape/dependabot/go_modules/github.com/ulikunitz/xz-0.5.14
Bump github.com/ulikunitz/xz from 0.5.12 to 0.5.14
2025-09-01 09:43:12 +02:00
dependabot[bot]
b3efe4d003 Bump github.com/ulikunitz/xz from 0.5.12 to 0.5.14
Bumps [github.com/ulikunitz/xz](https://github.com/ulikunitz/xz) from 0.5.12 to 0.5.14.
- [Commits](https://github.com/ulikunitz/xz/compare/v0.5.12...v0.5.14)

---
updated-dependencies:
- dependency-name: github.com/ulikunitz/xz
  dependency-version: 0.5.14
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 05:35:18 +00:00
anjali-deore
5faade2b66 Fixed test cases
Signed-off-by: anjali-deore <200181980+cx-anjali-deore@users.noreply.github.com>
2025-08-31 17:37:32 +05:30
Matthias Bertschy
79207f66be don't read services from configmap, use file
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-08-29 15:21:25 +02:00
anjali-deore
af39f9a7ef fix removed space
Signed-off-by: anjali-deore <200181980+cx-anjali-deore@users.noreply.github.com>
2025-08-29 15:31:35 +05:30
anjali-deore
482b7c1f67 fix 1817 ,code cleanup
Signed-off-by: anjali-deore <200181980+cx-anjali-deore@users.noreply.github.com>
2025-08-29 15:20:00 +05:30
anjali-deore
82e2fd0be2 fix issue 1817,Added Container Name in control scan output
Signed-off-by: anjali-deore <200181980+cx-anjali-deore@users.noreply.github.com>
2025-08-29 15:20:00 +05:30
Matthias Bertschy
6eec751027 Merge pull request #1864 from kubescape/dependabot/go_modules/httphandler/github.com/hashicorp/go-getter-1.7.9
Bump github.com/hashicorp/go-getter from 1.7.8 to 1.7.9 in /httphandler
2025-08-21 17:48:44 +02:00
dependabot[bot]
4a6480c8b4 Bump github.com/hashicorp/go-getter from 1.7.8 to 1.7.9
Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.7.8 to 1.7.9.
- [Release notes](https://github.com/hashicorp/go-getter/releases)
- [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml)
- [Commits](https://github.com/hashicorp/go-getter/compare/v1.7.8...v1.7.9)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-getter
  dependency-version: 1.7.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-21 17:14:35 +02:00
Matthias Bertschy
a51bfa4c3e Merge pull request #1863 from kubescape/new_fix/issue_1284
Issue 1284 fix: new approach implemented
2025-08-19 16:02:53 +02:00
Yehudah Tor
2a48af3c17 new approach of fix implemented
Signed-off-by: Yehudah Tor <yehudahtor@gmail.com>
2025-08-19 12:22:15 +03:00
Matthias Bertschy
ffeb4577e3 refactor output formatting in prettyprinter and related files
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-08-18 12:15:54 +02:00
Matthias Bertschy
b5c7422355 Merge pull request #1862 from kubescape/threshold
bump helm.sh/helm/v3 to 3.18.5
2025-08-18 11:07:42 +02:00
Matthias Bertschy
e41b5d77a0 bump helm.sh/helm/v3 to 3.18.5
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-08-18 10:28:34 +02:00
Matthias Bertschy
5afaae8847 Merge pull request #1860 from kubescape/threshold
return error on image when severity threshold exceeded
2025-08-18 10:22:19 +02:00
Matthias Bertschy
011fc0689d return error on image when severity threshold exceeded
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-08-14 14:38:59 +02:00
aadarsh-nagrath
db30020c95 feat: add default matchers option to image scanning
hey! added the default matchers option for image scanning as requested in #1838. now you can choose between stock matchers and CPE matchers when scanning images.

what's new:
- added --use-default-matchers flag to scan/image/patch commands
- true = stock matchers (default behavior)
- false = CPE matchers (more precise)

usage:
# use CPE matchers for more precise detection
kubescape scan image nginx:latest --use-default-matchers=false

# or in scan command
kubescape scan --scan-images --use-default-matchers=false

everything's backward compatible - existing code works exactly the same. just added the new option for folks who want more control over their vulnerability detection.

fixes #1838

Signed-off-by: aadarsh-nagrath <anagrath1@gmail.com>
2025-08-06 21:48:40 +05:30
Matthias Bertschy
c5341a356b fix prettyprinter test results
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-28 15:06:09 +02:00
Matthias Bertschy
85a7f57373 Merge pull request #1854 from kubescape/fixurls
fix docs URL in tests
2025-07-28 14:55:14 +02:00
Matthias Bertschy
cd9ebdf08f fix workflow permissions
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-28 14:44:36 +02:00
Matthias Bertschy
bc602a78ab fix docs URL in tests
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-28 14:41:25 +02:00
Matthias Bertschy
a2361fd155 Merge pull request #1853 from kubescape/dependabot/go_modules/github.com/containerd/containerd/v2-2.0.5
Bump github.com/containerd/containerd/v2 from 2.0.4 to 2.0.5
2025-07-28 14:26:07 +02:00
Matthias Bertschy
aa8d41fc2e fix workflow permissions
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-28 14:18:31 +02:00
dependabot[bot]
5bd4beb41f Bump github.com/containerd/containerd/v2 from 2.0.4 to 2.0.5
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.4...v2.0.5)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 14:14:59 +02:00
Matthias Bertschy
dbf21dee37 Merge pull request #1852 from kubescape/dependabot/go_modules/helm.sh/helm/v3-3.17.4
Bump helm.sh/helm/v3 from 3.17.3 to 3.17.4
2025-07-28 14:13:32 +02:00
dependabot[bot]
be49d9b7be Bump helm.sh/helm/v3 from 3.17.3 to 3.17.4
Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.17.3 to 3.17.4.
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.17.3...v3.17.4)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.17.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 14:02:05 +02:00
Matthias Bertschy
7a5699fba3 Merge pull request #1851 from kubescape/dependabot/go_modules/github.com/cloudflare/circl-1.6.1
Bump github.com/cloudflare/circl from 1.5.0 to 1.6.1
2025-07-28 13:48:28 +02:00
dependabot[bot]
1f8afecea8 Bump github.com/cloudflare/circl from 1.5.0 to 1.6.1
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.5.0 to 1.6.1.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.5.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 13:33:52 +02:00
Matthias Bertschy
3ebb1d749e Merge pull request #1850 from kubescape/dependabot/go_modules/httphandler/github.com/go-viper/mapstructure/v2-2.3.0
Bump github.com/go-viper/mapstructure/v2 from 2.2.1 to 2.3.0 in /httphandler
2025-07-28 13:32:17 +02:00
dependabot[bot]
f80c9d947d Bump github.com/go-viper/mapstructure/v2 in /httphandler
Bumps [github.com/go-viper/mapstructure/v2](https://github.com/go-viper/mapstructure) from 2.2.1 to 2.3.0.
- [Release notes](https://github.com/go-viper/mapstructure/releases)
- [Changelog](https://github.com/go-viper/mapstructure/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-viper/mapstructure/compare/v2.2.1...v2.3.0)

---
updated-dependencies:
- dependency-name: github.com/go-viper/mapstructure/v2
  dependency-version: 2.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 13:18:52 +02:00
Matthias Bertschy
03b76ff4aa Merge pull request #1833 from kubescape/dependabot/go_modules/github.com/open-policy-agent/opa-1.4.0
Bump github.com/open-policy-agent/opa from 1.3.0 to 1.4.0
2025-07-28 13:06:52 +02:00
dependabot[bot]
01531b6276 Bump github.com/open-policy-agent/opa from 1.3.0 to 1.4.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-version: 1.4.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 12:20:18 +02:00
Ben Hirschberg
aedfe1c4c0 Merge pull request #1849 from kubescape/fix/addon-urls
added urls hub.armo --> kubescape.io
2025-07-27 14:19:34 +03:00
Yehudah Tor
d2bedc1d2b added urls
Signed-off-by: Yehudah Tor <yehudahtor@gmail.com>
2025-07-27 13:50:47 +03:00
Ben Hirschberg
35288e7b85 Merge pull request #1846 from kubescape/fix/update-links
Fix/update links
2025-07-27 10:01:27 +03:00
Matthias Bertschy
cd046fa695 fix buildnumber test
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-26 11:08:43 +02:00
Matthias Bertschy
407b8be08f Merge pull request #1848 from kubescape/fixgrype
close grype DB at the very end of processing
2025-07-25 11:23:56 +02:00
Matthias Bertschy
b211fe9148 Merge pull request #1847 from kubescape/fixversion
do not fail version if update info cannot be fetched
2025-07-25 11:23:24 +02:00
Matthias Bertschy
525e51d68e close grype DB at the very end of processing
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-25 09:50:10 +02:00
Matthias Bertschy
daabd6c81a do not fail version if update info cannot be fetched
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-07-25 08:48:27 +02:00
Yehudah Tor
5b351d5eec Done URL ref's before problematic ones
Signed-off-by: Yehudah Tor <yehudahtor@gmail.com>
2025-07-24 12:11:31 +03:00
Yehudah Tor
a5b607ae2e All controls updated
Signed-off-by: Yehudah Tor <yehudahtor@gmail.com>
2025-07-24 11:12:18 +03:00
Yehudah Tor
fec51b00ba getting started kubescape.io control links updated
Signed-off-by: Yehudah Tor <yehudahtor@gmail.com>
2025-07-23 15:16:41 +03:00
Amir Malka
4f9809eec1 fix: control-plane node taints check (#1843)
Signed-off-by: Amir Malka <amirm@armosec.io>
2025-07-15 11:06:09 +03:00
Matthias Bertschy
c0c25c3430 Merge pull request #1841 from kubescape/submit
check scanInfo.Submit in HandleResults to not submit by default
2025-06-30 09:09:05 +02:00
Matthias Bertschy
6ed3e408be check scanInfo.Submit in HandleResults to not submit by default
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-06-30 08:16:41 +02:00
Matthias Bertschy
6042818a71 use go 1.24
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-06-24 16:31:45 +02:00
Matthias Bertschy
b37c20aed9 fix workflow permissions
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-06-24 16:01:11 +02:00
Matthias Bertschy
3de8204c43 fix workflow permissions
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-06-24 15:59:27 +02:00
Ben Hirschberg
d5bd3708b8 Merge pull request #1836 from slashben/feature/mcpserver-v1
Feature/mcpserver v1
2025-06-24 16:29:16 +03:00
Ben
2bd686131e Incorporating review
Signed-off-by: Ben <ben@armosec.io>
2025-06-10 14:32:26 +03:00
Ben
1ea4e0c304 Add configuration scan tools and resource handling
Implemented two new tools: 'list_configuration_security_scan_manifests' for listing available configuration scan manifests and 'get_configuration_security_scan_manifest' for retrieving details of a specific manifest. Added a resource template for configuration manifests and integrated these tools into the mcpserver.

Signed-off-by: Ben <ben@armosec.io>
2025-06-09 09:11:28 +03:00
Matthias Bertschy
b3251306d0 Merge pull request #1837 from ian-fox/master
Make a second sigint terminate kubescape immediately
2025-06-03 12:54:02 +02:00
Ian Fox
91ecdaba4e Make a second sigint terminate immediately
Right now, sending a sigint will start a graceful shutdown, which can take quite a while.
By calling stop() in the signal handler it unregisters the handler so that a subsequent
sigint received during a graceful shutdown will kill the process immediately.

Signed-off-by: Ian Fox <code@whatthefox.dev>
2025-06-02 19:12:45 +02:00
Matthias Bertschy
fa05dcd00d go.mod massaging
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-05-23 16:02:15 +02:00
Ben
1c2c928732 Failing build
Signed-off-by: Ben <ben@armosec.io>
2025-05-23 15:13:22 +03:00
Ben
69ac490006 Failing build
Signed-off-by: Ben <ben@armosec.io>
2025-05-23 15:13:12 +03:00
Matthias Bertschy
b67b9f3af2 Merge pull request #1835 from pixel365/cancel
Ensure context cancellation on command execution failure
2025-05-16 21:20:16 +02:00
Ruslan Semagin
628ed4a374 Ensure context cancellation on command execution failure
Signed-off-by: Ruslan Semagin <pixel.365.24@gmail.com>
2025-05-16 10:11:06 +03:00
Matthias Bertschy
a6fe34b466 add workflow_dispatch to pr scanner
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-05-02 16:36:27 +02:00
Matthias Bertschy
2e9406d96a Merge pull request #1828 from rash1411/patch-1
Update dir.go
2025-04-29 21:44:09 +02:00
Matthias Bertschy
6b1bf07f7f Merge pull request #1827 from shangchengbabaiban/master
fix: Fix stdin restoration in TestUserConfirmed
2025-04-29 21:40:30 +02:00
Matthias Bertschy
318c2c7ae6 Merge pull request #1829 from kubescape/dependabot/go_modules/golang.org/x/net-0.38.0
Bump golang.org/x/net from 0.37.0 to 0.38.0
2025-04-28 14:19:13 +02:00
dependabot[bot]
45f60b6fe0 Bump golang.org/x/net from 0.37.0 to 0.38.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 14:03:44 +02:00
rash1411
20557bc721 Update dir.go
Fix: Correct runtime.Caller usage and handle error in CurrentDir()

- Changed runtime.Caller(1) to runtime.Caller(0) to correctly fetch the current file's directory.
- Added proper check for the 'ok' value returned by runtime.Caller.
- Improved robustness by panicking if runtime information retrieval fails.


Signed-off-by: rash1411 <107741585+rash1411@users.noreply.github.com>
2025-04-27 01:29:04 +05:30
shangchengbabaiban
d8bfb27bc3 fix: Fix stdin restoration in TestUserConfirmed
Signed-off-by: shangchengbabaiban <shuang.cui@live.cn>
2025-04-19 10:51:09 +08:00
Matthias Bertschy
9776691816 Merge pull request #1824 from kubescape/dependabot/go_modules/helm.sh/helm/v3-3.17.3
Bump helm.sh/helm/v3 from 3.14.4 to 3.17.3
2025-04-15 12:41:19 +02:00
dependabot[bot]
80e419df24 Bump helm.sh/helm/v3 from 3.14.4 to 3.17.3
Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.14.4 to 3.17.3.
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.14.4...v3.17.3)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-version: 3.17.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-15 12:22:19 +02:00
Matthias Bertschy
143f831f5b Merge pull request #1822 from kubescape/mergelabels
merge labels and annotations in RetryOnConflict
2025-04-14 16:26:47 +02:00
Matthias Bertschy
a4897304e8 merge labels and annotations in RetryOnConflict
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-04-14 15:36:34 +02:00
Matthias Bertschy
80d1165e2c Merge pull request #1820 from kubescape/opav1
upgrade open-policy-agent to 1.x
2025-04-14 14:13:23 +02:00
Matthias Bertschy
31ed7d5160 upgrade open-policy-agent to 1.x
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-04-10 17:17:08 +02:00
Matthias Bertschy
d62e9ce207 Merge pull request #1814 from pixel365/tests
test: increase test coverage
2025-04-07 09:17:27 +02:00
Ruslan Semagin
3b10443ff5 fix: goimports
Signed-off-by: Ruslan Semagin <pixel.365.24@gmail.com>
2025-04-01 12:26:15 +03:00
Ruslan Semagin
218e3914b1 test: increase test coverage
Signed-off-by: Ruslan Semagin <pixel.365.24@gmail.com>
2025-04-01 10:12:35 +03:00
Matthias Bertschy
2b07fbe782 Merge pull request #1813 from kubescape/merge
check for nil map in merge methods
2025-03-31 11:18:05 +02:00
Matthias Bertschy
0655d0496b check for nil map in merge methods
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-28 12:10:38 +01:00
Matthias Bertschy
02bf31fbc9 Merge pull request #1812 from kubescape/dependabot/go_modules/github.com/golang-jwt/jwt/v5-5.2.2
Bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2
2025-03-28 11:03:59 +01:00
dependabot[bot]
4d8a3f51e3 Bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2
Bumps [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v5.2.1...v5.2.2)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-28 10:30:02 +01:00
Matthias Bertschy
43d29f7b8b Merge pull request #1811 from kubescape/prerequisites
feat: enhance prerequisites UI with new review-values and improved pr…
2025-03-24 12:44:42 +01:00
David Alima
ee0d4cba98 feat: enhance prerequisites UI with new review-values and improved prerequisites report
Signed-off-by: David Alima <davida@armosec.io>
2025-03-24 13:10:58 +02:00
Matthias Bertschy
d860c0234a Merge pull request #1809 from kubescape/dependabot/go_modules/golang.org/x/net-0.36.0
Bump golang.org/x/net from 0.33.0 to 0.36.0
2025-03-20 13:35:08 +01:00
Matthias Bertschy
f13ded61bf Bump github.com/containerd/containerd to 1.7.27
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-20 13:05:17 +01:00
dependabot[bot]
fe8fc700f4 Bump golang.org/x/net from 0.33.0 to 0.36.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-20 09:59:07 +00:00
Matthias Bertschy
47b670637b Merge pull request #1808 from kubescape/prereq
updating prerequisites to support custom kubeconfig
2025-03-19 09:37:57 +01:00
Matthias Bertschy
97c83a652b updating prerequisites to support custom kubeconfig
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-18 10:02:17 +01:00
Matthias Bertschy
a089831720 upload everything in dist
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-10 10:32:00 +01:00
Matthias Bertschy
e4f1720a0c bump goreleaser action in the other file too
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-10 09:52:42 +01:00
Matthias Bertschy
233eb2134c bump goreleaser action
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-10 09:19:48 +01:00
Matthias Bertschy
06da926455 Merge pull request #1806 from kubescape/goreleaser
generate checksums for all artifacts
2025-03-10 08:56:35 +01:00
Amit Schendel
37f6193fe0 Merge pull request #1807 from kubescape/Bump-license-date
Update README.md
2025-03-09 17:08:09 +02:00
Amit Schendel
27ac036b7d Update README.md
Signed-off-by: Amit Schendel <58078857+amitschendel@users.noreply.github.com>
2025-03-09 17:04:32 +02:00
Matthias Bertschy
321d335b39 generate checksums for all artifacts
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-09 12:37:21 +01:00
Matthias Bertschy
91b7d8fc2b Merge pull request #1803 from kubescape/dependabot/go_modules/github.com/go-jose/go-jose/v3-3.0.4
Bump github.com/go-jose/go-jose/v3 from 3.0.3 to 3.0.4
2025-03-04 10:38:44 +01:00
dependabot[bot]
2b28911db0 Bump github.com/go-jose/go-jose/v3 from 3.0.3 to 3.0.4
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.3...v3.0.4)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-03-04 09:28:14 +01:00
Amir Malka
667e5e8258 fix score calculation for framework with all controls in status irrelevant (#1802)
Signed-off-by: Amir Malka <amirm@armosec.io>
2025-03-03 15:20:14 +02:00
Matthias Bertschy
728b341048 Merge pull request #1797 from kubescape/prereq
update prerequisites command
2025-02-26 11:51:42 +01:00
Matthias Bertschy
75b295d579 update prerequisites command
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-26 11:20:33 +01:00
Matthias Bertschy
75298eabf2 Merge pull request #1796 from kubescape/dependabot/go_modules/httphandler/github.com/go-jose/go-jose/v4-4.0.5
Bump github.com/go-jose/go-jose/v4 from 4.0.2 to 4.0.5 in /httphandler
2025-02-25 08:45:31 +01:00
dependabot[bot]
2458f2ceb9 Bump github.com/go-jose/go-jose/v4 from 4.0.2 to 4.0.5
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.0.2 to 4.0.5.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.0.2...v4.0.5)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-25 07:58:42 +01:00
Amir Malka
f57948ad97 deprecate control name in exception policies (#1795)
* deprecate control name in posture exception policies

Signed-off-by: Amir Malka <amirm@armosec.io>

* deprecate control name in posture exception policies

Signed-off-by: Amir Malka <amirm@armosec.io>

* deprecate control name in posture exception policies

Signed-off-by: Amir Malka <amirm@armosec.io>

* deprecate control name in posture exception policies

Signed-off-by: Amir Malka <amirm@armosec.io>

---------

Signed-off-by: Amir Malka <amirm@armosec.io>
2025-02-20 10:27:27 +02:00
Matthias Bertschy
d0befc5f16 Merge pull request #1792 from kubescape/colors
removing enable/disable colors, as not compatible with all loggers
2025-02-18 12:11:40 +01:00
Matthias Bertschy
5d4bd2e94e removing enable/disable colors, as not compatible with all loggers
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-18 11:38:47 +01:00
Matthias Bertschy
ae37fdc295 fix go tidy
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-14 16:19:42 +01:00
Matthias Bertschy
3dd95ff3a3 Merge pull request #1791 from kubescape/prereq
update prerequisites
2025-02-14 16:12:03 +01:00
Matthias Bertschy
daadb5b804 update prerequisites
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-14 16:10:31 +01:00
Matthias Bertschy
d250017faf Merge pull request #1790 from pixel365/err
fix: add error handling and context propagation
2025-02-12 10:56:03 +01:00
Ruslan Semagin
835bcbeb12 fix: format imports with goimports
Signed-off-by: Ruslan Semagin <pixel.365.24@gmail.com>
2025-02-12 12:15:14 +03:00
Ruslan Semagin
2e4f7c4477 fix: add error handling and context propagation
Signed-off-by: Ruslan Semagin <pixel.365.24@gmail.com>
2025-02-12 09:02:01 +03:00
Matthias Bertschy
66bf93eb0c Merge pull request #1789 from pixel365/ctx
Refactor: propagate context from main to avoid redundant context creation
2025-02-11 21:27:48 +01:00
Matthias Bertschy
3a036ed0e3 remove test resources saved by accident
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-11 21:26:26 +01:00
Ruslan Semagin
fe7dad4560 Refactor: propagate context from main to avoid redundant context creation
- Introduced a single context in main() to handle interrupt signals (os.Interrupt, syscall.SIGTERM).
- Removed repetitive context creation in the program by reusing the propagated context.
- Improved code readability and maintainability by centralizing context management.
- Ensured consistent handling of graceful shutdown across the program.

Signed-off-by: Ruslan Semagin <pixel.365.24@gmail.com>
2025-02-11 19:52:48 +03:00
Matthias Bertschy
fb36b09f3a remove mv kubescape arm64 binary step
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-11 09:49:17 +01:00
Matthias Bertschy
e71b0c75a9 fix downloaded artifacts path
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-11 08:55:43 +01:00
Matthias Bertschy
d615099ce1 try fixing download-artifacts action for release
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-11 07:42:30 +01:00
Matthias Bertschy
f265b91939 Merge pull request #1788 from kubescape/sarif
do not skip on location resolver error
2025-02-10 13:40:59 +01:00
Matthias Bertschy
825694ade1 do not skip on location resolver error
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-10 10:25:44 +01:00
Matthias Bertschy
979a30aea7 Merge pull request #1591 from Sanskarzz/scanapiexception
added support for exception rules in Scan API ( As a HTTP request)
2025-02-07 13:32:42 +01:00
Matthias Bertschy
39c4aa4faa Merge pull request #1568 from VaibhavMalik4187/feature-exceptions
Feature: Added exceptions flag in scan image cmd
2025-02-07 12:40:24 +01:00
Matthias Bertschy
475b672a7a add fixed grypeDB for tests
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-07 12:34:11 +01:00
Matthias Bertschy
815c87b532 implement reviews from DW
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-07 12:16:35 +01:00
Sanskarzz
82120f9d31 added support for exception rules
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
2025-02-07 12:15:56 +01:00
VaibhavMalik4187
0545818f82 Added tests and improvements for image exceptions
Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2025-02-07 10:26:53 +01:00
VaibhavMalik4187
046da1940c Added support for targets in image exceptions
This commit introduces the ability to specify targets in image
exceptions. Each target will have the following 4 attributes:

1. Registry
2. Organization
3. ImageName
4. ImageTag

These attributes will be used to match against the canonical image name
of the image to be scanned. The vulnerabilites and the severities
specified in the VulnerabilitiesIgnorePolicy object will be considered
only if the image to be scanned matches the targets specified for that
policy. Regular expressions can also be used to specify the image
attributes.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2025-02-07 10:26:53 +01:00
VaibhavMalik4187
a31154897f Added support for severity exceptions in imagescan
This commit add relevant functions to support severity exceptions during
image scan.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2025-02-07 10:26:53 +01:00
VaibhavMalik4187
199c57be30 WIP: Load image exceptions from file
Added initial commit to start loading image exceptions from json files.

Currently, it supports vulnerability exceptions using their CVE-IDs.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2025-02-07 10:26:53 +01:00
VaibhavMalik4187
7d55c79f11 Feature: Added exceptions flag in scan image cmd
This commit introduces the "exceptions" flag in the scan image command.
Users can pass a list of vulnerabilities they ignore while scanning an
image using this flag. Also added tests for the same.

Fixes: https://github.com/kubescape/kubescape/issues/1564

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2025-02-07 10:26:53 +01:00
Matthias Bertschy
ee76364371 fix scorecard-action version
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-07 09:23:28 +01:00
Matthias Bertschy
4f2c7ac1de Merge pull request #1787 from kubescape/bump
bump all CI actions
2025-02-07 09:09:44 +01:00
Matthias Bertschy
00340827be bump all CI actions
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-07 08:00:19 +01:00
Matthias Bertschy
708fe64240 Merge pull request #1786 from Fernando-hub527/refactor/maroto-version-v2
Refactor: maroto version v2
2025-02-07 07:33:34 +01:00
Fernando-hub527
8985bbe3a9 refactor: update Maroto configuration file location
Signed-off-by: Fernando-hub527 <fernandocoelhosaraivanando@gmail.com>
2025-02-06 18:07:57 -03:00
Matthias Bertschy
1ffca5648e delete TableObject.getSeverityColor to please CI
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-02-06 07:56:07 +01:00
Fernando-hub527
76b1ecb022 refactor: create a method that returns the pdf row
Return a struct with the data for ease of use

Signed-off-by: Fernando-hub527 <fernandocoelhosaraivanando@gmail.com>
2025-02-05 21:52:50 -03:00
Fernando-hub527
fc69a3692e refactor: remove unused code
Signed-off-by: Fernando-hub527 <fernandocoelhosaraivanando@gmail.com>
2025-02-05 21:47:28 -03:00
Fernando-hub527
e159458129 test:validate creation of report pdf
Signed-off-by: Fernando-hub527 <fernandocoelhosaraivanando@gmail.com>
2025-02-05 21:45:01 -03:00
Fernando-hub527
b259f117ff refactor: create a report template with maroto/v2
Separate template creation from application logic

Signed-off-by: Fernando-hub527 <fernandocoelhosaraivanando@gmail.com>
2025-02-05 21:36:39 -03:00
Fernando-hub527
13cf34bffd refactor: update Maroto version to v2
Signed-off-by: Fernando-hub527 <fernandocoelhosaraivanando@gmail.com>
2025-02-05 21:31:22 -03:00
Matthias Bertschy
0300fee38b bump actions/download-artifact@v4
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-31 16:38:01 +01:00
Matthias Bertschy
d61d641e81 bump actions/upload-artifact@v4
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-31 16:09:16 +01:00
Matthias Bertschy
2added0f7c use go 1.23, disable cache
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-31 16:06:13 +01:00
Matthias Bertschy
b6f6573ed8 Merge pull request #1785 from kubescape/sarif
fix helm detection in sarif printer
2025-01-31 16:04:36 +01:00
Matthias Bertschy
4215771134 fix helm detection in sarif printer
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-31 15:23:15 +01:00
Amit Schendel
fd37446e1b Merge pull request #1784 from kubescape/feature/incubating-logo
Changing README of Kubescape from Sandbox to Incubating
2025-01-27 14:01:09 +02:00
Amit Schendel
351498aac5 Changing README of Kubescape from Sandbox to Incubating
Signed-off-by: Amit Schendel <amitschendel@gmail.com>
2025-01-27 13:47:16 +02:00
Eng Zer Jun
2005010568 Replace golang.org/x/exp/slices with slices from stdlib (#1783) 2025-01-17 20:51:08 +02:00
Matthias Bertschy
e16c4cc9b4 fix go.mod for httphandler
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-16 15:20:58 +01:00
Matthias Bertschy
544ba9831a Merge pull request #1782 from kubescape/prereq
add prerequisites command
2025-01-16 14:50:28 +01:00
Matthias Bertschy
b6c919feb1 add prerequisites command
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-16 14:10:40 +01:00
Matthias Bertschy
1c3b2831a2 Merge pull request #1781 from kubescape/add-trigger-indication-in-posture-report
add trigger by indication in posture report
2025-01-09 16:12:43 +01:00
Amit Schendel
8a19a73bb1 Update b-binary-build-and-e2e-tests.yaml (#1761)
Signed-off-by: Amit Schendel <58078857+amitschendel@users.noreply.github.com>
2025-01-09 17:01:51 +02:00
Amir Malka
d966b0acbc update opa-utils
Signed-off-by: Amir Malka <amirm@armosec.io>
2025-01-09 16:35:53 +02:00
Amir Malka
14ffe35437 add trigger by indication in posture report
Signed-off-by: Amir Malka <amirm@armosec.io>
2025-01-09 16:13:29 +02:00
Matthias Bertschy
985d72e5fb Merge pull request #1780 from kubescape/airgap
Airgap support
2025-01-09 14:00:50 +01:00
Matthias Bertschy
70a9380966 fix go imports
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-09 12:14:56 +01:00
Matthias Bertschy
f706d126f5 fix error in log about failed resource update
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-09 11:52:34 +01:00
Matthias Bertschy
600f19406e enable offline mode with env KS_OFFLINE
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-09 10:33:19 +01:00
Matthias Bertschy
d7ebf3239b include artifacts in httphandler image
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-09 09:44:22 +01:00
Matthias Bertschy
5e0b25b04a update go-git
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-09 09:42:36 +01:00
Ben Hirschberg
98fe2347fa Merge pull request #1779 from kubescape/roadmap-deprecation-message
Deprecation of the roadmap.md in this repo
2025-01-06 13:53:47 +02:00
Ben Hirschberg
9b22d3284e Deprecation of the roadmap.md in this repo
Signed-off-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
2025-01-06 13:24:55 +02:00
Matthias Bertschy
9544e9cd66 Merge pull request #1778 from kubescape/bump
bump vulnerable deps
2024-12-20 17:04:20 +01:00
Matthias Bertschy
1ed1bb11f2 bump vulnerable deps
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2024-12-20 16:31:50 +01:00
Matthias Bertschy
b8ca1fcbce Merge pull request #1766 from Oshratn/inc_tasks
Update of README
2024-12-16 11:39:20 +01:00
Matthias Bertschy
326a3e4c63 Merge pull request #1776 from kubescape/dependabot/go_modules/golang.org/x/crypto-0.31.0
Bump golang.org/x/crypto from 0.27.0 to 0.31.0
2024-12-13 17:04:34 +01:00
dependabot[bot]
b348acd291 Bump golang.org/x/crypto from 0.27.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 15:31:42 +00:00
Matthias Bertschy
4fc3eacf7b Merge pull request #1775 from kubescape/dependabot/go_modules/httphandler/golang.org/x/crypto-0.31.0
Bump golang.org/x/crypto from 0.27.0 to 0.31.0 in /httphandler
2024-12-13 16:29:25 +01:00
dependabot[bot]
d6030a9c03 Bump golang.org/x/crypto from 0.27.0 to 0.31.0 in /httphandler
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 14:18:38 +00:00
Ben Hirschberg
e87bf7b723 Update maintainers in SECURITY-INSIGHTS.yml (#1774)
Signed-off-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
2024-12-11 17:58:13 +02:00
Oshrat Nir
570369a66f Merge branch 'master' into inc_tasks
Signed-off-by: Oshrat Nir <45561829+Oshratn@users.noreply.github.com>
2024-12-02 16:50:35 +02:00
Oshrat Nir
97f24920e8 conflict resolution
Signed-off-by: Oshrat Nir <oshratn@armosec.io>
2024-12-02 16:44:58 +02:00
Oshrat Nir
f57305280f Update of README
Signed-off-by: Oshrat Nir <oshratn@armosec.io>
2024-12-02 16:27:46 +02:00
179 changed files with 12961 additions and 4866 deletions

View File

@@ -1,6 +1,7 @@
name: 00-pr_scanner
permissions: read-all
on:
workflow_dispatch: {}
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths-ignore:
@@ -26,6 +27,7 @@ jobs:
deployments: read
id-token: write
issues: read
models: read
discussions: read
packages: read
pages: read
@@ -53,6 +55,7 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -65,7 +68,7 @@ jobs:
COMPONENT_NAME: kubescape
CGO_ENABLED: 0
GO111MODULE: ""
GO_VERSION: "1.21"
GO_VERSION: "1.25"
RELEASE: "latest"
CLIENT: test
secrets: inherit

View File

@@ -3,12 +3,12 @@ permissions: read-all
on:
push:
tags:
- 'v*.*.*-rc.*'
- "v*.*.*-rc.*"
jobs:
retag:
outputs:
NEW_TAG: ${{ steps.tag-calculator.outputs.NEW_TAG }}
runs-on: ubuntu22-core4-mem16-ssd150
runs-on: ubuntu-large
steps:
- uses: actions/checkout@v4
- id: tag-calculator
@@ -23,6 +23,7 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -31,13 +32,14 @@ jobs:
statuses: read
contents: write
attestations: write
artifact-metadata: read
needs: [retag]
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
with:
COMPONENT_NAME: kubescape
CGO_ENABLED: 0
GO111MODULE: ""
GO_VERSION: "1.21"
GO_VERSION: "1.25"
RELEASE: ${{ needs.retag.outputs.NEW_TAG }}
CLIENT: release
secrets: inherit
@@ -50,6 +52,7 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: read
pages: read
pull-requests: read
@@ -57,6 +60,7 @@ jobs:
statuses: read
security-events: read
attestations: read
artifact-metadata: read
needs: [retag, binary-build]
uses: ./.github/workflows/c-create-release.yaml
with:
@@ -72,6 +76,7 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -97,6 +102,7 @@ jobs:
discussions: read
id-token: write
issues: read
models: read
packages: write
pages: read
pull-requests: read
@@ -105,8 +111,9 @@ jobs:
statuses: read
attestations: read
contents: write
artifact-metadata: read
uses: ./.github/workflows/e-post-release.yaml
needs: [publish-image]
needs: [retag, publish-image]
with:
TAG: ${{ needs.retag.outputs.NEW_TAG }}
secrets: inherit

View File

@@ -27,7 +27,7 @@ jobs:
name: Create cross-platform build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ubuntu22-core4-mem16-ssd150
runs-on: ubuntu-large
steps:
- uses: actions/checkout@v4
@@ -39,7 +39,6 @@ jobs:
name: Installing go
with:
go-version: ${{ inputs.GO_VERSION }}
cache: true
- name: Test core pkg
run: ${{ env.DOCKER_CMD }} go test -v ./...
@@ -49,10 +48,10 @@ jobs:
run: ${{ env.DOCKER_CMD }} sh -c 'cd httphandler && go test -v ./...'
if: startsWith(github.ref, 'refs/tags')
- uses: anchore/sbom-action/download-syft@v0.15.2
- uses: anchore/sbom-action/download-syft@v0
name: Setup Syft
- uses: goreleaser/goreleaser-action@v5
- uses: goreleaser/goreleaser-action@v6
name: Build
with:
distribution: goreleaser
@@ -71,20 +70,18 @@ jobs:
- name: golangci-lint
continue-on-error: false
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v8
with:
version: latest
version: v2.1
args: --timeout 10m
only-new-issues: true
skip-pkg-cache: true
skip-build-cache: true
scanners:
env:
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
name: PR Scanner
runs-on: ubuntu22-core4-mem16-ssd150
runs-on: ubuntu-large
steps:
- uses: actions/checkout@v4
with:
@@ -93,8 +90,7 @@ jobs:
- uses: actions/setup-go@v4
name: Installing go
with:
go-version: '1.21'
cache: true
go-version: "1.25"
- name: Scanning - Forbidden Licenses (go-licenses)
id: licenses-scan
continue-on-error: true
@@ -107,7 +103,7 @@ jobs:
if: ${{ env.GITGUARDIAN_API_KEY }}
continue-on-error: true
id: credentials-scan
uses: GitGuardian/ggshield-action@4ab2994172fadab959240525e6b833d9ae3aca61 # ratchet:GitGuardian/ggshield-action@master
uses: GitGuardian/ggshield-action@master
with:
args: -v --all-policies
env:
@@ -120,7 +116,7 @@ jobs:
if: ${{ env.SNYK_TOKEN }}
id: vulnerabilities-scan
continue-on-error: true
uses: snyk/actions/golang@806182742461562b67788a64410098c9d9b96adb # ratchet:snyk/actions/golang@master
uses: snyk/actions/golang@master
with:
command: test --all-projects
env:
@@ -142,7 +138,7 @@ jobs:
- name: Comment results to PR
continue-on-error: true # Warning: This might break opening PRs from forks
uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # ratchet:peter-evans/create-or-update-comment@v2.1.0
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |

View File

@@ -18,7 +18,7 @@ on:
GO_VERSION:
required: false
type: string
default: "1.23"
default: "1.25"
GO111MODULE:
required: false
type: string
@@ -70,7 +70,7 @@ on:
type: string
GO_VERSION:
type: string
default: "1.23"
default: "1.25"
GO111MODULE:
required: true
type: string
@@ -163,7 +163,6 @@ jobs:
name: Installing go
with:
go-version: ${{ inputs.GO_VERSION }}
cache: true
- name: (debug) Step 3 - Check disk space before build
run: df -h
@@ -182,13 +181,13 @@ jobs:
- name: (debug) Step 5 - Check disk space before setting up Syft
run: df -h
- uses: anchore/sbom-action/download-syft@v0.15.2
- uses: anchore/sbom-action/download-syft@v0
name: Setup Syft
- name: (debug) Step 6 - Check disk space before goreleaser
run: df -h
- uses: goreleaser/goreleaser-action@v5
- uses: goreleaser/goreleaser-action@v6
name: Build
with:
distribution: goreleaser
@@ -224,11 +223,11 @@ jobs:
- name: (debug) Step 9 - Check disk space before uploading artifacts
run: df -h
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # ratchet:actions/upload-artifact@v3.1.1
- uses: actions/upload-artifact@v4
name: Upload artifacts
with:
name: kubescape
path: dist/kubescape*
path: dist/*
if-no-files-found: error
- name: (debug) Step 10 - Check disk space after uploading artifacts
@@ -249,7 +248,7 @@ jobs:
CGO_ENABLED: 0
GO111MODULE: "on"
BUILD_PLATFORM: linux/amd64,linux/arm64
GO_VERSION: "1.23"
GO_VERSION: "1.25"
REQUIRED_TESTS: '[
"ks_microservice_create_2_cronjob_mitre_and_nsa_proxy",
"ks_microservice_triggering_with_cron_job",
@@ -290,7 +289,7 @@ jobs:
if: ${{ (needs.wf-preparation.outputs.is-secret-set == 'true') && (always() && (contains(needs.*.result, 'success') || contains(needs.*.result, 'skipped')) && !(contains(needs.*.result, 'failure')) && !(contains(needs.*.result, 'cancelled'))) }}
runs-on: ubuntu-latest # This cannot change
steps:
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3.0.2
- uses: actions/download-artifact@v4
id: download-artifact
with:
name: kubescape
@@ -307,9 +306,9 @@ jobs:
repository: armosec/system-tests
path: .
- uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # ratchet:actions/setup-python@v4
- uses: actions/setup-python@v4
with:
python-version: '3.8.13'
python-version: '3.9'
cache: 'pip'
- name: create env
@@ -322,7 +321,7 @@ jobs:
- name: Create k8s Kind Cluster
id: kind-cluster-install
uses: helm/kind-action@d08cf6ff1575077dee99962540d77ce91c62387d # ratchet:helm/kind-action@v1.3.0
uses: helm/kind-action@v1.10.0
with:
cluster_name: ${{ steps.uuid.outputs.RANDOM_UUID }}
@@ -352,7 +351,7 @@ jobs:
deactivate
- name: Test Report
uses: mikepenz/action-junit-report@6e9933f4a97f4d2b99acef4d7b97924466037882 # ratchet:mikepenz/action-junit-report@v3.6.1
uses: mikepenz/action-junit-report@v5
if: always() # always run even if the previous step fails
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -33,7 +33,7 @@ jobs:
CGO_ENABLED: 0
GO111MODULE: "on"
BUILD_PLATFORM: ${{ inputs.PLATFORMS && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
GO_VERSION: "1.21"
GO_VERSION: "1.25"
REQUIRED_TESTS: '[]'
COSIGN: ${{ inputs.CO_SIGN }}
HELM_E2E_TEST: false

View File

@@ -27,14 +27,15 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3.0.2
- uses: actions/download-artifact@v4
id: download-artifact
with:
name: kubescape
path: .
# TODO: kubescape-windows-latest is deprecated and should be removed
- name: Get kubescape.exe from kubescape-windows-latest.exe
run: cp ${{steps.download-artifact.outputs.download-path}}/kubescape/kubescape-${{ env.WINDOWS_OS }}.exe ${{steps.download-artifact.outputs.download-path}}/kubescape/kubescape.exe
run: cp ${{steps.download-artifact.outputs.download-path}}/kubescape-${{ env.WINDOWS_OS }}.exe ${{steps.download-artifact.outputs.download-path}}/kubescape.exe
- name: Set release token
id: set-token
@@ -50,7 +51,7 @@ jobs:
find . -type f -print
- name: Release
uses: softprops/action-gh-release@975c1b265e11dd76618af1c374e7981f9a6ff44a
uses: softprops/action-gh-release@v2
with:
token: ${{ steps.set-token.outputs.token }}
name: ${{ inputs.RELEASE_NAME }}
@@ -60,32 +61,26 @@ jobs:
prerelease: false
fail_on_unmatched_files: true
files: |
./kubescape/kubescape-${{ env.MAC_OS }}
./kubescape/kubescape-${{ env.MAC_OS }}.sbom
./kubescape/kubescape-${{ env.MAC_OS }}.sha256
./kubescape/kubescape-${{ env.MAC_OS }}.tar.gz
./kubescape/kubescape-${{ env.UBUNTU_OS }}
./kubescape/kubescape-${{ env.UBUNTU_OS }}.sbom
./kubescape/kubescape-${{ env.UBUNTU_OS }}.sha256
./kubescape/kubescape-${{ env.UBUNTU_OS }}.tar.gz
./kubescape/kubescape-${{ env.WINDOWS_OS }}.exe
./kubescape/kubescape-${{ env.WINDOWS_OS }}.exe.sbom
./kubescape/kubescape-${{ env.WINDOWS_OS }}.exe.sha256
./kubescape/kubescape-${{ env.WINDOWS_OS }}.tar.gz
./kubescape/kubescape-arm64-${{ env.MAC_OS }}
./kubescape/kubescape-arm64-${{ env.MAC_OS }}.sbom
./kubescape/kubescape-arm64-${{ env.MAC_OS }}.sha256
./kubescape/kubescape-arm64-${{ env.MAC_OS }}.tar.gz
./kubescape/kubescape-arm64-${{ env.UBUNTU_OS }}
./kubescape/kubescape-arm64-${{ env.UBUNTU_OS }}.sbom
./kubescape/kubescape-arm64-${{ env.UBUNTU_OS }}.sha256
./kubescape/kubescape-arm64-${{ env.UBUNTU_OS }}.tar.gz
./kubescape/kubescape-arm64-${{ env.WINDOWS_OS }}.exe
./kubescape/kubescape-arm64-${{ env.WINDOWS_OS }}.exe.sbom
./kubescape/kubescape-arm64-${{ env.WINDOWS_OS }}.exe.sha256
./kubescape/kubescape-arm64-${{ env.WINDOWS_OS }}.tar.gz
./kubescape/kubescape-riscv64-${{ env.UBUNTU_OS }}
./kubescape/kubescape-riscv64-${{ env.UBUNTU_OS }}.sbom
./kubescape/kubescape-riscv64-${{ env.UBUNTU_OS }}.sha256
./kubescape/kubescape-riscv64-${{ env.UBUNTU_OS }}.tar.gz
./kubescape/kubescape.exe
./checksums.sha256
./kubescape-${{ env.MAC_OS }}
./kubescape-${{ env.MAC_OS }}.sbom
./kubescape-${{ env.MAC_OS }}.tar.gz
./kubescape-${{ env.UBUNTU_OS }}
./kubescape-${{ env.UBUNTU_OS }}.sbom
./kubescape-${{ env.UBUNTU_OS }}.tar.gz
./kubescape-${{ env.WINDOWS_OS }}.exe
./kubescape-${{ env.WINDOWS_OS }}.exe.sbom
./kubescape-${{ env.WINDOWS_OS }}.tar.gz
./kubescape-arm64-${{ env.MAC_OS }}
./kubescape-arm64-${{ env.MAC_OS }}.sbom
./kubescape-arm64-${{ env.MAC_OS }}.tar.gz
./kubescape-arm64-${{ env.UBUNTU_OS }}
./kubescape-arm64-${{ env.UBUNTU_OS }}.sbom
./kubescape-arm64-${{ env.UBUNTU_OS }}.tar.gz
./kubescape-arm64-${{ env.WINDOWS_OS }}.exe
./kubescape-arm64-${{ env.WINDOWS_OS }}.exe.sbom
./kubescape-arm64-${{ env.WINDOWS_OS }}.tar.gz
./kubescape-riscv64-${{ env.UBUNTU_OS }}
./kubescape-riscv64-${{ env.UBUNTU_OS }}.sbom
./kubescape-riscv64-${{ env.UBUNTU_OS }}.tar.gz
./kubescape.exe

View File

@@ -7,6 +7,7 @@ permissions:
discussions: read
id-token: write
issues: read
models: read
packages: read
pages: read
pull-requests: read
@@ -63,22 +64,21 @@ jobs:
with:
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # ratchet:docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # ratchet:docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Quay.io
env:
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3.0.2
- uses: actions/download-artifact@v4
id: download-artifact
with:
name: kubescape
path: .
- name: mv kubescape amd64 binary
run: mv ${{steps.download-artifact.outputs.download-path}}/kubescape/kubescape-ubuntu-latest kubescape-amd64-ubuntu-latest
- name: mv kubescape arm64 binary
run: mv ${{steps.download-artifact.outputs.download-path}}/kubescape/kubescape-arm64-ubuntu-latest kubescape-arm64-ubuntu-latest
run: mv kubescape-ubuntu-latest kubescape-amd64-ubuntu-latest
- name: chmod +x
run: chmod +x -v kubescape-a*
- name: Build and push images
@@ -106,4 +106,3 @@ jobs:
# Verify the image
echo "$COSIGN_PUBLIC_KEY" > cosign.pub
cosign verify -key cosign.pub ${{ inputs.image_name }}:${{ inputs.image_tag }}

View File

@@ -19,7 +19,7 @@ jobs:
uses: rajatjindal/krew-release-bot@v0.0.47
if: github.repository_owner == 'kubescape'
env:
GITHUB_REF: ${{ inputs.TAG }}
GITHUB_REF: refs/tags/${{ inputs.TAG }}
- name: Invoke workflow to update packaging
uses: benc-uk/workflow-dispatch@v1
if: github.repository_owner == 'kubescape'

View File

@@ -32,12 +32,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
uses: ossf/scorecard-action@v2.4.0
with:
results_file: results.sarif
results_format: sarif
@@ -59,7 +59,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0
uses: actions/upload-artifact@v4
with:
name: SARIF file
path: results.sarif
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

View File

@@ -7,14 +7,14 @@ jobs:
if: github.event.label.name == 'typo'
runs-on: ubuntu-latest
steps:
- uses: ben-z/actions-comment-on-issue@10be23f9c43ac792663043420fda29dde07e2f0f # ratchet:ben-z/actions-comment-on-issue@1.0.2
- uses: ben-z/actions-comment-on-issue@1.0.2
with:
message: "Hello! :wave:\n\nThis issue is being automatically closed, Please open a PR with a relevant fix."
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
auto_close_issues:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/close-matching-issues@e9e43aad2fa6f06a058cedfd8fb975fd93b56d8f # ratchet:lee-dohm/close-matching-issues@v2
- uses: lee-dohm/close-matching-issues@v2
with:
query: 'label:typo'
token: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore vendored
View File

@@ -11,3 +11,7 @@ ca.srl
ks
dist/
# Test output files
customFilename.pdf
customFilename.xml

View File

@@ -1,51 +1,57 @@
linters-settings:
govet:
shadow: true
dupl:
threshold: 200
goconst:
min-len: 3
min-occurrences: 2
gocognit:
min-complexity: 65
version: "2"
linters:
enable:
- gosec
- staticcheck
- nolintlint
- gofmt
- unused
- govet
- bodyclose
- typecheck
- goimports
- ineffassign
- gosimple
- gosec
- nolintlint
disable:
# temporarily disabled
- errcheck
- dupl
- gocritic
- errcheck
- gochecknoglobals
- gochecknoinits
- gocognit
- gocritic
- lll
- nakedret
- revive
- stylecheck
- unconvert
- unparam
#- forbidigo # <- see later
# should remain disabled
- lll
- gochecknoinits
- gochecknoglobals
issues:
exclude-rules:
- linters:
- revive
text: "var-naming"
- linters:
- revive
text: "type name will be used as (.+?) by other packages, and that stutters"
- linters:
- stylecheck
text: "ST1003"
settings:
dupl:
threshold: 200
gocognit:
min-complexity: 65
goconst:
min-len: 3
min-occurrences: 2
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- revive
text: var-naming
- linters:
- revive
text: type name will be used as (.+?) by other packages, and that stutters
- linters:
- staticcheck
text: ST1003
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@@ -1,16 +1,29 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines bellow are called `modelines`. See `:help modeline`
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
archives:
- id: binaries
formats:
- binary
name_template: >-
{{ .Binary }}
- id: default
formats:
- tar.gz
name_template: >-
{{ .Binary }}
builds:
- goos:
- linux
@@ -33,15 +46,6 @@ builds:
{{- else }}{{ .Os }}{{ end }}-latest
no_unique_dist_dir: true
archives:
- format: binary
id: binaries
name_template: >-
{{ .Binary }}
- format: tar.gz
name_template: >-
{{ .Binary }}
changelog:
sort: asc
filters:
@@ -50,9 +54,7 @@ changelog:
- "^test:"
checksum:
ids:
- binaries
split: true
name_template: "checksums.sha256"
sboms:
- artifacts: binary

484
README.md
View File

@@ -3,11 +3,12 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/kubescape/kubescape)](https://goreportcard.com/report/github.com/kubescape/kubescape)
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/kubescape/kubescape)
[![GitHub](https://img.shields.io/github/license/kubescape/kubescape)](https://github.com/kubescape/kubescape/blob/master/LICENSE)
[![CNCF](https://shields.io/badge/CNCF-Sandbox%20project-blue?logo=linux-foundation&style=flat)](https://landscape.cncf.io/card-mode?project=sandbox&selected=kubescape)
[![CNCF](https://shields.io/badge/CNCF-Incubating%20project-blue?logo=linux-foundation&style=flat)](https://landscape.cncf.io/?item=provisioning--security-compliance--kubescape)
[![Artifact HUB](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/kubescape)](https://artifacthub.io/packages/search?repo=kubescape)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkubescape%2Fkubescape.svg?type=shield&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Fkubescape%2Fkubescape?ref=badge_shield&issueType=license)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/6944/badge)](https://www.bestpractices.dev/projects/6944)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/kubescape/kubescape/badge)](https://securityscorecards.dev/viewer/?uri=github.com/kubescape/kubescape)
[![Docs](https://img.shields.io/badge/docs-latest-brightgreen?logo=gitbook)](https://kubescape.io/docs/)
[![Stars](https://img.shields.io/github/stars/kubescape/kubescape?style=social)](https://github.com/kubescape/kubescape/stargazers)
[![Twitter Follow](https://img.shields.io/twitter/follow/kubescape?style=social)](https://twitter.com/kubescape)
[![Slack](https://img.shields.io/badge/slack-kubescape-blueviolet?logo=slack)](https://cloud-native.slack.com/archives/C04EY3ZF9GE)
@@ -22,119 +23,478 @@
_Comprehensive Kubernetes Security from Development to Runtime_
Kubescape is an open-source Kubernetes security platform that provides comprehensive security coverage from left to right across the entire development and deployment lifecycle. It offers hardening, posture management, and runtime security capabilities to ensure robust protection for Kubernetes environments.
Kubescape is an open-source Kubernetes security platform that provides comprehensive security coverage, from left to right across the entire development and deployment lifecycle. It offers hardening, posture management, and runtime security capabilities to ensure robust protection for Kubernetes environments.
**Key features of Kubescape include**
Kubescape was created by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository) and is a [Cloud Native Computing Foundation (CNCF) incubating project](https://www.cncf.io/projects/).
* **Shift-left security**: Kubescape enables developers to scan for misconfigurations as early as the manifest file submission stage, promoting a proactive approach to security.
* **IDE and CI/CD integration**: The tool integrates seamlessly with popular IDEs like VSCode and Lens, as well as CI/CD platforms such as GitHub and GitLab, allowing for security checks throughout the development process.
* **Cluster scanning**: Kubescape can scan active Kubernetes clusters for vulnerabilities, misconfigurations, and security issues
* **Multiple framework support**: Kubescape can test against various security frameworks, including NSA, MITRE, SOC2, and more.
* **YAML and Helm chart validation**: The tool checks YAML files and Helm charts for correct configuration according to the frameworks above, without requiring an active cluster.
* **Kubernetes hardening**: Kubescape ensures proactive identification and rapid remediation of misconfigurations and vulnerabilities through manual, recurring, or event-triggered scans.
* **Runtime security**: Kubescape extends its protection to the runtime environment, providing continuous monitoring and threat detection for deployed applications.
* **Compliance management**: The tool aids in maintaining compliance with recognized frameworks and standards, simplifying the process of meeting regulatory requirements.
* **Multi-cloud support**: Kubescape offers frictionless security across various cloud providers and Kubernetes distributions.
_Please [star ⭐](https://github.com/kubescape/kubescape/stargazers) the repo if you want us to continue developing and improving Kubescape!_
By providing this comprehensive security coverage from development to production, Kubescape enables organizations to implement a robust security posture throughout their Kubernetes deployment, addressing potential vulnerabilities and threats at every stage of the application lifecycle.
---
Kubescape was created by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository) and is a [Cloud Native Computing Foundation (CNCF) sandbox project](https://www.cncf.io/sandbox-projects/).
## 📑 Table of Contents
_Please [star ⭐](https://github.com/kubescape/kubescape/stargazers) the repo if you want us to continue developing and improving Kubescape! 😀_
- [Features](#-features)
- [Demo](#-demo)
- [Quick Start](#-quick-start)
- [Installation](#-installation)
- [CLI Commands](#-cli-commands)
- [Usage Examples](#-usage-examples)
- [Architecture](#-architecture)
- [In-Cluster Operator](#-in-cluster-operator)
- [Integrations](#-integrations)
- [Community](#-community)
- [Changelog](#changelog)
- [License](#license)
## Demo
---
Kubescape has a command line tool that you can use to quickly get a report on the security posture of a Kubernetes cluster:
## ✨ Features
<img src="docs/img/demo-v3.gif">
| Feature | Description |
|---------|-------------|
| 🔍 **Misconfiguration Scanning** | Scan clusters, YAML files, and Helm charts against NSA-CISA, MITRE ATT&CK®, and CIS Benchmarks |
| 🐳 **Image Vulnerability Scanning** | Detect CVEs in container images using [Grype](https://github.com/anchore/grype) |
| 🩹 **Image Patching** | Automatically patch vulnerable images using [Copacetic](https://github.com/project-copacetic/copacetic) |
| 🔧 **Auto-Remediation** | Automatically fix misconfigurations in Kubernetes manifests |
| 🛡️ **Admission Control** | Enforce security policies with Validating Admission Policies (VAP) |
| 📊 **Runtime Security** | eBPF-based runtime monitoring via [Inspektor Gadget](https://github.com/inspektor-gadget) |
| 🤖 **AI Integration** | MCP server for AI assistant integration |
## Getting started
---
Experimenting with Kubescape is as easy as:
## 🎬 Demo
<img src="docs/img/demo-v3.gif" alt="Kubescape CLI demo">
---
## 🚀 Quick Start
### 1. Install Kubescape
```sh
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
```
This script will automatically download the latest Kubescape CLI release and scan the Kubernetes cluster in your current kubectl context.
> 💡 See [Installation](#-installation) for more options (Homebrew, Krew, Windows, etc.)
Learn more about:
### 2. Run Your First Scan
* [Installing the Kubescape CLI](https://kubescape.io/docs/install-cli/)
* [Running your first scan](https://kubescape.io/docs/scanning/)
* [Accepting risk with exceptions](https://kubescape.io/docs/accepting-risk/)
```sh
# Scan your current cluster
kubescape scan
_Did you know you can use Kubescape in all these places?_
# Scan a specific YAML file or directory
kubescape scan /path/to/manifests/
# Scan a container image for vulnerabilities
kubescape scan image nginx:latest
```
### 3. Explore the Results
Kubescape provides a detailed security posture overview including:
- Control plane security status
- Access control risks
- Workload misconfigurations
- Network policy gaps
- Compliance scores (MITRE, NSA)
---
## 📦 Installation
### One-Line Install (Linux/macOS)
```bash
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
```
### Package Managers
| Platform | Command |
|----------|---------|
| **Homebrew** | `brew install kubescape` |
| **Krew** | `kubectl krew install kubescape` |
| **Arch Linux** | `yay -S kubescape` |
| **Ubuntu** | `sudo add-apt-repository ppa:kubescape/kubescape && sudo apt install kubescape` |
| **NixOS** | `nix-shell -p kubescape` |
| **Chocolatey** | `choco install kubescape` |
| **Scoop** | `scoop install kubescape` |
### Windows (PowerShell)
```powershell
iwr -useb https://raw.githubusercontent.com/kubescape/kubescape/master/install.ps1 | iex
```
📖 **[Full Installation Guide →](docs/installation.md)**
---
## 🛠️ CLI Commands
Kubescape provides a comprehensive CLI with the following commands:
| Command | Description |
|---------|-------------|
| [`kubescape scan`](#scanning) | Scan cluster, files, or images for security issues |
| [`kubescape scan image`](#image-scanning) | Scan container images for vulnerabilities |
| [`kubescape fix`](#auto-fix) | Auto-fix misconfigurations in manifest files |
| [`kubescape patch`](#image-patching) | Patch container images to fix vulnerabilities |
| [`kubescape list`](#list-frameworks-and-controls) | List available frameworks and controls |
| [`kubescape download`](#offline-support) | Download artifacts for offline/air-gapped use |
| [`kubescape config`](#configuration) | Manage cached configurations |
| [`kubescape operator`](#operator-commands) | Interact with in-cluster Kubescape operator |
| [`kubescape vap`](#validating-admission-policies) | Manage Validating Admission Policies |
| [`kubescape mcpserver`](#mcp-server) | Start MCP server for AI assistant integration |
| `kubescape completion` | Generate shell completion scripts |
| `kubescape version` | Display version information |
---
## 📖 Usage Examples
### Scanning
#### Scan a Running Cluster
```bash
# Default scan (all frameworks)
kubescape scan
# Scan with a specific framework
kubescape scan framework nsa
kubescape scan framework mitre
kubescape scan framework cis-v1.23-t1.0.1
# Scan a specific control
kubescape scan control C-0005 -v
```
#### Scan Files and Repositories
```bash
# Scan local YAML files
kubescape scan /path/to/manifests/
# Scan a Helm chart
kubescape scan /path/to/helm/chart/
# Scan a Git repository
kubescape scan https://github.com/kubescape/kubescape
# Scan with Kustomize
kubescape scan /path/to/kustomize/directory/
```
#### Scan Options
```bash
# Include/exclude namespaces
kubescape scan --include-namespaces production,staging
kubescape scan --exclude-namespaces kube-system,kube-public
# Use alternative kubeconfig
kubescape scan --kubeconfig /path/to/kubeconfig
# Set compliance threshold (exit code 1 if below threshold)
kubescape scan --compliance-threshold 80
# Set severity threshold
kubescape scan --severity-threshold high
```
#### Output Formats
```bash
# JSON output
kubescape scan --format json --output results.json
# JUnit XML (for CI/CD)
kubescape scan --format junit --output results.xml
# SARIF (for GitHub Code Scanning)
kubescape scan --format sarif --output results.sarif
# HTML report
kubescape scan --format html --output report.html
# PDF report
kubescape scan --format pdf --output report.pdf
```
### Image Scanning
```bash
# Scan a public image
kubescape scan image nginx:1.21
# Scan with verbose output
kubescape scan image nginx:1.21 -v
# Scan a private registry image
kubescape scan image myregistry/myimage:tag --username user --password pass
```
### Auto-Fix
Automatically fix misconfigurations in your manifest files:
```bash
# First, scan and save results to JSON
kubescape scan /path/to/manifests --format json --output results.json
# Then apply fixes
kubescape fix results.json
# Dry run (preview changes without applying)
kubescape fix results.json --dry-run
# Apply fixes without confirmation prompts
kubescape fix results.json --no-confirm
```
### Image Patching
Patch container images to fix OS-level vulnerabilities:
```bash
# Start buildkitd (required)
sudo buildkitd &
# Patch an image
sudo kubescape patch --image docker.io/library/nginx:1.22
# Specify custom output tag
sudo kubescape patch --image nginx:1.22 --tag nginx:1.22-patched
# See detailed vulnerability report
sudo kubescape patch --image nginx:1.22 -v
```
📖 **[Full Patch Command Documentation →](cmd/patch/README.md)**
### List Frameworks and Controls
```bash
# List available frameworks
kubescape list frameworks
# List all controls
kubescape list controls
# Output as JSON
kubescape list controls --format json
```
### Offline Support
Download artifacts for air-gapped environments:
```bash
# Download all artifacts
kubescape download artifacts --output /path/to/offline/dir
# Download a specific framework
kubescape download framework nsa --output /path/to/nsa.json
# Scan using downloaded artifacts
kubescape scan --use-artifacts-from /path/to/offline/dir
```
### Configuration
```bash
# View current configuration
kubescape config view
# Set account ID
kubescape config set accountID <your-account-id>
# Delete cached configuration
kubescape config delete
```
### Operator Commands
Interact with the in-cluster Kubescape operator:
```bash
# Trigger a configuration scan
kubescape operator scan configurations
# Trigger a vulnerability scan
kubescape operator scan vulnerabilities
```
### Validating Admission Policies
Manage Kubernetes Validating Admission Policies:
```bash
# Deploy the Kubescape CEL admission policy library
kubescape vap deploy-library | kubectl apply -f -
# Create a policy binding
kubescape vap create-policy-binding \
--name my-policy-binding \
--policy c-0016 \
--namespace my-namespace | kubectl apply -f -
```
### MCP Server
Start an MCP (Model Context Protocol) server for AI assistant integration:
```bash
kubescape mcpserver
```
The MCP server exposes Kubescape's vulnerability and configuration scan data to AI assistants, enabling natural language queries about your cluster's security posture.
**Available MCP Tools:**
- `list_vulnerability_manifests` - Discover vulnerability manifests
- `list_vulnerabilities_in_manifest` - List CVEs in a manifest
- `list_vulnerability_matches_for_cve` - Get details for a specific CVE
- `list_configuration_security_scan_manifests` - List configuration scan results
- `get_configuration_security_scan_manifest` - Get configuration scan details
---
## 🏗️ Architecture
Kubescape can run in two modes:
### CLI Mode
The CLI is a standalone tool that scans clusters, files, and images on-demand.
<div align="center">
<img src="docs/img/ksfromcodetodeploy.png" alt="Places you can use Kubescape: in your IDE, CI, CD, or against a running cluster.">
<img src="docs/img/ks-cli-arch.png" width="600" alt="CLI Architecture">
</div>
### Continuous security monitoring with the Kubescape Operator
**Key Components:**
- **[Open Policy Agent (OPA)](https://github.com/open-policy-agent/opa)** - Policy evaluation engine
- **[Regolibrary](https://github.com/kubescape/regolibrary)** - Library of security controls
- **[Grype](https://github.com/anchore/grype)** - Image vulnerability scanning
- **[Copacetic](https://github.com/project-copacetic/copacetic)** - Image patching
As well as a CLI, Kubescape provides an in-cluster mode, which is installed via a Helm chart. Kubescape in-cluster provides extensive features such as continuous scanning, image vulnerability scanning, runtime analysis, network policy generation, and more. [Learn more about the Kubescape operator](https://kubescape.io/docs/operator/).
### Operator Mode (In-Cluster)
### Using Kubescape as a GitHub Action
For continuous monitoring, deploy the Kubescape operator via Helm.
Kubescape can be used as a GitHub Action. This is a great way to integrate Kubescape into your CI/CD pipeline. You can find the Kubescape GitHub Action in the [GitHub Action marketplace](https://github.com/marketplace/actions/kubescape).
<div align="center">
<img src="docs/img/ks-operator-arch.png" width="600" alt="Operator Architecture">
</div>
## Under the hood
**Additional Capabilities:**
- Continuous configuration scanning
- Image vulnerability scanning
- Runtime analysis with eBPF
- Network policy generation
Kubescape uses [Open Policy Agent](https://github.com/open-policy-agent/opa) to verify Kubernetes objects against [a library of posture controls](https://github.com/kubescape/regolibrary). Kubescape retrieves Kubernetes resources from the API server and runs a set of [Rego snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
📖 **[Full Architecture Documentation →](docs/architecture.md)**
Container image scanning is powered by [Grype](https://github.com/anchore/grype) and image patching uses [Copacetic](https://github.com/project-copacetic/copacetic).
---
By default, CLI scan results are printed in a console-friendly manner, but they can be:
## ☸️ In-Cluster Operator
* exported to JSON, junit XML or SARIF
* rendered to HTML or PDF
* submitted to a [cloud service](docs/providers.md)
The Kubescape operator provides continuous security monitoring in your cluster:
### In-cluster architecture
```bash
# Add the Kubescape Helm repository
helm repo add kubescape https://kubescape.github.io/helm-charts/
![Architecture diagram](docs/img/architecture-diagram.png)
# Install the operator
helm upgrade --install kubescape kubescape/kubescape-operator \
--namespace kubescape \
--create-namespace
```
## Community
**Operator Features:**
- 🔄 Continuous misconfiguration scanning
- 🐳 Image vulnerability scanning for all workloads
- 🔍 Runtime threat detection (eBPF-based)
- 🌐 Network policy generation
- 📈 Prometheus metrics integration
We welcome user feedback and ideas for improvement.
📖 **[Operator Installation Guide →](https://kubescape.io/docs/operator/)**
Kubescape users and developers meet on the CNCF Slack. [Join it](https://slack.cncf.io/) and find us in [#kubescape](https://cloud-native.slack.com/archives/C04EY3ZF9GE) or [#kubescape-dev](https://cloud-native.slack.com/archives/C04GY6H082K).
---
We hold [community meetings](https://zoom.us/j/95174063585) on Zoom, every second Tuesday, at 15:00 CET. ([See that in your local time zone](https://time.is/compare/1500_in_CET).
## 🔌 Integrations
* Meetings are announced in [#kubescape-dev](https://cloud-native.slack.com/archives/C04GY6H082K) on Slack (including any cancellations).
* [The agenda and notes are in a public Google doc](https://docs.google.com/document/d/1X_eyhPzJvb4ascVQ2e0jN87LAvq7lTuXT5d4gQxi8us/edit?tab=t.0).
* [Recordings are posted to YouTube](https://www.youtube.com/@kubescape).
### CI/CD
The Kubescape project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
| Platform | Integration |
|----------|-------------|
| **GitHub Actions** | [kubescape/github-action](https://github.com/marketplace/actions/kubescape) |
| **GitLab CI** | [Documentation](https://kubescape.io/docs/integrations/gitlab/) |
| **Jenkins** | [Documentation](https://kubescape.io/docs/integrations/jenkins/) |
### Adopters
### IDE Extensions
See [here](ADOPTERS.md) for a list of reference adopters.
| IDE | Extension |
|-----|-----------|
| **VS Code** | [Kubescape Extension](https://marketplace.visualstudio.com/items?itemName=kubescape.kubescape) |
| **Lens** | [Kubescape Lens Extension](https://github.com/armosec/lens-kubescape) |
### Contributions
### Where You Can Use Kubescape
Thanks to all our contributors! Check out our [CONTRIBUTING](CONTRIBUTING.md) file to learn how to join them.
<div align="center">
<img src="docs/img/ksfromcodetodeploy.png" alt="Kubescape integration points: IDE, CI, CD, Runtime">
</div>
* Feel free to pick a task from the [issues](https://github.com/kubescape/kubescape/issues?q=is%3Aissue+is%3Aopen+label%3A%22open+for+contribution%22), [roadmap](docs/roadmap.md) or suggest a feature of your own.
* [Open an issue](https://github.com/kubescape/kubescape/issues/new/choose): we aim to respond to all issues within 48 hours.
---
<br>
## 👥 Community
<a href = "https://github.com/kubescape/kubescape/graphs/contributors">
<img src = "https://contrib.rocks/image?repo=kubescape/kubescape"/>
Kubescape is a CNCF incubating project with an active community.
### Get Involved
- 💬 **[Slack - Users Channel](https://cloud-native.slack.com/archives/C04EY3ZF9GE)** - Ask questions, get help
- 💬 **[Slack - Developers Channel](https://cloud-native.slack.com/archives/C04GY6H082K)** - Contribute to development
- 🐛 **[GitHub Issues](https://github.com/kubescape/kubescape/issues)** - Report bugs and request features
- 📋 **[Project Board](https://github.com/orgs/kubescape/projects/4)** - See what we're working on
- 🗺️ **[Roadmap](https://github.com/kubescape/project-governance/blob/main/ROADMAP.md)** - Future plans
### Contributing
We welcome contributions! Please see our:
- **[Contributing Guide](https://github.com/kubescape/project-governance/blob/main/CONTRIBUTING.md)**
- **[Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md)**
### Community Resources
- **[Community Info](https://github.com/kubescape/project-governance/blob/main/COMMUNITY.md)**
- **[Governance](https://github.com/kubescape/project-governance/blob/main/GOVERNANCE.md)**
- **[Security Policy](https://github.com/kubescape/project-governance/blob/main/SECURITY.md)**
- **[Maintainers](https://github.com/kubescape/project-governance/blob/main/MAINTAINERS.md)**
### Contributors
<a href="https://github.com/kubescape/kubescape/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kubescape/kubescape"/>
</a>
---
## Changelog
Kubescape changes are tracked on the [release](https://github.com/kubescape/kubescape/releases) page.
Kubescape changes are tracked on the [releases page](https://github.com/kubescape/kubescape/releases).
---
## License
Copyright 2021-2024, the Kubescape Authors. All rights reserved. Kubescape is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
Copyright 2021-2025, the Kubescape Authors. All rights reserved.
Kubescape is a [Cloud Native Computing Foundation (CNCF) sandbox project](https://www.cncf.io/sandbox-projects/) and was contributed by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository).
Kubescape is released under the [Apache 2.0 license](LICENSE).
Kubescape is a [Cloud Native Computing Foundation (CNCF) incubating project](https://www.cncf.io/projects/kubescape/) and was contributed by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository).
<div align="center">
<img src="https://raw.githubusercontent.com/cncf/artwork/master/other/cncf-sandbox/horizontal/color/cncf-sandbox-horizontal-color.svg" width="300" alt="CNCF Sandbox Project">
</div>
<img src="https://raw.githubusercontent.com/cncf/artwork/refs/heads/main/other/cncf-member/incubating/color/cncf-incubating-color.svg" width="300" alt="CNCF Incubating Project">
</div>

View File

@@ -4,15 +4,19 @@ header:
last-reviewed: '2023-10-12'
expiration-date: '2024-10-12T01:00:00.000Z'
project-url: https://github.com/kubescape/kubescape/
project-release: '1.0.0'
project-release: 1.0.0
project-lifecycle:
status: active
bug-fixes-only: false
core-maintainers:
- github:slashben
- github:amirmalka
- github:amitschendel
- github:bezbran
- github:craigbox
- github:matthyx
- github:dwertent
- github:matthyx
- github:rotemamsa
- github:slashben
contribution-policy:
accepts-pull-requests: true
accepts-automated-pull-requests: false

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.23-bookworm AS builder
FROM --platform=$BUILDPLATFORM golang:1.25-trixie AS builder
ENV GO111MODULE=on CGO_ENABLED=0
WORKDIR /work
@@ -8,13 +8,18 @@ RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
cd httphandler && GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/ksserver .
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
go run downloader/main.go
FROM gcr.io/distroless/static-debian12:nonroot
FROM gcr.io/distroless/static-debian13:nonroot
USER nonroot
WORKDIR /home/nonroot/
COPY --from=builder /out/ksserver /usr/bin/ksserver
COPY --from=builder /root/.kubescape /home/nonroot/.kubescape
ARG image_version client
ENV RELEASE=$image_version CLIENT=$client

View File

@@ -1,19 +1,264 @@
## Docker Build
# Building Kubescape
### Build your own Docker image
This guide covers how to build Kubescape from source.
1. Clone Project
```
git clone https://github.com/kubescape/kubescape.git kubescape && cd "$_"
## Table of Contents
- [Prerequisites](#prerequisites)
- [Building the CLI](#building-the-cli)
- [Building Docker Images](#building-docker-images)
- [Build Options](#build-options)
- [Development Setup](#development-setup)
- [Troubleshooting](#troubleshooting)
---
## Prerequisites
### Required
- **Go 1.23+** - [Installation Guide](https://golang.org/doc/install)
- **Git** - For cloning the repository
- **Make** - For running build commands
### Optional (for Docker builds)
- **Docker** - [Installation Guide](https://docs.docker.com/get-docker/)
- **Docker Buildx** - For multi-platform builds (included with Docker Desktop)
### Verify Prerequisites
```bash
go version # Should be 1.21 or higher
git --version
make --version
docker --version # Optional
```
2. Build kubescape CLI Docker image
---
## Building the CLI
### Clone the Repository
```bash
git clone https://github.com/kubescape/kubescape.git
cd kubescape
```
### Build with Make
```bash
# Build for your current platform
make build
# The binary will be at ./kubescape
./kubescape version
```
### Build Directly with Go
```bash
go build -o kubescape .
```
### Cross-Compilation
Build for different platforms:
```bash
# Linux (amd64)
GOOS=linux GOARCH=amd64 go build -o kubescape-linux-amd64 .
# Linux (arm64)
GOOS=linux GOARCH=arm64 go build -o kubescape-linux-arm64 .
# macOS (amd64)
GOOS=darwin GOARCH=amd64 go build -o kubescape-darwin-amd64 .
# macOS (arm64 / Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o kubescape-darwin-arm64 .
# Windows (amd64)
GOOS=windows GOARCH=amd64 go build -o kubescape-windows-amd64.exe .
```
---
## Building Docker Images
### Build All Images
```bash
make all
docker buildx build -t kubescape-cli -f build/kubescape-cli.Dockerfile --build-arg="ks_binary=kubescape" --load .
```
3. Build kubescape Docker image
### Build CLI Docker Image
Build a Docker image containing only the Kubescape CLI:
```bash
# First build the binary
make build
# Then build the Docker image
docker buildx build \
-t kubescape-cli:latest \
-f build/kubescape-cli.Dockerfile \
--build-arg="ks_binary=kubescape" \
--load .
```
docker buildx build -t kubescape -f build/Dockerfile --load .
### Build Full Kubescape Image
Build the complete Kubescape image (includes HTTP handler):
```bash
docker buildx build \
-t kubescape:latest \
-f build/Dockerfile \
--load .
```
### Multi-Platform Build
Build for multiple architectures:
```bash
docker buildx build \
-t kubescape:latest \
-f build/Dockerfile \
--platform linux/amd64,linux/arm64 \
--push .
```
---
## Build Options
### Make Targets
| Target | Description |
|--------|-------------|
| `make build` | Build the Kubescape binary |
| `make test` | Run unit tests |
| `make all` | Build everything |
| `make clean` | Remove build artifacts |
### Build Tags
You can use Go build tags to customize the build:
```bash
# Example with build tags
go build -tags "netgo" -o kubescape .
```
### Version Information
To embed version information in the build:
```bash
VERSION=$(git describe --tags --always)
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
COMMIT=$(git rev-parse HEAD)
go build -ldflags "-X main.version=$VERSION -X main.buildDate=$BUILD_DATE -X main.commit=$COMMIT" -o kubescape .
```
---
## Development Setup
### Install Development Dependencies
```bash
# Install golangci-lint for linting
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Install other tools as needed
go mod download
```
### Run Tests
```bash
# Run all tests
make test
# Run tests with coverage
go test -cover ./...
# Run specific package tests
go test ./core/...
```
### Run Linter
```bash
golangci-lint run
```
### Code Formatting
```bash
go fmt ./...
```
---
## Troubleshooting
### Build Fails with "module not found"
```bash
# Update dependencies
go mod tidy
go mod download
```
### CGO-related Errors
If you encounter CGO errors, try building with CGO disabled:
```bash
CGO_ENABLED=0 go build -o kubescape .
```
### Docker Build Fails
Ensure Docker daemon is running and you have sufficient permissions:
```bash
# Check Docker status
docker info
# Run with sudo if needed (Linux)
sudo docker buildx build ...
```
### Out of Memory During Build
For systems with limited memory:
```bash
# Limit Go's memory usage
GOGC=50 go build -o kubescape .
```
---
## Dockerfiles
| File | Description |
|------|-------------|
| `build/Dockerfile` | Full Kubescape image with HTTP handler |
| `build/kubescape-cli.Dockerfile` | Minimal CLI-only image |
---
## Related Documentation
- [Contributing Guide](https://github.com/kubescape/project-governance/blob/main/CONTRIBUTING.md)
- [Architecture](../docs/architecture.md)
- [Getting Started](../docs/getting-started.md)

View File

@@ -1,4 +1,4 @@
FROM gcr.io/distroless/static-debian12:debug-nonroot
FROM gcr.io/distroless/static-debian13:debug-nonroot
USER nonroot
WORKDIR /home/nonroot/

View File

@@ -1,8 +1,6 @@
package config
import (
"context"
"github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v3/core/meta"
v1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
@@ -15,7 +13,7 @@ func getDeleteCmd(ks meta.IKubescape) *cobra.Command {
Short: "Delete cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := ks.DeleteCachedConfig(context.TODO(), &v1.DeleteConfig{}); err != nil {
if err := ks.DeleteCachedConfig(&v1.DeleteConfig{}); err != nil {
logger.L().Fatal(err.Error())
}
},

View File

@@ -1,9 +1,9 @@
package download
import (
"context"
"fmt"
"path/filepath"
"slices"
"strings"
"github.com/kubescape/go-logger"
@@ -12,7 +12,6 @@ import (
"github.com/kubescape/kubescape/v3/core/meta"
v1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
)
var (
@@ -74,11 +73,9 @@ func GetDownloadCmd(ks meta.IKubescape) *cobra.Command {
downloadInfo.Target = args[0]
if len(args) >= 2 {
downloadInfo.Identifier = args[1]
}
if err := ks.Download(context.TODO(), &downloadInfo); err != nil {
if err := ks.Download(&downloadInfo); err != nil {
logger.L().Fatal(err.Error())
}
return nil

View File

@@ -1,14 +1,12 @@
package fix
import (
"context"
"errors"
"fmt"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
@@ -36,7 +34,7 @@ func GetFixCmd(ks meta.IKubescape) *cobra.Command {
}
fixInfo.ReportFile = args[0]
return ks.Fix(context.TODO(), &fixInfo)
return ks.Fix(&fixInfo)
},
}

View File

@@ -1,9 +1,9 @@
package list
import (
"context"
"errors"
"fmt"
"slices"
"strings"
"github.com/kubescape/go-logger"
@@ -12,7 +12,6 @@ import (
"github.com/kubescape/kubescape/v3/core/meta"
v1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
)
var (
@@ -27,7 +26,7 @@ var (
%[1]s list controls
Control documentation:
https://hub.armosec.io/docs/controls
https://kubescape.io/docs/controls/
`, cautils.ExecName())
)
@@ -62,7 +61,7 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
listPolicies.Target = args[0]
if err := ks.List(context.TODO(), &listPolicies); err != nil {
if err := ks.List(&listPolicies); err != nil {
logger.L().Fatal(err.Error())
}
return nil

466
cmd/mcpserver/mcpserver.go Normal file
View File

@@ -0,0 +1,466 @@
package mcpserver
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"time"
"github.com/kubescape/go-logger"
helpersv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers"
"github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1"
spdxv1beta1 "github.com/kubescape/storage/pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type KubescapeMcpserver struct {
s *server.MCPServer
ksClient spdxv1beta1.SpdxV1beta1Interface
}
func createVulnerabilityToolsAndResources(ksServer *KubescapeMcpserver) {
// Tool to list vulnerability manifests
listManifestsTool := mcp.NewTool(
"list_vulnerability_manifests",
mcp.WithDescription("Discover available vulnerability manifests at image and workload levels"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
mcp.WithString("level",
mcp.Description("Type of vulnerability manifests to list"),
mcp.Enum("image", "workload", "both"),
),
)
ksServer.s.AddTool(listManifestsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_vulnerability_manifests", request.Params.Arguments.(map[string]interface{}))
})
listVulnerabilitiesTool := mcp.NewTool(
"list_vulnerabilities_in_manifest",
mcp.WithDescription("List all vulnerabilities in a given manifest"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
mcp.WithString("manifest_name",
mcp.Required(),
mcp.Description("Name of the manifest to list vulnerabilities from"),
),
)
ksServer.s.AddTool(listVulnerabilitiesTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_vulnerabilities_in_manifest", request.Params.Arguments.(map[string]interface{}))
})
listVulnerabilityMatchesForCVE := mcp.NewTool(
"list_vulnerability_matches_for_cve",
mcp.WithDescription("List all vulnerability matches for a given CVE in a given manifest"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
mcp.WithString("manifest_name",
mcp.Required(),
mcp.Description("Name of the manifest to list vulnerabilities from"),
),
mcp.WithString("cve_id",
mcp.Required(),
mcp.Description("ID of the CVE to list matches for"),
),
)
ksServer.s.AddTool(listVulnerabilityMatchesForCVE, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_vulnerability_matches_for_cve", request.Params.Arguments.(map[string]interface{}))
})
vulnerabilityManifestTemplate := mcp.NewResourceTemplate(
"kubescape://vulnerability-manifests/{namespace}/{manifest_name}",
"Vulnerability Manifest",
mcp.WithTemplateDescription("Complete vulnerability manifest either for a specific workload or image. Use 'list_vulnerability_manifests' tool to discover available manifests."),
mcp.WithTemplateMIMEType("application/json"),
)
ksServer.s.AddResourceTemplate(vulnerabilityManifestTemplate, ksServer.ReadResource)
}
func createConfigurationsToolsAndResources(ksServer *KubescapeMcpserver) {
// Tool to list configuration manifests
listConfigsTool := mcp.NewTool(
"list_configuration_security_scan_manifests",
mcp.WithDescription("Discover available security configuration scan results at workload level (this returns a list of manifests, not the scan results themselves, to get the scan results, use the get_configuration_security_scan_manifest tool)"),
mcp.WithString("namespace",
mcp.Description("Filter by namespace (optional)"),
),
)
ksServer.s.AddTool(listConfigsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("list_configuration_security_scan_manifests", request.Params.Arguments.(map[string]interface{}))
})
getConfigDetailsTool := mcp.NewTool(
"get_configuration_security_scan_manifest",
mcp.WithDescription("Get details of a specific security configuration scan result"),
mcp.WithString("namespace",
mcp.Description("Namespace of the manifest (optional, defaults to 'kubescape')"),
),
mcp.WithString("manifest_name",
mcp.Required(),
mcp.Description("Name of the configuration manifest to get details for (get this from the list_configuration_security_scan_manifests tool)"),
),
)
ksServer.s.AddTool(getConfigDetailsTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return ksServer.CallTool("get_configuration_security_scan_manifest", request.Params.Arguments.(map[string]interface{}))
})
configManifestTemplate := mcp.NewResourceTemplate(
"kubescape://configuration-manifests/{namespace}/{manifest_name}",
"Configuration Security Scan Manifest",
mcp.WithTemplateDescription("Complete configuration scan manifest for a specific workload. Use 'list_configuration_security_scan_manifests' tool to discover available manifests."),
mcp.WithTemplateMIMEType("application/json"),
)
ksServer.s.AddResourceTemplate(configManifestTemplate, ksServer.ReadConfigurationResource)
}
func (ksServer *KubescapeMcpserver) ReadResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
uri := request.Params.URI
// Validate the URI and check if it starts with kubescape://vulnerability-manifests/
if !strings.HasPrefix(uri, "kubescape://vulnerability-manifests/") {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
// Verify that the URI is either the CVE list or CVE details
if !strings.HasSuffix(uri, "/cve_list") && !strings.Contains(uri, "/cve_details/") {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
// Split the URI into namespace and manifest name
parts := strings.Split(uri, "/")
if len(parts) != 4 && len(parts) != 5 {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
namespace := parts[1]
manifestName := parts[2]
cveID := ""
if len(parts) == 5 {
cveID = parts[3]
}
// Get the vulnerability manifest
manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace).Get(ctx, manifestName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err)
}
var responseJson []byte
if cveID == "" {
// CVE list
var cveList []v1beta1.Vulnerability
for _, match := range manifest.Spec.Payload.Matches {
cveList = append(cveList, match.Vulnerability)
}
responseJson, err = json.Marshal(cveList)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve list: %s", err)
}
} else {
// CVE details
var match []v1beta1.Match
for _, m := range manifest.Spec.Payload.Matches {
if m.Vulnerability.ID == cveID {
match = append(match, m)
}
}
responseJson, err = json.Marshal(match)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve details: %s", err)
}
}
return []mcp.ResourceContents{mcp.TextResourceContents{
URI: uri,
Text: string(responseJson),
}}, nil
}
func (ksServer *KubescapeMcpserver) ReadConfigurationResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
uri := request.Params.URI
if !strings.HasPrefix(uri, "kubescape://configuration-manifests/") {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
parts := strings.Split(uri[len("kubescape://configuration-manifests/"):], "/")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid URI: %s", uri)
}
namespace := parts[0]
manifestName := parts[1]
manifest, err := ksServer.ksClient.WorkloadConfigurationScans(namespace).Get(ctx, manifestName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get configuration manifest: %s", err)
}
responseJson, err := json.Marshal(manifest)
if err != nil {
return nil, fmt.Errorf("failed to marshal configuration manifest: %s", err)
}
return []mcp.ResourceContents{mcp.TextResourceContents{
URI: uri,
Text: string(responseJson),
}}, nil
}
func (ksServer *KubescapeMcpserver) CallTool(name string, arguments map[string]interface{}) (*mcp.CallToolResult, error) {
switch name {
case "list_vulnerability_manifests":
//namespace, ok := arguments["namespace"]
//if !ok {
// namespace = ""
//}
level, ok := arguments["level"]
if !ok {
level = "both"
}
result := map[string]interface{}{
"vulnerability_manifests": map[string]interface{}{},
}
// Get workload-level manifests
labelSelector := ""
if level == "workload" {
labelSelector = "kubescape.io/context=filtered"
} else if level == "image" {
labelSelector = "kubescape.io/context=non-filtered"
}
var manifests *v1beta1.VulnerabilityManifestList
var err error
if labelSelector == "" {
manifests, err = ksServer.ksClient.VulnerabilityManifests(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{})
} else {
manifests, err = ksServer.ksClient.VulnerabilityManifests(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{
LabelSelector: labelSelector,
})
}
if err != nil {
return nil, err
}
log.Printf("Found %d manifests", len(manifests.Items))
vulnerabilityManifests := []map[string]interface{}{}
for _, manifest := range manifests.Items {
isImageLevel := manifest.Annotations[helpersv1.WlidMetadataKey] == ""
manifestMap := map[string]interface{}{
"type": "workload",
"namespace": manifest.Namespace,
"manifest_name": manifest.Name,
"image-level": isImageLevel,
"workload-level": !isImageLevel,
"image-id": manifest.Annotations[helpersv1.ImageIDMetadataKey],
"image-tag": manifest.Annotations[helpersv1.ImageTagMetadataKey],
"workload-id": manifest.Annotations[helpersv1.WlidMetadataKey],
"workload-container-name": manifest.Annotations[helpersv1.ContainerNameMetadataKey],
"resource_uri": fmt.Sprintf("kubescape://vulnerability-manifests/%s/%s",
manifest.Namespace, manifest.Name),
}
vulnerabilityManifests = append(vulnerabilityManifests, manifestMap)
}
result["vulnerability_manifests"].(map[string]interface{})["manifests"] = vulnerabilityManifests
// Add template information
result["available_templates"] = map[string]string{
"vulnerability_manifest_cve_list": "kubescape://vulnerability-manifests/{namespace}/{manifest_name}/cve_list",
"vulnerability_manifest_cve_details": "kubescape://vulnerability-manifests/{namespace}/{manifest_name}/cve_details/{cve_id}",
}
content, _ := json.Marshal(result)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(content),
},
},
}, nil
case "list_vulnerabilities_in_manifest":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifestName, ok := arguments["manifest_name"]
if !ok {
return nil, fmt.Errorf("manifest_name is required")
}
manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err)
}
var cveList []v1beta1.Vulnerability
for _, match := range manifest.Spec.Payload.Matches {
cveList = append(cveList, match.Vulnerability)
}
responseJson, err := json.Marshal(cveList)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve list: %s", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJson),
},
},
}, nil
case "list_vulnerability_matches_for_cve":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifestName, ok := arguments["manifest_name"]
if !ok {
return nil, fmt.Errorf("manifest_name is required")
}
cveID, ok := arguments["cve_id"]
if !ok {
return nil, fmt.Errorf("cve_id is required")
}
manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err)
}
var match []v1beta1.Match
for _, m := range manifest.Spec.Payload.Matches {
if m.Vulnerability.ID == cveID.(string) {
match = append(match, m)
}
}
responseJson, err := json.Marshal(match)
if err != nil {
return nil, fmt.Errorf("failed to marshal cve details: %s", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJson),
},
},
}, nil
case "list_configuration_security_scan_manifests":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifests, err := ksServer.ksClient.WorkloadConfigurationScans(namespace.(string)).List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil, err
}
log.Printf("Found %d configuration manifests", len(manifests.Items))
configManifests := []map[string]interface{}{}
for _, manifest := range manifests.Items {
item := map[string]interface{}{
"namespace": manifest.Namespace,
"manifest_name": manifest.Name,
"resource_uri": fmt.Sprintf("kubescape://configuration-manifests/%s/%s", manifest.Namespace, manifest.Name),
}
configManifests = append(configManifests, item)
}
result := map[string]interface{}{
"configuration_manifests": map[string]interface{}{
"manifests": configManifests,
},
"available_templates": map[string]string{
"configuration_manifest_details": "kubescape://configuration-manifests/{namespace}/{manifest_name}",
},
}
content, _ := json.Marshal(result)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(content),
},
},
}, nil
case "get_configuration_security_scan_manifest":
namespace, ok := arguments["namespace"]
if !ok {
namespace = "kubescape"
}
manifestName, ok := arguments["manifest_name"]
if !ok {
return nil, fmt.Errorf("manifest_name is required")
}
manifest, err := ksServer.ksClient.WorkloadConfigurationScans(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get configuration manifest: %s", err)
}
responseJson, err := json.Marshal(manifest)
if err != nil {
return nil, fmt.Errorf("failed to marshal configuration manifest: %s", err)
}
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(responseJson),
},
},
}, nil
default:
return nil, fmt.Errorf("unknown tool: %s", name)
}
}
func mcpServerEntrypoint() error {
logger.L().Info("Starting MCP server...")
// Create a kubernetes client and verify it's working
client, err := CreateKsObjectConnection("default", 10*time.Second)
if err != nil {
return fmt.Errorf("failed to create kubernetes client: %v", err)
}
// Create a new MCP server
s := server.NewMCPServer(
"Kubescape MCP Server",
"0.0.1",
server.WithToolCapabilities(false),
server.WithRecovery(),
)
ksServer := &KubescapeMcpserver{
s: s,
ksClient: client,
}
// Creating Kubescape tools and resources
createVulnerabilityToolsAndResources(ksServer)
createConfigurationsToolsAndResources(ksServer)
// Start the server
if err := server.ServeStdio(s); err != nil {
return fmt.Errorf("Server error: %v\n", err)
}
return nil
}
func GetMCPServerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "mcpserver",
Short: "Start the Kubescape MCP server",
Long: `Start the Kubescape MCP server`,
RunE: func(cmd *cobra.Command, args []string) error {
return mcpServerEntrypoint()
},
}
return cmd
}

14
cmd/mcpserver/storage.go Normal file
View File

@@ -0,0 +1,14 @@
package mcpserver
import (
"time"
"github.com/kubescape/kubescape/v3/pkg/ksinit"
spdxv1beta1 "github.com/kubescape/storage/pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1"
)
// CreateKsObjectConnection delegates to the shared ksinit package
func CreateKsObjectConnection(namespace string, maxElapsedTime time.Duration) (spdxv1beta1.SpdxV1beta1Interface, error) {
return ksinit.CreateKsObjectConnection(namespace, maxElapsedTime)
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
"github.com/spf13/cobra"
)

View File

@@ -1,21 +1,17 @@
package patch
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/docker/distribution/reference"
"github.com/distribution/reference"
"github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v3/cmd/shared"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
"github.com/spf13/cobra"
)
@@ -31,6 +27,7 @@ var patchCmdExamples = fmt.Sprintf(`
func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
var patchInfo metav1.PatchInfo
var scanInfo cautils.ScanInfo
var useDefaultMatchers bool
patchCmd := &cobra.Command{
Use: "patch --image <image>:<tag> [flags]",
@@ -52,12 +49,15 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
return err
}
results, err := ks.Patch(context.Background(), &patchInfo, &scanInfo)
// Set the UseDefaultMatchers field in scanInfo
scanInfo.UseDefaultMatchers = useDefaultMatchers
exceedsSeverityThreshold, err := ks.Patch(&patchInfo, &scanInfo)
if err != nil {
return err
}
if imagescan.ExceedsSeverityThreshold(results, imagescan.ParseSeverity(scanInfo.FailThresholdSeverity)) {
if exceedsSeverityThreshold {
shared.TerminateOnExceedingSeverity(&scanInfo, logger.L())
}
@@ -79,6 +79,7 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
patchCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display full report. Default to false")
patchCmd.PersistentFlags().StringVarP(&scanInfo.FailThresholdSeverity, "severity-threshold", "s", "", "Severity threshold is the severity of a vulnerability at which the command fails and returns exit code 1")
patchCmd.PersistentFlags().BoolVarP(&useDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false) for image scanning")
return patchCmd
}

View File

@@ -3,6 +3,8 @@ package patch
import (
"testing"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/mocks"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
@@ -50,3 +52,18 @@ func TestGetPatchCmdWithNonExistentImage(t *testing.T) {
assert.Error(t, err)
assert.Equal(t, expectedErrorMessage, err.Error())
}
func Test_validateImagePatchInfo_EmptyImage(t *testing.T) {
patchInfo := &metav1.PatchInfo{}
err := validateImagePatchInfo(patchInfo)
assert.NotNil(t, err)
assert.Equal(t, "image tag is required", err.Error())
}
func Test_validateImagePatchInfo_Image(t *testing.T) {
patchInfo := &metav1.PatchInfo{
Image: "testing",
}
err := validateImagePatchInfo(patchInfo)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,51 @@
package prerequisites
import (
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/meta"
"github.com/kubescape/sizing-checker/pkg/checks/connectivitycheck"
"github.com/kubescape/sizing-checker/pkg/checks/ebpfcheck"
"github.com/kubescape/sizing-checker/pkg/checks/pvcheck"
"github.com/kubescape/sizing-checker/pkg/checks/sizing"
"github.com/kubescape/sizing-checker/pkg/common"
"github.com/spf13/cobra"
)
func GetPreReqCmd(ks meta.IKubescape) *cobra.Command {
var kubeconfigPath *string
// preReqCmd represents the prerequisites command
preReqCmd := &cobra.Command{
Use: "prerequisites",
Short: "Check prerequisites for installing Kubescape Operator",
Run: func(cmd *cobra.Command, args []string) {
clientSet, inCluster := common.BuildKubeClient(*kubeconfigPath)
if clientSet == nil {
logger.L().Fatal("Could not create kube client. Exiting.")
}
// 1) Collect cluster data
clusterData, err := common.CollectClusterData(ks.Context(), clientSet)
if err != nil {
logger.L().Error("Failed to collect cluster data", helpers.Error(err))
}
// 2) Run checks
sizingResult := sizing.RunSizingChecker(clusterData)
pvResult := pvcheck.RunPVProvisioningCheck(ks.Context(), clientSet, clusterData, inCluster)
connectivityResult := connectivitycheck.RunConnectivityChecks(ks.Context(), clientSet, clusterData, inCluster)
ebpfResult := ebpfcheck.RunEbpfCheck(ks.Context(), clientSet, clusterData, inCluster)
// 3) Build and export the final ReportData
finalReport := common.BuildReportData(clusterData, sizingResult, pvResult, connectivityResult, ebpfResult)
finalReport.InCluster = inCluster
common.GenerateOutput(finalReport, inCluster)
},
}
kubeconfigPath = preReqCmd.PersistentFlags().String("kubeconfig", "", "Path to the kubeconfig file. If not set, in-cluster config is used or $HOME/.kube/config if outside a cluster.")
return preReqCmd
}

View File

@@ -1,6 +1,7 @@
package cmd
import (
"context"
"fmt"
"strings"
@@ -12,8 +13,10 @@ import (
"github.com/kubescape/kubescape/v3/cmd/download"
"github.com/kubescape/kubescape/v3/cmd/fix"
"github.com/kubescape/kubescape/v3/cmd/list"
"github.com/kubescape/kubescape/v3/cmd/mcpserver"
"github.com/kubescape/kubescape/v3/cmd/operator"
"github.com/kubescape/kubescape/v3/cmd/patch"
"github.com/kubescape/kubescape/v3/cmd/prerequisites"
"github.com/kubescape/kubescape/v3/cmd/scan"
"github.com/kubescape/kubescape/v3/cmd/update"
"github.com/kubescape/kubescape/v3/cmd/vap"
@@ -22,7 +25,6 @@ import (
"github.com/kubescape/kubescape/v3/core/cautils/getter"
"github.com/kubescape/kubescape/v3/core/core"
"github.com/kubescape/kubescape/v3/core/meta"
"github.com/spf13/cobra"
)
@@ -42,8 +44,8 @@ var ksExamples = fmt.Sprintf(`
%[1]s config view
`, cautils.ExecName())
func NewDefaultKubescapeCommand() *cobra.Command {
ks := core.NewKubescape()
func NewDefaultKubescapeCommand(ctx context.Context) *cobra.Command {
ks := core.NewKubescape(ctx)
return getRootCmd(ks)
}
@@ -51,7 +53,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd := &cobra.Command{
Use: "kubescape",
Short: "Kubescape is a tool for testing Kubernetes security posture. Docs: https://hub.armosec.io/docs",
Short: "Kubescape is a tool for testing Kubernetes security posture. Docs: https://kubescape.io/docs/",
Example: ksExamples,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
k8sinterface.SetClusterContextName(rootInfo.KubeContext)
@@ -84,8 +86,6 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd.PersistentFlags().StringVarP(&rootInfo.Logger, "logger", "l", helpers.InfoLevel.String(), fmt.Sprintf("Logger level. Supported: %s [$KS_LOGGER]", strings.Join(helpers.SupportedLevels(), "/")))
rootCmd.PersistentFlags().StringVar(&rootInfo.CacheDir, "cache-dir", getter.DefaultLocalStore, "Cache directory [$KS_CACHE_DIR]")
rootCmd.PersistentFlags().BoolVarP(&rootInfo.DisableColor, "disable-color", "", false, "Disable color output for logging")
rootCmd.PersistentFlags().BoolVarP(&rootInfo.EnableColor, "enable-color", "", false, "Force enable color output for logging")
rootCmd.PersistentFlags().StringVarP(&rootInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context")
// Supported commands
@@ -93,13 +93,15 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd.AddCommand(download.GetDownloadCmd(ks))
rootCmd.AddCommand(list.GetListCmd(ks))
rootCmd.AddCommand(completion.GetCompletionCmd())
rootCmd.AddCommand(version.GetVersionCmd())
rootCmd.AddCommand(version.GetVersionCmd(ks))
rootCmd.AddCommand(config.GetConfigCmd(ks))
rootCmd.AddCommand(update.GetUpdateCmd())
rootCmd.AddCommand(update.GetUpdateCmd(ks))
rootCmd.AddCommand(fix.GetFixCmd(ks))
rootCmd.AddCommand(patch.GetPatchCmd(ks))
rootCmd.AddCommand(vap.GetVapHelperCmd())
rootCmd.AddCommand(operator.GetOperatorCmd(ks))
rootCmd.AddCommand(prerequisites.GetPreReqCmd(ks))
rootCmd.AddCommand(mcpserver.GetMCPServerCmd())
// deprecated commands
rootCmd.AddCommand(&cobra.Command{
@@ -114,7 +116,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
return rootCmd
}
func Execute() error {
ks := NewDefaultKubescapeCommand()
func Execute(ctx context.Context) error {
ks := NewDefaultKubescapeCommand(ctx)
return ks.Execute()
}

24
cmd/root_test.go Normal file
View File

@@ -0,0 +1,24 @@
package cmd
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewDefaultKubescapeCommand(t *testing.T) {
t.Run("NewDefaultKubescapeCommand", func(t *testing.T) {
cmd := NewDefaultKubescapeCommand(context.Background())
assert.NotNil(t, cmd)
})
}
func TestExecute(t *testing.T) {
t.Run("Execute", func(t *testing.T) {
err := Execute(context.Background())
if err != nil {
assert.EqualErrorf(t, err, "unknown command \"^\\\\QTestExecute\\\\E$\" for \"kubescape\"", err.Error())
}
})
}

View File

@@ -14,14 +14,10 @@ import (
"github.com/kubescape/go-logger/zaplogger"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/cautils/getter"
"github.com/mattn/go-isatty"
)
func initLogger() {
logger.DisableColor(rootInfo.DisableColor)
logger.EnableColor(rootInfo.EnableColor)
if rootInfo.LoggerName == "" {
if l := os.Getenv("KS_LOGGER_NAME"); l != "" {
rootInfo.LoggerName = l
@@ -35,8 +31,8 @@ func initLogger() {
}
logger.InitLogger(rootInfo.LoggerName)
}
func initLoggerLevel() {
if rootInfo.Logger == helpers.InfoLevel.String() {
} else if l := os.Getenv("KS_LOGGER"); l != "" {

View File

@@ -1,20 +1,17 @@
package scan
import (
"context"
"fmt"
"io"
"os"
"strings"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/cmd/shared"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/spf13/cobra"
)
@@ -32,7 +29,7 @@ var (
Run '%[1]s list controls' for the list of supported controls
Control documentation:
https://hub.armosec.io/docs/controls
https://kubescape.io/docs/controls/
`, cautils.ExecName())
)
@@ -98,12 +95,11 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
return err
}
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
results, err := ks.Scan(scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err := results.HandleResults(ctx); err != nil {
if err := results.HandleResults(ks.Context(), scanInfo); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {

View File

@@ -1,25 +1,22 @@
package scan
import (
"context"
"errors"
"fmt"
"io"
"os"
"slices"
"strings"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
reporthandlingapis "github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"golang.org/x/exp/slices"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/cmd/shared"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/cautils/getter"
"github.com/kubescape/kubescape/v3/core/meta"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
reporthandlingapis "github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/spf13/cobra"
)
@@ -115,13 +112,12 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
scanInfo.SetPolicyIdentifiers(frameworks, apisv1.KindFramework)
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
results, err := ks.Scan(scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(ctx); err != nil {
if err = results.HandleResults(ks.Context(), scanInfo); err != nil {
logger.L().Fatal(err.Error())
}

View File

@@ -1,7 +1,6 @@
package scan
import (
"context"
"fmt"
"github.com/kubescape/go-logger"
@@ -9,8 +8,6 @@ import (
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/meta"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
"github.com/spf13/cobra"
)
@@ -25,12 +22,18 @@ var (
# Scan the 'nginx' image and see the full report
%[1]s scan image "nginx" -v
# Scan the 'nginx' image and use exceptions
%[1]s scan image "nginx" --exceptions exceptions.json
`, cautils.ExecName())
)
// getImageCmd returns the scan image command
func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
var imgCredentials shared.ImageCredentials
var exceptions string
var useDefaultMatchers bool
cmd := &cobra.Command{
Use: "image <image>:<tag> [flags]",
Short: "Scan an image for vulnerabilities",
@@ -51,17 +54,19 @@ func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command
}
imgScanInfo := &metav1.ImageScanInfo{
Image: args[0],
Username: imgCredentials.Username,
Password: imgCredentials.Password,
Image: args[0],
Username: imgCredentials.Username,
Password: imgCredentials.Password,
Exceptions: exceptions,
UseDefaultMatchers: useDefaultMatchers,
}
results, err := ks.ScanImage(context.Background(), imgScanInfo, scanInfo)
exceedsSeverityThreshold, err := ks.ScanImage(imgScanInfo, scanInfo)
if err != nil {
return err
}
if imagescan.ExceedsSeverityThreshold(results, imagescan.ParseSeverity(scanInfo.FailThresholdSeverity)) {
if exceedsSeverityThreshold {
shared.TerminateOnExceedingSeverity(scanInfo, logger.L())
}
@@ -69,8 +74,11 @@ func getImageCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command
},
}
// The exceptions flag
cmd.PersistentFlags().StringVarP(&exceptions, "exceptions", "", "", "Path to the exceptions file")
cmd.PersistentFlags().StringVarP(&imgCredentials.Username, "username", "u", "", "Username for registry login")
cmd.PersistentFlags().StringVarP(&imgCredentials.Password, "password", "p", "", "Password for registry login")
cmd.PersistentFlags().BoolVarP(&useDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false)")
return cmd
}

View File

@@ -1,7 +1,6 @@
package scan
import (
"context"
"flag"
"fmt"
"strings"
@@ -64,6 +63,8 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
},
}
scanInfo.TriggeredByCLI = true
scanCmd.PersistentFlags().StringVarP(&scanInfo.AccountID, "account", "", "", "Kubescape SaaS account ID. Default will load account ID from cache")
scanCmd.PersistentFlags().StringVarP(&scanInfo.AccessKey, "access-key", "", "", "Kubescape SaaS access key. Default will load access key from cache")
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
@@ -91,6 +92,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().BoolVarP(&scanInfo.PrintAttackTree, "print-attack-tree", "", false, "Print attack tree")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.EnableRegoPrint, "enable-rego-prints", "", false, "Enable sending to rego prints to the logs (use with debug log level: -l debug)")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.ScanImages, "scan-images", "", false, "Scan resources images")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.UseDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false) for image scanning")
scanCmd.PersistentFlags().MarkDeprecated("fail-threshold", "use '--compliance-threshold' flag instead. Flag will be removed at 1.Dec.2023")
scanCmd.PersistentFlags().MarkDeprecated("create-account", "Create account is no longer supported. In case of a missing Account ID and a configured backend server, a new account id will be generated automatically by Kubescape. Feel free to contact the Kubescape maintainers for more information.")
@@ -133,15 +135,12 @@ func setSecurityViewScanInfo(args []string, scanInfo *cautils.ScanInfo) {
}
func securityScan(scanInfo cautils.ScanInfo, ks meta.IKubescape) error {
ctx := context.TODO()
results, err := ks.Scan(ctx, &scanInfo)
results, err := ks.Scan(&scanInfo)
if err != nil {
return err
}
if err = results.HandleResults(ctx); err != nil {
if err = results.HandleResults(ks.Context(), &scanInfo); err != nil {
return err
}

View File

@@ -2,20 +2,19 @@ package scan
import (
"context"
"os"
"reflect"
"testing"
"time"
"github.com/kubescape/go-logger/helpers"
"github.com/stretchr/testify/assert"
"github.com/kubescape/kubescape/v3/cmd/shared"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/mocks"
v1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"os"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
func TestExceedsSeverity(t *testing.T) {
@@ -188,20 +187,23 @@ type spyLogger struct {
setItems []spyLogMessage
}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
var _ helpers.ILogger = &spyLogger{}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) TimedWrapper(funcName string, timeout time.Duration, task func()) {}
func (l *spyLogger) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]

View File

@@ -1,7 +1,6 @@
package scan
import (
"context"
"errors"
"fmt"
"strings"
@@ -11,7 +10,6 @@ import (
"github.com/kubescape/kubescape/v3/core/meta"
v1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/spf13/cobra"
)
@@ -67,13 +65,12 @@ func getWorkloadCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comma
setWorkloadScanInfo(scanInfo, kind, name)
// todo: add api version if provided
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
results, err := ks.Scan(scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(ctx); err != nil {
if err = results.HandleResults(ks.Context(), scanInfo); err != nil {
logger.L().Fatal(err.Error())
}

View File

@@ -94,3 +94,17 @@ func TestGetWorkloadCmd_ChartPathAndFilePathEmpty(t *testing.T) {
expectedErrorMessage = "invalid workload identifier"
assert.Equal(t, expectedErrorMessage, err.Error())
}
func Test_parseWorkloadIdentifierString_Empty(t *testing.T) {
t.Run("empty identifier", func(t *testing.T) {
_, _, err := parseWorkloadIdentifierString("")
assert.Error(t, err)
})
}
func Test_parseWorkloadIdentifierString_NoError(t *testing.T) {
t.Run("valid identifier", func(t *testing.T) {
_, _, err := parseWorkloadIdentifierString("default/Deployment")
assert.NoError(t, err)
})
}

View File

@@ -5,6 +5,7 @@ import (
"os"
"reflect"
"testing"
"time"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
@@ -20,20 +21,23 @@ type spyLogger struct {
setItems []spyLogMessage
}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
var _ helpers.ILogger = &spyLogger{}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Start(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopSuccess(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) StopError(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) TimedWrapper(funcName string, timeout time.Duration, task func()) {}
func (l *spyLogger) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]

View File

@@ -5,10 +5,11 @@ package update
// kubescape update
import (
"context"
"fmt"
"strings"
"github.com/kubescape/kubescape/v3/core/meta"
"github.com/kubescape/backend/pkg/versioncheck"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -25,17 +26,18 @@ var updateCmdExamples = fmt.Sprintf(`
%[1]s update
`, cautils.ExecName())
func GetUpdateCmd() *cobra.Command {
func GetUpdateCmd(ks meta.IKubescape) *cobra.Command {
updateCmd := &cobra.Command{
Use: "update",
Short: "Update to latest release version",
Long: ``,
Example: updateCmdExamples,
RunE: func(_ *cobra.Command, args []string) error {
ctx := context.TODO()
v := versioncheck.NewVersionCheckHandler()
versionCheckRequest := versioncheck.NewVersionCheckRequest("", versioncheck.BuildNumber, "", "", "update", nil)
v.CheckLatestVersion(ctx, versionCheckRequest)
if err := v.CheckLatestVersion(ks.Context(), versionCheckRequest); err != nil {
return err
}
//Checking the user's version of kubescape to the latest release
if versioncheck.BuildNumber == "" || strings.Contains(versioncheck.BuildNumber, "rc") {

18
cmd/update/update_test.go Normal file
View File

@@ -0,0 +1,18 @@
package update
import (
"context"
"testing"
"github.com/kubescape/kubescape/v3/core/core"
"github.com/stretchr/testify/assert"
)
func TestGetUpdateCmd(t *testing.T) {
ks := core.NewKubescape(context.TODO())
cmd := GetUpdateCmd(ks)
assert.NotNil(t, cmd)
err := cmd.RunE(cmd, []string{})
assert.Nil(t, err)
}

View File

@@ -9,11 +9,10 @@ import (
"github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v3/core/cautils"
"sigs.k8s.io/yaml"
"github.com/spf13/cobra"
admissionv1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
)
var vapHelperCmdExamples = fmt.Sprintf(`
@@ -221,9 +220,11 @@ func createPolicyBinding(bindingName string, policyName string, action string, p
}
policyBinding.Spec.ValidationActions = []admissionv1.ValidationAction{admissionv1.ValidationAction(action)}
paramAction := admissionv1.DenyAction
if paramRefName != "" {
policyBinding.Spec.ParamRef = &admissionv1.ParamRef{
Name: paramRefName,
Name: paramRefName,
ParameterNotFoundAction: &paramAction,
}
}
// Marshal the policy binding to YAML

View File

@@ -1,26 +1,26 @@
package version
import (
"context"
"fmt"
"github.com/kubescape/kubescape/v3/core/meta"
"github.com/kubescape/backend/pkg/versioncheck"
"github.com/spf13/cobra"
)
func GetVersionCmd() *cobra.Command {
func GetVersionCmd(ks meta.IKubescape) *cobra.Command {
versionCmd := &cobra.Command{
Use: "version",
Short: "Get current version",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.TODO()
v := versioncheck.NewIVersionCheckHandler(ctx)
versionCheckRequest := versioncheck.NewVersionCheckRequest("", versioncheck.BuildNumber, "", "", "version", nil)
v.CheckLatestVersion(ctx, versionCheckRequest)
fmt.Fprintf(cmd.OutOrStdout(),
v := versioncheck.NewIVersionCheckHandler(ks.Context())
_ = v.CheckLatestVersion(ks.Context(), versioncheck.NewVersionCheckRequest("", versioncheck.BuildNumber, "", "", "version", nil))
_, _ = fmt.Fprintf(cmd.OutOrStdout(),
"Your current version is: %s\n",
versionCheckRequest.ClientVersion,
versioncheck.BuildNumber,
)
return nil
},

View File

@@ -2,9 +2,12 @@ package version
import (
"bytes"
"context"
"io"
"testing"
"github.com/kubescape/kubescape/v3/core/core"
"github.com/kubescape/backend/pkg/versioncheck"
"github.com/stretchr/testify/assert"
)
@@ -17,7 +20,7 @@ func TestGetVersionCmd(t *testing.T) {
}{
{
name: "Undefined Build Number",
buildNumber: "",
buildNumber: "unknown",
want: "Your current version is: unknown\n",
},
{
@@ -30,7 +33,8 @@ func TestGetVersionCmd(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
versioncheck.BuildNumber = tt.buildNumber
if cmd := GetVersionCmd(); cmd != nil {
ks := core.NewKubescape(context.TODO())
if cmd := GetVersionCmd(ks); cmd != nil {
buf := bytes.NewBufferString("")
cmd.SetOut(buf)
cmd.Execute()

View File

@@ -1,14 +1,248 @@
# Kubescape core package
# Kubescape Core Package
The `core` package provides the main Kubescape scanning engine as a Go library, allowing you to integrate Kubescape security scanning directly into your applications.
## Table of Contents
- [Installation](#installation)
- [Quick Start](#quick-start)
- [API Reference](#api-reference)
- [Examples](#examples)
- [Configuration Options](#configuration-options)
---
## Installation
```bash
go get github.com/kubescape/kubescape/v3/core
```
---
## Quick Start
```go
package main
// initialize kubescape
ks := core.NewKubescape()
import (
"context"
"fmt"
"log"
// scan cluster
results, err := ks.Scan(&cautils.ScanInfo{})
"github.com/kubescape/kubescape/v3/core"
"github.com/kubescape/kubescape/v3/core/cautils"
)
// convert scan results to json
jsonRes, err := results.ToJson()
func main() {
ctx := context.Background()
```
// Initialize Kubescape
ks := core.NewKubescape(ctx)
// Configure scan
scanInfo := &cautils.ScanInfo{
// Scan the current cluster
ScanAll: true,
}
// Run scan
results, err := ks.Scan(scanInfo)
if err != nil {
log.Fatalf("Scan failed: %v", err)
}
// Convert results to JSON
jsonRes, err := results.ToJson()
if err != nil {
log.Fatalf("Failed to convert results: %v", err)
}
fmt.Println(string(jsonRes))
}
```
---
## API Reference
### Creating a Kubescape Instance
```go
// Create with context
ks := core.NewKubescape(ctx)
```
### Scanning
```go
// Scan with configuration
results, err := ks.Scan(scanInfo)
```
### Listing Frameworks and Controls
```go
// List available policies
err := ks.List(listPolicies)
```
### Downloading Artifacts
```go
// Download for offline use
err := ks.Download(downloadInfo)
```
### Image Scanning
```go
// Scan container image
exceedsSeverity, err := ks.ScanImage(imgScanInfo, scanInfo)
```
### Fixing Misconfigurations
```go
// Apply fixes to manifests
err := ks.Fix(fixInfo)
```
---
## Examples
### Scan a Specific Framework
```go
scanInfo := &cautils.ScanInfo{}
scanInfo.SetPolicyIdentifiers([]string{"nsa"}, "framework")
results, err := ks.Scan(scanInfo)
```
### Scan Specific Namespaces
```go
scanInfo := &cautils.ScanInfo{
IncludeNamespaces: "production,staging",
}
results, err := ks.Scan(scanInfo)
```
### Scan Local YAML Files
```go
scanInfo := &cautils.ScanInfo{
InputPatterns: []string{"/path/to/manifests"},
}
scanInfo.SetScanType(cautils.ScanTypeRepo)
results, err := ks.Scan(scanInfo)
```
### Export Results to Different Formats
```go
results, _ := ks.Scan(scanInfo)
// JSON
jsonData, _ := results.ToJson()
// Get summary
summary := results.GetData().Report.SummaryDetails
fmt.Printf("Compliance Score: %.2f%%\n", summary.ComplianceScore)
```
### Scan with Compliance Threshold
```go
scanInfo := &cautils.ScanInfo{
ComplianceThreshold: 80.0, // Fail if below 80%
}
results, err := ks.Scan(scanInfo)
if err != nil {
// Handle scan failure
}
// Check if threshold was exceeded
if results.GetData().Report.SummaryDetails.ComplianceScore < scanInfo.ComplianceThreshold {
log.Fatal("Compliance score below threshold")
}
```
---
## Configuration Options
### ScanInfo Fields
| Field | Type | Description |
|-------|------|-------------|
| `AccountID` | string | Kubescape SaaS account ID |
| `AccessKey` | string | Kubescape SaaS access key |
| `InputPatterns` | []string | Paths to scan (files, directories, URLs) |
| `ExcludedNamespaces` | string | Comma-separated namespaces to exclude |
| `IncludeNamespaces` | string | Comma-separated namespaces to include |
| `Format` | string | Output format (json, junit, sarif, etc.) |
| `Output` | string | Output file path |
| `VerboseMode` | bool | Show all resources in output |
| `FailThreshold` | float32 | Fail threshold percentage |
| `ComplianceThreshold` | float32 | Compliance threshold percentage |
| `UseExceptions` | string | Path to exceptions file |
| `UseArtifactsFrom` | string | Path to offline artifacts |
| `Submit` | bool | Submit results to SaaS |
| `Local` | bool | Keep results local (don't submit) |
---
## Error Handling
```go
results, err := ks.Scan(scanInfo)
if err != nil {
switch {
case errors.Is(err, context.DeadlineExceeded):
log.Fatal("Scan timed out")
case errors.Is(err, context.Canceled):
log.Fatal("Scan was canceled")
default:
log.Fatalf("Scan error: %v", err)
}
}
```
---
## Thread Safety
The Kubescape instance is safe for concurrent use. You can run multiple scans in parallel:
```go
var wg sync.WaitGroup
for _, ns := range namespaces {
wg.Add(1)
go func(namespace string) {
defer wg.Done()
scanInfo := &cautils.ScanInfo{
IncludeNamespaces: namespace,
}
results, _ := ks.Scan(scanInfo)
// Process results...
}(ns)
}
wg.Wait()
```
---
## Related Documentation
- [CLI Reference](../docs/cli-reference.md)
- [Getting Started Guide](../docs/getting-started.md)
- [Architecture](../docs/architecture.md)

View File

@@ -7,8 +7,6 @@ import (
"path/filepath"
"regexp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/google/uuid"
v1 "github.com/kubescape/backend/pkg/client/v1"
"github.com/kubescape/backend/pkg/servicediscovery"
@@ -19,14 +17,14 @@ import (
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v3/core/cautils/getter"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
configFileName string = "config"
kubescapeNamespace string = "kubescape"
kubescapeConfigMapName string = "kubescape-config" // deprecated - for backward compatibility
kubescapeCloudConfigMapName string = "ks-cloud-config" // deprecated - for backward compatibility
kubescapeConfigMapName string = "kubescape-config" // deprecated - for backward compatibility
cloudConfigMapLabelSelector string = "kubescape.io/infra=config"
credsLabelSelectors string = "kubescape.io/infra=credentials" //nolint:gosec
@@ -208,6 +206,8 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, accountID, accessKey, clu
loadConfigFromFile(c.configObj)
}
loadUrlsFromFile(c.configObj)
// second, load urls from config map
c.updateConfigEmptyFieldsFromKubescapeConfigMap()
@@ -271,15 +271,12 @@ func (c *ClusterConfig) updateConfigEmptyFieldsFromKubescapeConfigMap() error {
return err
}
var ksConfigMap *corev1.ConfigMap
var urlsConfigMap *corev1.ConfigMap
if len(configMaps.Items) == 0 {
// try to find configmaps by name (for backward compatibility)
ksConfigMap, _ = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), kubescapeConfigMapName, metav1.GetOptions{})
urlsConfigMap, _ = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), kubescapeCloudConfigMapName, metav1.GetOptions{})
} else {
// use the first configmap with the label
ksConfigMap = &configMaps.Items[0]
urlsConfigMap = &configMaps.Items[0]
}
if ksConfigMap != nil {
@@ -292,30 +289,6 @@ func (c *ClusterConfig) updateConfigEmptyFieldsFromKubescapeConfigMap() error {
}
}
if urlsConfigMap != nil {
if jsonConf, ok := urlsConfigMap.Data["services"]; ok {
services, err := servicediscovery.GetServices(
servicediscoveryv2.NewServiceDiscoveryStreamV2([]byte(jsonConf)),
)
if err != nil {
// try to parse as v1
services, err = servicediscovery.GetServices(
servicediscoveryv1.NewServiceDiscoveryStreamV1([]byte(jsonConf)),
)
if err != nil {
return err
}
}
if services.GetApiServerUrl() != "" {
c.configObj.CloudAPIURL = services.GetApiServerUrl()
}
if services.GetReportReceiverHttpUrl() != "" {
c.configObj.CloudReportURL = services.GetReportReceiverHttpUrl()
}
}
}
return err
}
@@ -398,7 +371,7 @@ func (c *ClusterConfig) updateConfigData(configMap *corev1.ConfigMap) {
func loadConfigFromFile(configObj *ConfigObj) error {
dat, err := os.ReadFile(ConfigFileFullPath())
if err != nil {
return err
return nil // no config file
}
return readConfig(dat, configObj)
}
@@ -414,6 +387,32 @@ func readConfig(dat []byte, configObj *ConfigObj) error {
return nil
}
func loadUrlsFromFile(obj *ConfigObj) error {
dat, err := os.ReadFile("/etc/config/services.json")
if err != nil {
return nil // no config file
}
services, err := servicediscovery.GetServices(
servicediscoveryv2.NewServiceDiscoveryStreamV2(dat),
)
if err != nil {
// try to parse as v1
services, err = servicediscovery.GetServices(
servicediscoveryv1.NewServiceDiscoveryStreamV1(dat),
)
if err != nil {
return err
}
}
if services.GetApiServerUrl() != "" {
obj.CloudAPIURL = services.GetApiServerUrl()
}
if services.GetReportReceiverHttpUrl() != "" {
obj.CloudReportURL = services.GetReportReceiverHttpUrl()
}
return nil
}
func DeleteConfigFile() error {
return os.Remove(ConfigFileFullPath())
}

View File

@@ -4,7 +4,10 @@ import (
"context"
"sort"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/sbom"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
@@ -20,8 +23,14 @@ type K8SResources map[string][]string
type ExternalResources map[string][]string
type ImageScanData struct {
PresenterConfig *models.PresenterConfig
Image string
Context pkg.Context
IgnoredMatches []match.IgnoredMatch
Image string
Matches match.Matches
Packages []pkg.Package
RemainingMatches *match.Matches
SBOM *sbom.SBOM
VulnerabilityProvider vulnerability.Provider
}
type ScanTypes string
@@ -59,6 +68,7 @@ type OPASessionObj struct {
SingleResourceScan workloadinterface.IWorkload // single resource scan
TopWorkloadsByScore []reporthandling.IResource
TemplateMapping map[string]MappingNodes // Map chart obj to template (only for rendering from path)
TriggeredByCLI bool
}
func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework, k8sResources K8SResources, scanInfo *ScanInfo) *OPASessionObj {
@@ -75,6 +85,7 @@ func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework
SessionID: scanInfo.ScanID,
Metadata: scanInfoToScanMetadata(ctx, scanInfo),
OmitRawResources: scanInfo.OmitRawResources,
TriggeredByCLI: scanInfo.TriggeredByCLI,
TemplateMapping: make(map[string]MappingNodes),
}
}

View File

@@ -1,12 +1,11 @@
package cautils
import (
"golang.org/x/mod/semver"
"github.com/kubescape/backend/pkg/versioncheck"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"golang.org/x/mod/semver"
)
func NewPolicies() *Policies {

View File

@@ -7,16 +7,14 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
"golang.org/x/exp/slices"
"github.com/kubescape/go-logger"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
"gopkg.in/yaml.v3"
)

View File

@@ -5,10 +5,8 @@ import (
"strings"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
"github.com/kubescape/regolibrary/v2/gitregostore"
)

View File

@@ -1,6 +1,9 @@
package cautils
import (
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
@@ -9,11 +12,15 @@ import (
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
helmchart "helm.sh/helm/v3/pkg/chart"
helmloader "helm.sh/helm/v3/pkg/chart/loader"
helmchartutil "helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli"
helmdownloader "helm.sh/helm/v3/pkg/downloader"
helmengine "helm.sh/helm/v3/pkg/engine"
helmgetter "helm.sh/helm/v3/pkg/getter"
helmregistry "helm.sh/helm/v3/pkg/registry"
"k8s.io/client-go/util/homedir"
)
type HelmChart struct {
@@ -25,7 +32,51 @@ func IsHelmDirectory(path string) (bool, error) {
return helmchartutil.IsChartDir(path)
}
// newRegistryClient creates a Helm registry client for chart authentication
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLS, plainHTTP bool, username, password string) (*helmregistry.Client, error) {
// Basic client options with debug disabled
opts := []helmregistry.ClientOption{
helmregistry.ClientOptDebug(false),
helmregistry.ClientOptWriter(io.Discard),
}
// Add TLS certificates if provided
if certFile != "" && keyFile != "" {
opts = append(opts, helmregistry.ClientOptCredentialsFile(certFile))
}
// Add CA certificate if provided
if caFile != "" {
opts = append(opts, helmregistry.ClientOptCredentialsFile(caFile))
}
// Enable plain HTTP if needed
if insecureSkipTLS {
opts = append(opts, helmregistry.ClientOptPlainHTTP())
}
registryClient, err := helmregistry.NewClient(opts...)
if err != nil {
return nil, err
}
return registryClient, nil
}
// defaultKeyring returns the default GPG keyring path for chart verification
func defaultKeyring() string {
if v, ok := os.LookupEnv("GNUPGHOME"); ok {
return filepath.Join(v, "pubring.gpg")
}
return filepath.Join(homedir.HomeDir(), ".gnupg", "pubring.gpg")
}
func NewHelmChart(path string) (*HelmChart, error) {
// Build chart dependencies before loading if Chart.lock exists
if err := buildDependencies(path); err != nil {
logger.L().Warning("Failed to build chart dependencies", helpers.String("path", path), helpers.Error(err))
}
chart, err := helmloader.Load(path)
if err != nil {
return nil, err
@@ -37,6 +88,35 @@ func NewHelmChart(path string) (*HelmChart, error) {
}, nil
}
// buildDependencies builds chart dependencies using the downloader manager
func buildDependencies(chartPath string) error {
// Create registry client for authentication
registryClient, err := newRegistryClient("", "", "", false, false, "", "")
if err != nil {
return fmt.Errorf("failed to create registry client: %w", err)
}
// Create downloader manager with required configuration
settings := cli.New()
manager := &helmdownloader.Manager{
Out: io.Discard, // Suppress output during scanning
ChartPath: chartPath,
Keyring: defaultKeyring(),
SkipUpdate: false, // Allow updates to get latest dependencies
Getters: helmgetter.All(settings),
RegistryClient: registryClient,
Debug: false,
}
// Build dependencies from Chart.lock file
err = manager.Build()
if e, ok := err.(helmdownloader.ErrRepoNotFound); ok {
return fmt.Errorf("%s. Please add missing repos via 'helm repo add'", e.Error())
}
return err
}
func (hc *HelmChart) GetName() string {
return hc.chart.Name()
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
"sigs.k8s.io/kustomize/api/krusty"
"sigs.k8s.io/kustomize/kyaml/filesys"
)

View File

@@ -4,10 +4,9 @@ import (
"encoding/json"
"time"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/google/uuid"
"github.com/kubescape/k8s-interface/workloadinterface"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/kubescape/rbac-utils/rbacscanner"
"github.com/kubescape/rbac-utils/rbacutils"
)

View File

@@ -10,8 +10,6 @@ type RootInfo struct {
Logger string // logger level
LoggerName string // logger name ("pretty"/"zap"/"none")
CacheDir string // cached dir
DisableColor bool // Disable Color
EnableColor bool // Force enable Color
DiscoveryServerURL string // Discovery Server URL (See https://github.com/kubescape/backend/tree/main/pkg/servicediscovery)
KubeContext string // context name
}

View File

@@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
"github.com/google/uuid"
"github.com/kubescape/backend/pkg/versioncheck"
giturl "github.com/kubescape/go-git-url"
"github.com/kubescape/go-logger"
@@ -18,8 +19,6 @@ import (
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/google/uuid"
)
type ScanningContext string
@@ -135,8 +134,10 @@ type ScanInfo struct {
EnableRegoPrint bool // true if print rego
ScanObject *objectsenvelopes.ScanObject // identifies a single resource (k8s object) to be scanned
IsDeletedScanObject bool // indicates whether the ScanObject is a deleted K8S resource
TriggeredByCLI bool // indicates whether the scan was triggered by the CLI
ScanType ScanTypes
ScanImages bool
UseDefaultMatchers bool
ChartPath string
FilePath string
scanningContext *ScanningContext

View File

@@ -1,7 +1,6 @@
package core
import (
"context"
"fmt"
"github.com/kubescape/kubescape/v3/core/cautils"
@@ -35,8 +34,8 @@ func (ks *Kubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
return nil
}
func (ks *Kubescape) DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error {
func (ks *Kubescape) DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error {
tenant := cautils.GetTenantConfig("", "", "", "", nil) // change k8sinterface
return tenant.DeleteCachedConfig(ctx)
return tenant.DeleteCachedConfig(ks.Context())
}

View File

@@ -44,12 +44,12 @@ func DownloadSupportCommands() []string {
return commands
}
func (ks *Kubescape) Download(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
func (ks *Kubescape) Download(downloadInfo *metav1.DownloadInfo) error {
setPathAndFilename(downloadInfo)
if err := os.MkdirAll(downloadInfo.Path, os.ModePerm); err != nil {
return err
}
if err := downloadArtifact(ctx, downloadInfo, downloadFunc); err != nil {
if err := downloadArtifact(ks.Context(), downloadInfo, downloadFunc); err != nil {
return err
}
return nil

View File

@@ -1,13 +1,11 @@
package core
import (
"context"
"fmt"
"strings"
"github.com/kubescape/go-logger"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/fixhandler"
)
@@ -17,14 +15,14 @@ const (
confirmationQuestion = "Would you like to apply the changes to the files above? [y|n]: "
)
func (ks *Kubescape) Fix(ctx context.Context, fixInfo *metav1.FixInfo) error {
func (ks *Kubescape) Fix(fixInfo *metav1.FixInfo) error {
logger.L().Info("Reading report file...")
handler, err := fixhandler.NewFixHandler(fixInfo)
if err != nil {
return err
}
resourcesToFix := handler.PrepareResourcesToFix(ctx)
resourcesToFix := handler.PrepareResourcesToFix(ks.Context())
if len(resourcesToFix) == 0 {
logger.L().Info(noResourcesToFix)
@@ -43,12 +41,12 @@ func (ks *Kubescape) Fix(ctx context.Context, fixInfo *metav1.FixInfo) error {
return nil
}
updatedFilesCount, errors := handler.ApplyChanges(ctx, resourcesToFix)
updatedFilesCount, errors := handler.ApplyChanges(ks.Context(), resourcesToFix)
logger.L().Info(fmt.Sprintf("Fixed resources in %d files.", updatedFilesCount))
if len(errors) > 0 {
for _, err := range errors {
logger.L().Ctx(ctx).Warning(err.Error())
logger.L().Ctx(ks.Context()).Warning(err.Error())
}
return fmt.Errorf("Failed to fix some resources, check the logs for more details")
}

View File

@@ -33,10 +33,11 @@ func TestUserConfirmed(t *testing.T) {
for _, tt := range tests {
t.Run(string(tt.input), func(t *testing.T) {
originalStdin := os.Stdin
r, w, _ := os.Pipe()
os.Stdin = r
defer func() {
os.Stdin = os.Stdin
os.Stdin = originalStdin
}()
go func() {

View File

@@ -1,10 +1,12 @@
package core
import (
"context"
"encoding/json"
"fmt"
"os"
"regexp"
"strings"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v3/core/cautils"
ksmetav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
@@ -12,39 +14,199 @@ import (
"github.com/kubescape/kubescape/v3/pkg/imagescan"
)
func (ks *Kubescape) ScanImage(ctx context.Context, imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
// Data structure to represent attributes
type Attributes struct {
Registry string `json:"registry"`
Organization string `json:"organization,omitempty"`
ImageName string `json:"imageName"`
ImageTag string `json:"imageTag,omitempty"`
}
// Data structure for a target
type Target struct {
DesignatorType string `json:"designatorType"`
Attributes Attributes `json:"attributes"`
}
// Data structure for metadata
type Metadata struct {
Name string `json:"name"`
}
// Data structure for vulnerabilities and severities
type VulnerabilitiesIgnorePolicy struct {
Metadata Metadata `json:"metadata"`
Kind string `json:"kind"`
Targets []Target `json:"targets"`
Vulnerabilities []string `json:"vulnerabilities"`
Severities []string `json:"severities"`
}
// Loads excpetion policies from exceptions json object.
func GetImageExceptionsFromFile(filePath string) ([]VulnerabilitiesIgnorePolicy, error) {
// Read the JSON file
jsonFile, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("error reading exceptions file: %w", err)
}
// Unmarshal the JSON data into an array of VulnerabilitiesIgnorePolicy
var policies []VulnerabilitiesIgnorePolicy
err = json.Unmarshal(jsonFile, &policies)
if err != nil {
return nil, fmt.Errorf("error unmarshaling exceptions file: %w", err)
}
return policies, nil
}
// This function will identify the registry, organization and image tag from the image name
func getAttributesFromImage(imgName string) (Attributes, error) {
canonicalImageName, err := cautils.NormalizeImageName(imgName)
if err != nil {
return Attributes{}, err
}
tokens := strings.Split(canonicalImageName, "/")
registry := tokens[0]
organization := tokens[1]
imageNameAndTag := strings.Split(tokens[2], ":")
imageName := imageNameAndTag[0]
// Intialize the image tag with default value
imageTag := "latest"
if len(imageNameAndTag) > 1 {
imageTag = imageNameAndTag[1]
}
attributes := Attributes{
Registry: registry,
Organization: organization,
ImageName: imageName,
ImageTag: imageTag,
}
return attributes, nil
}
// Checks if the target string matches the regex pattern
func regexStringMatch(pattern, target string) bool {
re, err := regexp.Compile(pattern)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to generate regular expression: %s", err))
return false
}
if re.MatchString(target) {
return true
}
return false
}
// Compares the registry, organization, image name, image tag against the targets specified
// in the exception policy object to check if the image being scanned qualifies for an
// exception policy.
func isTargetImage(targets []Target, attributes Attributes) bool {
for _, target := range targets {
if regexStringMatch(target.Attributes.Registry, attributes.Registry) && regexStringMatch(target.Attributes.Organization, attributes.Organization) && regexStringMatch(target.Attributes.ImageName, attributes.ImageName) && regexStringMatch(target.Attributes.ImageTag, attributes.ImageTag) {
return true
}
}
return false
}
// Generates a list of unique CVE-IDs and the severities which are to be excluded for
// the image being scanned.
func getUniqueVulnerabilitiesAndSeverities(policies []VulnerabilitiesIgnorePolicy, image string) ([]string, []string) {
// Create maps with slices as values to store unique vulnerabilities and severities (case-insensitive)
uniqueVulns := make(map[string][]string)
uniqueSevers := make(map[string][]string)
imageAttributes, err := getAttributesFromImage(image)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to generate image attributes: %s", err))
}
// Iterate over each policy and its vulnerabilities/severities
for _, policy := range policies {
// Include the exceptions only if the image is one of the targets
if isTargetImage(policy.Targets, imageAttributes) {
for _, vulnerability := range policy.Vulnerabilities {
// Add to slice directly
vulnerabilityUppercase := strings.ToUpper(vulnerability)
uniqueVulns[vulnerabilityUppercase] = append(uniqueVulns[vulnerabilityUppercase], vulnerability)
}
for _, severity := range policy.Severities {
// Add to slice directly
severityUppercase := strings.ToUpper(severity)
uniqueSevers[severityUppercase] = append(uniqueSevers[severityUppercase], severity)
}
}
}
// Extract unique keys (which are unique vulnerabilities/severities) and their slices
uniqueVulnsList := make([]string, 0, len(uniqueVulns))
for vuln := range uniqueVulns {
uniqueVulnsList = append(uniqueVulnsList, vuln)
}
uniqueSeversList := make([]string, 0, len(uniqueSevers))
for sever := range uniqueSevers {
uniqueSeversList = append(uniqueSeversList, sever)
}
return uniqueVulnsList, uniqueSeversList
}
func (ks *Kubescape) ScanImage(imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (bool, error) {
logger.L().Start(fmt.Sprintf("Scanning image %s...", imgScanInfo.Image))
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
distCfg, installCfg, _ := imagescan.NewDefaultDBConfig()
svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, imgScanInfo.UseDefaultMatchers)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err))
return false, err
}
defer svc.Close()
creds := imagescan.RegistryCredentials{
Username: imgScanInfo.Username,
Password: imgScanInfo.Password,
}
scanResults, err := svc.Scan(ctx, imgScanInfo.Image, creds)
var vulnerabilityExceptions []string
var severityExceptions []string
if imgScanInfo.Exceptions != "" {
exceptionPolicies, err := GetImageExceptionsFromFile(imgScanInfo.Exceptions)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to load exceptions from file: %s", imgScanInfo.Exceptions))
return false, err
}
vulnerabilityExceptions, severityExceptions = getUniqueVulnerabilitiesAndSeverities(exceptionPolicies, imgScanInfo.Image)
}
imageScanData, err := svc.Scan(ks.Context(), imgScanInfo.Image, creds, vulnerabilityExceptions, severityExceptions)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to scan image: %s", imgScanInfo.Image))
return nil, err
return false, err
}
logger.L().StopSuccess(fmt.Sprintf("Successfully scanned image: %s", imgScanInfo.Image))
scanInfo.SetScanType(cautils.ScanTypeImage)
outputPrinters := GetOutputPrinters(scanInfo, ctx, "")
outputPrinters := GetOutputPrinters(scanInfo, ks.Context(), "")
uiPrinter := GetUIPrinter(ctx, scanInfo, "")
uiPrinter := GetUIPrinter(ks.Context(), scanInfo, "")
resultsHandler := resultshandling.NewResultsHandler(nil, outputPrinters, uiPrinter)
resultsHandler.ImageScanData = []cautils.ImageScanData{
{
PresenterConfig: scanResults,
Image: imgScanInfo.Image,
},
}
resultsHandler.ImageScanData = []cautils.ImageScanData{*imageScanData}
return scanResults, resultsHandler.HandleResults(ctx)
return svc.ExceedsSeverityThreshold(imagescan.ParseSeverity(scanInfo.FailThresholdSeverity), imageScanData.Matches), resultsHandler.HandleResults(ks.Context(), scanInfo)
}

View File

@@ -0,0 +1,447 @@
package core
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetImageExceptionsFromFile(t *testing.T) {
tests := []struct {
filePath string
expectedPolicies []VulnerabilitiesIgnorePolicy
expectedErr error
}{
{
filePath: "./testdata/exceptions.json",
expectedPolicies: []VulnerabilitiesIgnorePolicy{
{
Metadata: Metadata{
Name: "medium-severity-vulnerabilites-exceptions",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "docker.io",
Organization: "",
ImageName: "",
ImageTag: "",
},
},
},
Vulnerabilities: []string{},
Severities: []string{"medium"},
},
{
Metadata: Metadata{
Name: "exclude-allowed-hostPath-control",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "",
Organization: "",
ImageName: "",
ImageTag: "",
},
},
},
Vulnerabilities: []string{"CVE-2023-42366", "CVE-2023-42365"},
Severities: []string{"critical", "low"},
},
{
Metadata: Metadata{
Name: "regex-example",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "quay.*",
Organization: "kube*",
ImageName: "kubescape*",
ImageTag: "v2*",
},
},
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "docker.io",
Organization: ".*",
ImageName: "kube*",
ImageTag: "v3*",
},
},
},
Vulnerabilities: []string{"CVE-2023-6879", "CVE-2023-44487"},
Severities: []string{"critical", "low"},
},
},
expectedErr: nil,
},
{
filePath: "./testdata/empty_exceptions.json",
expectedPolicies: []VulnerabilitiesIgnorePolicy{},
expectedErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.filePath, func(t *testing.T) {
policies, err := GetImageExceptionsFromFile(tt.filePath)
assert.Equal(t, tt.expectedPolicies, policies)
assert.Equal(t, tt.expectedErr, err)
})
}
}
func TestGetAttributesFromImage(t *testing.T) {
tests := []struct {
imageName string
expectedAttributes Attributes
expectedErr error
}{
{
imageName: "quay.io/kubescape/kubescape-cli:v3.0.0",
expectedAttributes: Attributes{
Registry: "quay.io",
Organization: "kubescape",
ImageName: "kubescape-cli",
ImageTag: "v3.0.0",
},
expectedErr: nil,
},
{
imageName: "alpine",
expectedAttributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "latest",
},
expectedErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.imageName, func(t *testing.T) {
attributes, err := getAttributesFromImage(tt.imageName)
assert.Equal(t, tt.expectedErr, err)
assert.Equal(t, tt.expectedAttributes, attributes)
})
}
}
func TestRegexStringMatch(t *testing.T) {
tests := []struct {
pattern string
target string
expected bool
}{
{
pattern: ".*",
target: "quay.io",
expected: true,
},
{
pattern: "kubescape",
target: "kubescape",
expected: true,
},
{
pattern: "kubescape*",
target: "kubescape-cli",
expected: true,
},
{
pattern: "",
target: "v3.0.0",
expected: true,
},
{
pattern: "docker.io",
target: "quay.io",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.target+"/"+tt.pattern, func(t *testing.T) {
assert.Equal(t, tt.expected, regexStringMatch(tt.pattern, tt.target))
})
}
}
func TestIsTargetImage(t *testing.T) {
tests := []struct {
targets []Target
attributes Attributes
expected bool
}{
{
targets: []Target{
{
Attributes: Attributes{
Registry: "docker.io",
Organization: ".*",
ImageName: ".*",
ImageTag: "",
},
},
},
attributes: Attributes{
Registry: "quay.io",
Organization: "kubescape",
ImageName: "kubescape-cli",
ImageTag: "v3.0.0",
},
expected: false,
},
{
targets: []Target{
{
Attributes: Attributes{
Registry: "quay.io",
Organization: "kubescape",
ImageName: "kubescape*",
ImageTag: "",
},
},
},
attributes: Attributes{
Registry: "quay.io",
Organization: "kubescape",
ImageName: "kubescape-cli",
ImageTag: "v3.0.0",
},
expected: true,
},
{
targets: []Target{
{
Attributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "",
},
},
},
attributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "latest",
},
expected: true,
},
{
targets: []Target{
{
Attributes: Attributes{
Registry: "quay.io",
Organization: "kubescape",
ImageName: "kubescape*",
ImageTag: "",
},
},
{
Attributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "",
},
},
},
attributes: Attributes{
Registry: "docker.io",
Organization: "library",
ImageName: "alpine",
ImageTag: "latest",
},
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.attributes.Registry+"/"+tt.attributes.ImageName, func(t *testing.T) {
assert.Equal(t, tt.expected, isTargetImage(tt.targets, tt.attributes))
})
}
}
func TestGetVulnerabilitiesAndSeverities(t *testing.T) {
tests := []struct {
policies []VulnerabilitiesIgnorePolicy
image string
expectedVulnerabilities []string
expectedSeverities []string
}{
{
policies: []VulnerabilitiesIgnorePolicy{
{
Metadata: Metadata{
Name: "vulnerabilites-exceptions",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "",
Organization: "kubescape*",
ImageName: "",
ImageTag: "",
},
},
},
Vulnerabilities: []string{"CVE-2023-42365"},
Severities: []string{},
},
{
Metadata: Metadata{
Name: "exclude-allowed-hostPath-control",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "docker.io",
Organization: "",
ImageName: "",
ImageTag: "",
},
},
},
Vulnerabilities: []string{"CVE-2023-42366", "CVE-2023-42365"},
Severities: []string{"critical", "low"},
},
},
image: "quay.io/kubescape/kubescape-cli:v3.0.0",
expectedVulnerabilities: []string{"CVE-2023-42365"},
expectedSeverities: []string{},
},
{
policies: []VulnerabilitiesIgnorePolicy{
{
Metadata: Metadata{
Name: "medium-severity-vulnerabilites-exceptions",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "",
Organization: "",
ImageName: "",
ImageTag: "",
},
},
},
Vulnerabilities: []string{},
Severities: []string{"medium"},
},
{
Metadata: Metadata{
Name: "exclude-allowed-hostPath-control",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "quay.io",
Organization: "",
ImageName: "",
ImageTag: "",
},
},
},
Vulnerabilities: []string{"CVE-2023-42366", "CVE-2023-42365"},
Severities: []string{},
},
},
image: "alpine",
expectedVulnerabilities: []string{},
expectedSeverities: []string{"MEDIUM"},
},
{
policies: []VulnerabilitiesIgnorePolicy{
{
Metadata: Metadata{
Name: "regex-example",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "quay.io",
Organization: "kube*",
ImageName: "kubescape*",
ImageTag: ".*",
},
},
},
Vulnerabilities: []string{},
Severities: []string{"critical"},
},
{
Metadata: Metadata{
Name: "only-for-docker-registry",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
Registry: "docker.io",
ImageTag: "v3*",
},
},
},
Vulnerabilities: []string{"CVE-2023-42366", "CVE-2022-28391"},
Severities: []string{"high"},
},
{
Metadata: Metadata{
Name: "exclude-allowed-hostPath-control",
},
Kind: "VulnerabilitiesIgnorePolicy",
Targets: []Target{
{
DesignatorType: "Attributes",
Attributes: Attributes{
ImageTag: "v3*",
},
},
},
Vulnerabilities: []string{"CVE-2022-30065", "CVE-2022-28391"},
Severities: []string{},
},
},
image: "quay.io/kubescape/kubescape-cli:v3.0.0",
expectedVulnerabilities: []string{"CVE-2022-30065", "CVE-2022-28391"},
expectedSeverities: []string{"CRITICAL"},
},
}
for _, tt := range tests {
t.Run(tt.image, func(t *testing.T) {
vulnerabilities, severities := getUniqueVulnerabilitiesAndSeverities(tt.policies, tt.image)
sort.Strings(tt.expectedVulnerabilities)
sort.Strings(vulnerabilities)
assert.Equal(t, tt.expectedVulnerabilities, vulnerabilities)
assert.Equal(t, tt.expectedSeverities, severities)
})
}
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"github.com/google/uuid"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
@@ -16,11 +17,8 @@ import (
printerv2 "github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/reporter"
reporterv2 "github.com/kubescape/kubescape/v3/core/pkg/resultshandling/reporter/v2"
"go.opentelemetry.io/otel"
"github.com/google/uuid"
"github.com/kubescape/rbac-utils/rbacscanner"
"go.opentelemetry.io/otel"
)
// getKubernetesApi
@@ -92,7 +90,11 @@ func getResourceHandler(ctx context.Context, scanInfo *cautils.ScanInfo, tenantC
return resourcehandler.NewFileResourceHandler()
}
getter.GetKSCloudAPIConnector()
// Only initialize cloud connector if not in air-gapped mode
// This call initializes the global cloud API connector for later use
if !isAirGappedMode(scanInfo) {
_ = getter.GetKSCloudAPIConnector()
}
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, hostSensorHandler, rbacObjects, tenantConfig.GetContextName())
}

View File

@@ -1,7 +1,17 @@
package core
type Kubescape struct{}
import (
"context"
)
func NewKubescape() *Kubescape {
return &Kubescape{}
type Kubescape struct {
Ctx context.Context
}
func (ks *Kubescape) Context() context.Context {
return ks.Ctx
}
func NewKubescape(ctx context.Context) *Kubescape {
return &Kubescape{Ctx: ctx}
}

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@@ -8,7 +9,8 @@ import (
// The function should return a non-nil pointer.
func TestNewKubescape_ReturnsNonNilPointer(t *testing.T) {
k := NewKubescape()
ctx := context.TODO()
k := NewKubescape(ctx)
assert.NotNil(t, k)
}
@@ -19,5 +21,6 @@ func TestNewKubescape_DoesNotPanic(t *testing.T) {
t.Errorf("Function panicked: %v", r)
}
}()
NewKubescape()
ctx := context.TODO()
NewKubescape(ctx)
}

View File

@@ -7,14 +7,13 @@ import (
"sort"
"strings"
"github.com/jwalton/gchalk"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
v2 "github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/maruel/natural"
"github.com/olekukonko/tablewriter"
)
var listFunc = map[string]func(context.Context, *metav1.ListPolicies) ([]string, error){
@@ -38,16 +37,16 @@ func ListSupportActions() []string {
sort.Strings(commands)
return commands
}
func (ks *Kubescape) List(ctx context.Context, listPolicies *metav1.ListPolicies) error {
func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
if policyListerFunc, ok := listFunc[listPolicies.Target]; ok {
policies, err := policyListerFunc(ctx, listPolicies)
policies, err := policyListerFunc(ks.Context(), listPolicies)
if err != nil {
return err
}
policies = naturalSortPolicies(policies)
if listFormatFunction, ok := listFormatFunc[listPolicies.Format]; ok {
listFormatFunction(ctx, listPolicies.Target, policies)
listFormatFunction(ks.Context(), listPolicies.Target, policies)
} else {
return fmt.Errorf("Invalid format \"%s\", Supported formats: 'pretty-print'/'json' ", listPolicies.Format)
}
@@ -100,30 +99,19 @@ func prettyPrintListFormat(ctx context.Context, targetPolicy string, policies []
return
}
policyTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
policyTable := table.NewWriter()
policyTable.SetOutputMirror(printer.GetWriter(ctx, ""))
policyTable.SetAutoWrapText(true)
header := fmt.Sprintf("Supported %s", targetPolicy)
policyTable.SetHeader([]string{header})
policyTable.SetHeaderLine(true)
policyTable.SetRowLine(true)
policyTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
policyTable.SetAutoFormatHeaders(false)
policyTable.SetAlignment(tablewriter.ALIGN_CENTER)
policyTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
data := v2.Matrix{}
policyTable.AppendHeader(table.Row{header})
policyTable.Style().Options.SeparateHeader = true
policyTable.Style().Options.SeparateRows = true
policyTable.Style().Format.HeaderAlign = text.AlignLeft
policyTable.Style().Format.Header = text.FormatDefault
policyTable.Style().Format.RowAlign = text.AlignCenter
policyTable.Style().Box = table.StyleBoxRounded
controlRows := generatePolicyRows(policies)
var headerColors []tablewriter.Colors
for range controlRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
policyTable.SetHeaderColor(headerColors...)
data = append(data, controlRows...)
policyTable.AppendBulk(data)
policyTable.AppendRows(generatePolicyRows(policies))
policyTable.Render()
}
@@ -134,40 +122,32 @@ func jsonListFormat(_ context.Context, _ string, policies []string) {
}
func prettyPrintControls(ctx context.Context, policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
controlsTable := table.NewWriter()
controlsTable.SetOutputMirror(printer.GetWriter(ctx, ""))
controlsTable.SetAutoWrapText(false)
controlsTable.SetHeaderLine(true)
controlsTable.SetRowLine(true)
controlsTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
controlsTable.SetAutoFormatHeaders(false)
controlsTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
controlsTable.Style().Options.SeparateHeader = true
controlsTable.Style().Options.SeparateRows = true
controlsTable.Style().Format.HeaderAlign = text.AlignLeft
controlsTable.Style().Format.Header = text.FormatDefault
controlsTable.Style().Box = table.StyleBoxRounded
controlsTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}})
controlRows := generateControlRows(policies)
short := utils.CheckShortTerminalWidth(controlRows, []string{"Control ID", "Control name", "Docs", "Frameworks"})
short := utils.CheckShortTerminalWidth(controlRows, table.Row{"Control ID", "Control name", "Docs", "Frameworks"})
if short {
controlsTable.SetAutoWrapText(false)
controlsTable.SetHeader([]string{"Controls"})
controlsTable.AppendHeader(table.Row{"Controls"})
controlRows = shortFormatControlRows(controlRows)
} else {
controlsTable.SetHeader([]string{"Control ID", "Control name", "Docs", "Frameworks"})
controlsTable.AppendHeader(table.Row{"Control ID", "Control name", "Docs", "Frameworks"})
}
var headerColors []tablewriter.Colors
for range controlRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
controlsTable.SetHeaderColor(headerColors...)
data := v2.Matrix{}
data = append(data, controlRows...)
controlsTable.AppendBulk(data)
controlsTable.AppendRows(controlRows)
controlsTable.Render()
}
func generateControlRows(policies []string) [][]string {
rows := [][]string{}
func generateControlRows(policies []string) []table.Row {
rows := make([]table.Row, 0, len(policies))
for _, control := range policies {
@@ -188,7 +168,7 @@ func generateControlRows(policies []string) [][]string {
docs := cautils.GetControlLink(id)
currentRow := []string{id, control, docs, strings.Replace(framework, " ", "\n", -1)}
currentRow := table.Row{id, control, docs, strings.Replace(framework, " ", "\n", -1)}
rows = append(rows, currentRow)
}
@@ -196,20 +176,19 @@ func generateControlRows(policies []string) [][]string {
return rows
}
func generatePolicyRows(policies []string) [][]string {
rows := [][]string{}
func generatePolicyRows(policies []string) []table.Row {
rows := make([]table.Row, 0, len(policies))
for _, policy := range policies {
currentRow := []string{policy}
rows = append(rows, currentRow)
rows = append(rows, table.Row{policy})
}
return rows
}
func shortFormatControlRows(controlRows [][]string) [][]string {
rows := [][]string{}
func shortFormatControlRows(controlRows []table.Row) []table.Row {
rows := make([]table.Row, 0, len(controlRows))
for _, controlRow := range controlRows {
rows = append(rows, []string{fmt.Sprintf("Control ID"+strings.Repeat(" ", 3)+": %+v\nControl Name"+strings.Repeat(" ", 1)+": %+v\nDocs"+strings.Repeat(" ", 9)+": %+v\nFrameworks"+strings.Repeat(" ", 3)+": %+v", controlRow[0], controlRow[1], controlRow[2], strings.Replace(controlRow[3], "\n", " ", -1))})
rows = append(rows, table.Row{fmt.Sprintf("Control ID"+strings.Repeat(" ", 3)+": %+v\nControl Name"+strings.Repeat(" ", 1)+": %+v\nDocs"+strings.Repeat(" ", 9)+": %+v\nFrameworks"+strings.Repeat(" ", 3)+": %+v", controlRow[0], controlRow[1], controlRow[2], strings.Replace(controlRow[3].(string), "\n", " ", -1))})
}
return rows
}

View File

@@ -9,6 +9,7 @@ import (
"sort"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/stretchr/testify/assert"
)
@@ -105,7 +106,7 @@ func TestGeneratePolicyRows_NonEmptyPolicyList(t *testing.T) {
result := generatePolicyRows(policies)
// Assert
assert.Equal(t, [][]string{{"policy1"}, {"policy2"}, {"policy3"}}, result)
assert.Equal(t, []table.Row{{"policy1"}, {"policy2"}, {"policy3"}}, result)
}
// Returns an empty 2D slice for an empty list of policies.
@@ -122,12 +123,12 @@ func TestGeneratePolicyRows_EmptyPolicyList(t *testing.T) {
// The function returns a list of rows, each containing a formatted string with control ID, control name, docs, and frameworks.
func TestShortFormatControlRows_ReturnsListOfRowsWithFormattedString(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"ID1", "Control 1", "Docs 1", "Framework 1"},
{"ID2", "Control 2", "Docs 2", "Framework 2"},
}
want := [][]string{
want := []table.Row{
{"Control ID : ID1\nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
{"Control ID : ID2\nControl Name : Control 2\nDocs : Docs 2\nFrameworks : Framework 2"},
}
@@ -139,12 +140,12 @@ func TestShortFormatControlRows_ReturnsListOfRowsWithFormattedString(t *testing.
// The function formats the control rows correctly, replacing newlines in the frameworks column with line breaks.
func TestShortFormatControlRows_FormatsControlRowsCorrectly(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"ID1", "Control 1", "Docs 1", "Framework\n1"},
{"ID2", "Control 2", "Docs 2", "Framework\n2"},
}
want := [][]string{
want := []table.Row{
{"Control ID : ID1\nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
{"Control ID : ID2\nControl Name : Control 2\nDocs : Docs 2\nFrameworks : Framework 2"},
}
@@ -156,11 +157,11 @@ func TestShortFormatControlRows_FormatsControlRowsCorrectly(t *testing.T) {
// The function handles a control row with an empty control ID.
func TestShortFormatControlRows_HandlesControlRowWithEmptyControlID(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"", "Control 1", "Docs 1", "Framework 1"},
}
want := [][]string{
want := []table.Row{
{"Control ID : \nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
}
@@ -171,11 +172,11 @@ func TestShortFormatControlRows_HandlesControlRowWithEmptyControlID(t *testing.T
// The function handles a control row with an empty control name.
func TestShortFormatControlRows_HandlesControlRowWithEmptyControlName(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"ID1", "", "Docs 1", "Framework 1"},
}
want := [][]string{
want := []table.Row{
{"Control ID : ID1\nControl Name : \nDocs : Docs 1\nFrameworks : Framework 1"},
}
@@ -192,7 +193,7 @@ func TestGenerateControlRowsWithAllFields(t *testing.T) {
"3|Control 3|Framework 3",
}
want := [][]string{
want := []table.Row{
{"1", "Control 1", "https://hub.armosec.io/docs/1", "Framework\n1"},
{"2", "Control 2", "https://hub.armosec.io/docs/2", "Framework\n2"},
{"3", "Control 3", "https://hub.armosec.io/docs/3", "Framework\n3"},
@@ -215,7 +216,7 @@ func TestGenerateControlRowsHandlesPoliciesWithEmptyStringOrNoPipesOrOnePipeMiss
"5|Control 5||Extra 5",
}
expectedRows := [][]string{
expectedRows := []table.Row{
{"", "", "https://hub.armosec.io/docs/", ""},
{"1", "", "https://hub.armosec.io/docs/1", ""},
{"2", "Control 2", "https://hub.armosec.io/docs/2", "Framework\n2"},
@@ -252,18 +253,18 @@ func TestGenerateTableWithCorrectHeadersAndRows(t *testing.T) {
os.Stdout = rescueStdout
// got := buf.String()
want := `────────────┬──────────────┬───────────────────────────────┬────────────
want := `────────────┬──────────────┬───────────────────────────────┬────────────
│ Control ID │ Control name │ Docs │ Frameworks │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 1 │ Control 1 │ https://hub.armosec.io/docs/1 │ Framework │
│ │ │ │ 1
│ │ │ │ 1
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 2 │ Control 2 │ https://hub.armosec.io/docs/2 │ Framework │
│ │ │ │ 2
│ │ │ │ 2
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 3 │ Control 3 │ https://hub.armosec.io/docs/3 │ Framework │
│ │ │ │ 3
────────────┴──────────────┴───────────────────────────────┴────────────
│ │ │ │ 3
────────────┴──────────────┴───────────────────────────────┴────────────
`
assert.Equal(t, want, string(got))
@@ -294,7 +295,7 @@ func TestGenerateTableWithMalformedPoliciesAndPrettyPrintHeadersAndRows(t *testi
os.Stdout = rescueStdout
want := `────────────┬──────────────┬───────────────────────────────┬────────────
want := `────────────┬──────────────┬───────────────────────────────┬────────────
│ Control ID │ Control name │ Docs │ Frameworks │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ │ │ https://hub.armosec.io/docs/ │ │
@@ -302,18 +303,18 @@ func TestGenerateTableWithMalformedPoliciesAndPrettyPrintHeadersAndRows(t *testi
│ 1 │ │ https://hub.armosec.io/docs/1 │ │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 2 │ Control 2 │ https://hub.armosec.io/docs/2 │ Framework │
│ │ │ │ 2
│ │ │ │ 2
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 3 │ Control 3 │ https://hub.armosec.io/docs/3 │ Framework │
│ │ │ │ 3
│ │ │ │ 3
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 4 │ │ https://hub.armosec.io/docs/4 │ Framework │
│ │ │ │ 4
│ │ │ │ 4
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ │ │ https://hub.armosec.io/docs/ │ │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 5 │ Control 5 │ https://hub.armosec.io/docs/5 │ │
────────────┴──────────────┴───────────────────────────────┴────────────
────────────┴──────────────┴───────────────────────────────┴────────────
`
assert.Equal(t, want, string(got))

View File

@@ -1,64 +1,90 @@
package core
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"slices"
"strings"
"time"
"github.com/anchore/grype/grype/presenter"
"github.com/anchore/clio"
grypejson "github.com/anchore/grype/grype/presenter/json"
"github.com/anchore/grype/grype/presenter/models"
copaGrype "github.com/anubhav06/copa-grype/grype"
"github.com/containerd/platforms"
"github.com/docker/buildx/build"
"github.com/docker/cli/cli/config"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
ksmetav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
log "github.com/sirupsen/logrus"
copaGrype "github.com/anubhav06/copa-grype/grype"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/project-copacetic/copacetic/pkg/buildkit"
"github.com/project-copacetic/copacetic/pkg/pkgmgr"
"github.com/project-copacetic/copacetic/pkg/types/unversioned"
"github.com/project-copacetic/copacetic/pkg/utils"
"github.com/quay/claircore/osrelease"
log "github.com/sirupsen/logrus"
)
func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
const (
copaProduct = "copa"
)
func (ks *Kubescape) Patch(patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.ScanInfo) (bool, error) {
// ===================== Scan the image =====================
logger.L().Start(fmt.Sprintf("Scanning image: %s", patchInfo.Image))
// Setup the scan service
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
distCfg, installCfg, _ := imagescan.NewDefaultDBConfig()
svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, scanInfo.UseDefaultMatchers)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err))
return false, err
}
defer svc.Close()
creds := imagescan.RegistryCredentials{
Username: patchInfo.Username,
Password: patchInfo.Password,
}
// Scan the image
scanResults, err := svc.Scan(ctx, patchInfo.Image, creds)
scanResults, err := svc.Scan(ks.Context(), patchInfo.Image, creds, nil, nil)
if err != nil {
return nil, err
return false, err
}
model, err := models.NewDocument(clio.Identification{}, scanResults.Packages, scanResults.Context,
*scanResults.RemainingMatches, scanResults.IgnoredMatches, scanResults.VulnerabilityProvider, nil, nil, models.DefaultSortStrategy, false)
if err != nil {
return false, fmt.Errorf("failed to create document: %w", err)
}
// If the scan results ID is empty, set it to "grype"
if scanResults.ID.Name == "" {
scanResults.ID.Name = "grype"
if model.Descriptor.Name == "" {
model.Descriptor.Name = "grype"
}
// Save the scan results to a file in json format
pres := presenter.GetPresenter("json", "", false, *scanResults)
pres := grypejson.NewPresenter(models.PresenterConfig{Document: model, SBOM: scanResults.SBOM})
fileName := fmt.Sprintf("%s:%s.json", patchInfo.ImageName, patchInfo.ImageTag)
fileName = strings.ReplaceAll(fileName, "/", "-")
writer := printer.GetWriter(ctx, fileName)
writer := printer.GetWriter(ks.Context(), fileName)
if err = pres.Present(writer); err != nil {
return nil, err
return false, err
}
logger.L().StopSuccess(fmt.Sprintf("Successfully scanned image: %s", patchInfo.Image))
@@ -71,8 +97,8 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s
disableCopaLogger()
}
if err = copaPatch(ctx, patchInfo.Timeout, patchInfo.BuildkitAddress, patchInfo.Image, fileName, patchedImageName, "", patchInfo.IgnoreError, patchInfo.BuildKitOpts); err != nil {
return nil, err
if err = copaPatch(ks.Context(), patchInfo.Timeout, patchInfo.BuildkitAddress, patchInfo.Image, fileName, patchedImageName, "", patchInfo.IgnoreError, patchInfo.BuildKitOpts); err != nil {
return false, err
}
// Restore the output streams
@@ -84,9 +110,9 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s
logger.L().Start(fmt.Sprintf("Re-scanning image: %s", patchedImageName))
scanResultsPatched, err := svc.Scan(ctx, patchedImageName, creds)
scanResultsPatched, err := svc.Scan(ks.Context(), patchedImageName, creds, nil, nil)
if err != nil {
return nil, err
return false, err
}
logger.L().StopSuccess(fmt.Sprintf("Successfully re-scanned image: %s", patchedImageName))
@@ -99,17 +125,12 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s
// ===================== Results Handling =====================
scanInfo.SetScanType(cautils.ScanTypeImage)
outputPrinters := GetOutputPrinters(scanInfo, ctx, "")
uiPrinter := GetUIPrinter(ctx, scanInfo, "")
outputPrinters := GetOutputPrinters(scanInfo, ks.Context(), "")
uiPrinter := GetUIPrinter(ks.Context(), scanInfo, "")
resultsHandler := resultshandling.NewResultsHandler(nil, outputPrinters, uiPrinter)
resultsHandler.ImageScanData = []cautils.ImageScanData{
{
PresenterConfig: scanResultsPatched,
Image: patchedImageName,
},
}
resultsHandler.ImageScanData = []cautils.ImageScanData{*scanResultsPatched}
return scanResultsPatched, resultsHandler.HandleResults(ctx)
return svc.ExceedsSeverityThreshold(imagescan.ParseSeverity(scanInfo.FailThresholdSeverity), scanResultsPatched.Matches), resultsHandler.HandleResults(ks.Context(), scanInfo)
}
func disableCopaLogger() {
@@ -163,43 +184,185 @@ func patchWithContext(ctx context.Context, buildkitAddr, image, reportFile, patc
}
}
var updates *unversioned.UpdateManifest
// Parse report for update packages
updates, err := tryParseScanReport(reportFile)
if err != nil {
return err
}
client, err := buildkit.NewClient(ctx, bkOpts)
bkClient, err := buildkit.NewClient(ctx, bkOpts)
if err != nil {
return err
return fmt.Errorf("copa: error creating buildkit client :: %w", err)
}
defer client.Close()
defer bkClient.Close()
// Configure buildctl/client for use by package manager
config, err := buildkit.InitializeBuildkitConfig(ctx, client, image, updates)
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
cfg := authprovider.DockerAuthProviderConfig{ConfigFile: dockerConfig}
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(cfg)}
solveOpt := client.SolveOpt{
Exports: []client.ExportEntry{
{
Type: client.ExporterImage,
Attrs: map[string]string{
"name": patchedImageName,
"push": "true",
},
},
},
Frontend: "", // i.e. we are passing in the llb.Definition directly
Session: attachable, // used for authprovider, sshagentprovider and secretprovider
}
solveOpt.SourcePolicy, err = build.ReadSourcePolicy()
if err != nil {
return err
return fmt.Errorf("copa: error reading source policy :: %w", err)
}
// Create package manager helper
pkgmgr, err := pkgmgr.GetPackageManager(updates.Metadata.OS.Type, config, workingFolder)
if err != nil {
return err
}
buildChannel := make(chan *client.SolveStatus)
_, err = bkClient.Build(ctx, solveOpt, copaProduct, func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
// Configure buildctl/client for use by package manager
config, err := buildkit.InitializeBuildkitConfig(ctx, c, image)
if err != nil {
return nil, fmt.Errorf("copa: error initializing buildkit config for image %s :: %w", image, err)
}
// Export the patched image state to Docker
patchedImageState, _, err := pkgmgr.InstallUpdates(ctx, updates, ignoreError)
if err != nil {
return err
}
// Create package manager helper
var manager pkgmgr.PackageManager
if reportFile == "" {
// determine OS family
fileBytes, err := buildkit.ExtractFileFromState(ctx, c, &config.ImageState, "/etc/os-release")
if err != nil {
return nil, fmt.Errorf("unable to extract /etc/os-release file from state %w", err)
}
if err = buildkit.SolveToDocker(ctx, config.Client, patchedImageState, config.ConfigData, patchedImageName); err != nil {
return err
}
osType, err := getOSType(ctx, fileBytes)
if err != nil {
return nil, fmt.Errorf("copa: error getting os type :: %w", err)
}
osVersion, err := getOSVersion(ctx, fileBytes)
if err != nil {
return nil, fmt.Errorf("copa: error getting os version :: %w", err)
}
// get package manager based on os family type
manager, err = pkgmgr.GetPackageManager(osType, osVersion, config, workingFolder)
if err != nil {
return nil, fmt.Errorf("copa: error getting package manager for ostype=%s, version=%s :: %w", osType, osVersion, err)
}
// do not specify updates, will update all
updates = nil
} else {
// get package manager based on os family type
manager, err = pkgmgr.GetPackageManager(updates.Metadata.OS.Type, updates.Metadata.OS.Version, config, workingFolder)
if err != nil {
return nil, fmt.Errorf("copa: error getting package manager by family type: ostype=%s, osversion=%s :: %w", updates.Metadata.OS.Type, updates.Metadata.OS.Version, err)
}
}
// Export the patched image state to Docker
// TODO: Add support for other output modes as buildctl does.
log.Infof("Patching %d vulnerabilities", len(updates.Updates))
patchedImageState, errPkgs, err := manager.InstallUpdates(ctx, updates, ignoreError)
log.Infof("Error is: %v", err)
if err != nil {
return nil, nil
}
platform := platforms.Normalize(platforms.DefaultSpec())
if platform.OS != "linux" {
platform.OS = "linux"
}
def, err := patchedImageState.Marshal(ctx, llb.Platform(platform))
if err != nil {
return nil, err
}
res, err := c.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
Evaluate: true,
})
if err != nil {
return nil, err
}
res.AddMeta(exptypes.ExporterImageConfigKey, config.ConfigData)
// Currently can only validate updates if updating via scanner
if reportFile != "" {
// create a new manifest with the successfully patched packages
validatedManifest := &unversioned.UpdateManifest{
Metadata: unversioned.Metadata{
OS: unversioned.OS{
Type: updates.Metadata.OS.Type,
Version: updates.Metadata.OS.Version,
},
Config: unversioned.Config{
Arch: updates.Metadata.Config.Arch,
},
},
Updates: []unversioned.UpdatePackage{},
}
for _, update := range updates.Updates {
if !slices.Contains(errPkgs, update.Name) {
validatedManifest.Updates = append(validatedManifest.Updates, update)
}
}
}
return res, nil
}, buildChannel)
return nil
}
func getOSType(ctx context.Context, osreleaseBytes []byte) (string, error) {
r := bytes.NewReader(osreleaseBytes)
osData, err := osrelease.Parse(ctx, r)
if err != nil {
return "", fmt.Errorf("unable to parse os-release data %w", err)
}
osType := strings.ToLower(osData["NAME"])
switch {
case strings.Contains(osType, "alpine"):
return "alpine", nil
case strings.Contains(osType, "debian"):
return "debian", nil
case strings.Contains(osType, "ubuntu"):
return "ubuntu", nil
case strings.Contains(osType, "amazon"):
return "amazon", nil
case strings.Contains(osType, "centos"):
return "centos", nil
case strings.Contains(osType, "mariner"):
return "cbl-mariner", nil
case strings.Contains(osType, "azure linux"):
return "azurelinux", nil
case strings.Contains(osType, "red hat"):
return "redhat", nil
case strings.Contains(osType, "rocky"):
return "rocky", nil
case strings.Contains(osType, "oracle"):
return "oracle", nil
case strings.Contains(osType, "alma"):
return "alma", nil
default:
log.Error("unsupported osType ", osType)
return "", errors.ErrUnsupported
}
}
func getOSVersion(ctx context.Context, osreleaseBytes []byte) (string, error) {
r := bytes.NewReader(osreleaseBytes)
osData, err := osrelease.Parse(ctx, r)
if err != nil {
return "", fmt.Errorf("unable to parse os-release data %w", err)
}
return osData["VERSION_ID"], nil
}
// This function adds support to copa for patching Kubescape produced results
func tryParseScanReport(file string) (*unversioned.UpdateManifest, error) {

View File

@@ -3,6 +3,7 @@ package core
import (
"context"
"fmt"
"slices"
"github.com/kubescape/backend/pkg/versioncheck"
"github.com/kubescape/go-logger"
@@ -21,11 +22,9 @@ import (
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/reporter"
"github.com/kubescape/kubescape/v3/pkg/imagescan"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"go.opentelemetry.io/otel"
"golang.org/x/exp/slices"
"k8s.io/client-go/kubernetes"
"github.com/kubescape/opa-utils/resources"
"go.opentelemetry.io/otel"
"k8s.io/client-go/kubernetes"
)
type componentInterfaces struct {
@@ -67,9 +66,11 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
}
// ================== version testing ======================================
v := versioncheck.NewIVersionCheckHandler(ctx)
_ = v.CheckLatestVersion(ctx, versioncheck.NewVersionCheckRequest(scanInfo.AccountID, versioncheck.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", string(scanInfo.GetScanningContext()), k8sClient))
// Skip version check in air-gapped mode (when keep-local flag is set)
if !scanInfo.Local {
v := versioncheck.NewIVersionCheckHandler(ctx)
_ = v.CheckLatestVersion(ctx, versioncheck.NewVersionCheckRequest(scanInfo.AccountID, versioncheck.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", string(scanInfo.GetScanningContext()), k8sClient))
}
// ================== setup host scanner object ======================================
ctxHostScanner, spanHostScanner := otel.Tracer("").Start(ctx, "setup host scanner")
@@ -122,8 +123,8 @@ func GetOutputPrinters(scanInfo *cautils.ScanInfo, ctx context.Context, clusterN
return outputPrinters
}
func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
ctxInit, spanInit := otel.Tracer("").Start(ctx, "initialization")
func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
ctxInit, spanInit := otel.Tracer("").Start(ks.Context(), "initialization")
logger.L().Start("Kubescape scanner initializing...")
// ===================== Initialization =====================
@@ -133,7 +134,15 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
interfaces := getInterfaces(ctxInit, scanInfo)
interfaces.report.SetTenantConfig(interfaces.tenantConfig)
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// Only create DownloadReleasedPolicy if not in air-gapped mode
var downloadReleasedPolicy *getter.DownloadReleasedPolicy
if isAirGappedMode(scanInfo) {
// In air-gapped mode (--keep-local or using local files via --use-from, --controls-config, --exceptions, or attack tracks),
// don't initialize the downloader to prevent network access
downloadReleasedPolicy = nil
} else {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy() // download config inputs from github release
}
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(ctxInit, scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy)
@@ -149,7 +158,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
// remove host scanner components
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
logger.L().Ctx(ctx).StopError("Failed to tear down host scanner", helpers.Error(err))
logger.L().Ctx(ks.Context()).StopError("Failed to tear down host scanner", helpers.Error(err))
}
}()
@@ -178,7 +187,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
spanInit.End()
// ========================= opa testing =====================
ctxOpa, spanOpa := otel.Tracer("").Start(ctx, "opa testing")
ctxOpa, spanOpa := otel.Tracer("").Start(ks.Context(), "opa testing")
defer spanOpa.End()
deps := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), interfaces.tenantConfig.GetContextName())
@@ -192,7 +201,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
if scanInfo.PrintAttackTree || isPrioritizationScanType(scanInfo.ScanType) {
_, spanPrioritization := otel.Tracer("").Start(ctxOpa, "prioritization")
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(ctxOpa, scanInfo.Getters.AttackTracksGetter, scanInfo.PrintAttackTree); err != nil {
logger.L().Ctx(ctx).Warning("failed to get attack tracks, this may affect the scanning results", helpers.Error(err))
logger.L().Ctx(ks.Context()).Warning("failed to get attack tracks, this may affect the scanning results", helpers.Error(err))
} else if err := priotizationHandler.PrioritizeResources(scanData); err != nil {
return resultsHandling, fmt.Errorf("%w", err)
}
@@ -203,7 +212,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
}
if scanInfo.ScanImages {
scanImages(scanInfo.ScanType, scanData, ctx, resultsHandling)
scanImages(scanInfo.ScanType, scanData, ks.Context(), resultsHandling, scanInfo)
}
// ========================= results handling =====================
resultsHandling.SetData(scanData)
@@ -215,7 +224,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
return resultsHandling, nil
}
func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx context.Context, resultsHandling *resultshandling.ResultsHandler) {
func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx context.Context, resultsHandling *resultshandling.ResultsHandler, scanInfo *cautils.ScanInfo) {
var imagesToScan []string
if scanType == cautils.ScanTypeWorkload {
@@ -244,8 +253,13 @@ func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx
}
}
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
distCfg, installCfg, _ := imagescan.NewDefaultDBConfig()
svc, err := imagescan.NewScanServiceWithMatchers(distCfg, installCfg, scanInfo.UseDefaultMatchers)
if err != nil {
logger.L().StopError(fmt.Sprintf("Failed to initialize image scanner: %s", err))
return
}
defer svc.Close()
for _, img := range imagesToScan {
logger.L().Start("Scanning", helpers.String("image", img))
@@ -256,20 +270,27 @@ func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx
}
}
func scanSingleImage(ctx context.Context, img string, svc imagescan.Service, resultsHandling *resultshandling.ResultsHandler) error {
func scanSingleImage(ctx context.Context, img string, svc *imagescan.Service, resultsHandling *resultshandling.ResultsHandler) error {
scanResults, err := svc.Scan(ctx, img, imagescan.RegistryCredentials{})
scanResults, err := svc.Scan(ctx, img, imagescan.RegistryCredentials{}, nil, nil)
if err != nil {
return err
}
resultsHandling.ImageScanData = append(resultsHandling.ImageScanData, cautils.ImageScanData{
Image: img,
PresenterConfig: scanResults,
})
resultsHandling.ImageScanData = append(resultsHandling.ImageScanData, *scanResults)
return nil
}
func isPrioritizationScanType(scanType cautils.ScanTypes) bool {
return scanType == cautils.ScanTypeCluster || scanType == cautils.ScanTypeRepo
}
// isAirGappedMode returns true if the scan is configured to run in air-gapped mode
// (i.e., without any network access to download policies, exceptions, or other artifacts)
func isAirGappedMode(scanInfo *cautils.ScanInfo) bool {
return scanInfo.Local ||
len(scanInfo.UseFrom) > 0 ||
scanInfo.ControlsInputs != "" ||
scanInfo.UseExceptions != "" ||
scanInfo.AttackTracks != ""
}

View File

@@ -58,3 +58,66 @@ func TestIsPrioritizationScanType(t *testing.T) {
})
}
}
func TestIsAirGappedMode(t *testing.T) {
tests := []struct {
name string
scanInfo *cautils.ScanInfo
want bool
}{
{
name: "air-gapped with Local flag",
scanInfo: &cautils.ScanInfo{
Local: true,
},
want: true,
},
{
name: "air-gapped with UseFrom",
scanInfo: &cautils.ScanInfo{
UseFrom: []string{"/path/to/policy"},
},
want: true,
},
{
name: "air-gapped with ControlsInputs",
scanInfo: &cautils.ScanInfo{
ControlsInputs: "/path/to/controls",
},
want: true,
},
{
name: "air-gapped with UseExceptions",
scanInfo: &cautils.ScanInfo{
UseExceptions: "/path/to/exceptions",
},
want: true,
},
{
name: "air-gapped with AttackTracks",
scanInfo: &cautils.ScanInfo{
AttackTracks: "/path/to/attack-tracks",
},
want: true,
},
{
name: "not air-gapped - all empty",
scanInfo: &cautils.ScanInfo{},
want: false,
},
{
name: "air-gapped with multiple flags",
scanInfo: &cautils.ScanInfo{
Local: true,
UseFrom: []string{"/path/to/policy"},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, isAirGappedMode(tt.scanInfo))
})
}
}

View File

@@ -0,0 +1,67 @@
[
{
"metadata": {
"name": "alpine-exceptions"
},
"kind": "VulnerabilitiesIgnorePolicy",
"targets": [
{
"designatorType": "Attributes",
"attributes": {
"imageName": "alpine*"
}
}
],
"severities": [
"medium"
]
},
{
"metadata": {
"name": "nginx-exceptions"
},
"kind": "VulnerabilitiesIgnorePolicy",
"targets": [
{
"designatorType": "Attributes",
"attributes": {
"imageName": "nginx*"
}
}
],
"vulnerabilities": [
"invalid-cve",
"CVE-2023-45853",
"CVE-2023-49463"
],
"severities": [
"critical",
"medium",
"invalid-severity"
]
},
{
"metadata": {
"name": "applicable-only-to-quay-registry-images"
},
"kind": "VulnerabilitiesIgnorePolicy",
"targets": [
{
"designatorType": "Attributes",
"attributes": {
"registry": "quay.io"
}
}
],
"vulnerabilities": [
"CVE-2023-42365"
],
"severities": [
"critical",
"medium",
"high",
"low"
]
}
]

View File

@@ -0,0 +1 @@
[]

78
core/core/testdata/exceptions.json vendored Normal file
View File

@@ -0,0 +1,78 @@
[
{
"metadata": {
"name": "medium-severity-vulnerabilites-exceptions"
},
"kind": "VulnerabilitiesIgnorePolicy",
"targets": [
{
"designatorType": "Attributes",
"attributes": {
"Registry": "docker.io",
"Organization": "",
"ImageName": ""
}
}
],
"vulnerabilities": [
],
"severities": [
"medium"
]
},
{
"metadata": {
"name": "exclude-allowed-hostPath-control"
},
"kind": "VulnerabilitiesIgnorePolicy",
"targets": [
{
"designatorType": "Attributes",
"attributes": {
}
}
],
"vulnerabilities": [
"CVE-2023-42366",
"CVE-2023-42365"
],
"severities": [
"critical",
"low"
]
},
{
"metadata": {
"name": "regex-example"
},
"kind": "VulnerabilitiesIgnorePolicy",
"targets": [
{
"designatorType": "Attributes",
"attributes": {
"Registry": "quay.*",
"Organization": "kube*",
"ImageName": "kubescape*",
"ImageTag": "v2*"
}
},
{
"designatorType": "Attributes",
"attributes": {
"Registry": "docker.io",
"Organization": ".*",
"ImageName": "kube*",
"ImageTag": "v3*"
}
}
],
"vulnerabilities": [
"CVE-2023-6879",
"CVE-2023-44487"
],
"severities": [
"critical",
"low"
]
}
]

View File

@@ -8,10 +8,8 @@ type SetConfig struct {
CloudReportURL string
CloudAPIURL string
}
type ViewConfig struct {
Writer io.Writer
}
type DeleteConfig struct {
}

View File

@@ -1,7 +1,9 @@
package v1
type ImageScanInfo struct {
Username string
Password string
Image string
Username string
Password string
Image string
Exceptions string
UseDefaultMatchers bool
}

View File

@@ -3,30 +3,31 @@ package meta
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
)
type IKubescape interface {
Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) // TODO - use scanInfo from v1
Context() context.Context
Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) // TODO - use scanInfo from v1
// policies
List(ctx context.Context, listPolicies *metav1.ListPolicies) error // TODO - return list response
Download(ctx context.Context, downloadInfo *metav1.DownloadInfo) error // TODO - return downloaded policies
List(listPolicies *metav1.ListPolicies) error // TODO - return list response
Download(downloadInfo *metav1.DownloadInfo) error // TODO - return downloaded policies
// config
SetCachedConfig(setConfig *metav1.SetConfig) error
ViewCachedConfig(viewConfig *metav1.ViewConfig) error
DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error
DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error
// fix
Fix(ctx context.Context, fixInfo *metav1.FixInfo) error
Fix(fixInfo *metav1.FixInfo) error
// patch
Patch(ctx context.Context, patchInfo *metav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error)
Patch(patchInfo *metav1.PatchInfo, scanInfo *cautils.ScanInfo) (bool, error)
// scan image
ScanImage(ctx context.Context, imgScanInfo *metav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error)
ScanImage(imgScanInfo *metav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (bool, error)
}

View File

@@ -3,7 +3,6 @@ package mocks
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
@@ -11,38 +10,42 @@ import (
type MockIKubescape struct{}
func (m *MockIKubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
func (m *MockIKubescape) Context() context.Context {
return context.TODO()
}
func (m *MockIKubescape) Scan(_ *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
return nil, nil
}
func (m *MockIKubescape) List(ctx context.Context, listPolicies *metav1.ListPolicies) error {
func (m *MockIKubescape) List(_ *metav1.ListPolicies) error {
return nil
}
func (m *MockIKubescape) Download(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
func (m *MockIKubescape) Download(_ *metav1.DownloadInfo) error {
return nil
}
func (m *MockIKubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
func (m *MockIKubescape) SetCachedConfig(_ *metav1.SetConfig) error {
return nil
}
func (m *MockIKubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
func (m *MockIKubescape) ViewCachedConfig(_ *metav1.ViewConfig) error {
return nil
}
func (m *MockIKubescape) DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error {
func (m *MockIKubescape) DeleteCachedConfig(_ *metav1.DeleteConfig) error {
return nil
}
func (m *MockIKubescape) Fix(ctx context.Context, fixInfo *metav1.FixInfo) error {
func (m *MockIKubescape) Fix(_ *metav1.FixInfo) error {
return nil
}
func (m *MockIKubescape) Patch(ctx context.Context, patchInfo *metav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
return nil, nil
func (m *MockIKubescape) Patch(_ *metav1.PatchInfo, _ *cautils.ScanInfo) (bool, error) {
return false, nil
}
func (m *MockIKubescape) ScanImage(ctx context.Context, imgScanInfo *metav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
return nil, nil
func (m *MockIKubescape) ScanImage(_ *metav1.ImageScanInfo, _ *cautils.ScanInfo) (bool, error) {
return false, nil
}

View File

@@ -13,9 +13,8 @@ import (
"strings"
"github.com/armosec/armoapi-go/armotypes"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/go-logger"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
"github.com/kubescape/opa-utils/reporthandling"

View File

@@ -9,7 +9,6 @@ import (
"strings"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"gopkg.in/yaml.v3"
)

View File

@@ -37,7 +37,6 @@ spec:
allowPrivilegeEscalation: true
privileged: true
readOnlyRootFilesystem: true
procMount: Unmasked
ports:
- name: scanner # Do not change port name
containerPort: 7888

View File

@@ -13,7 +13,6 @@ import (
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -97,7 +96,7 @@ func (hsh *HostSensorHandler) Init(ctx context.Context) error {
hsh.populatePodNamesToNodeNames(ctx, log)
if err := hsh.checkPodForEachNode(); err != nil {
logger.L().Ctx(ctx).Warning(failedToValidateHostSensorPodStatus, helpers.Error(err))
return fmt.Errorf("%s: %v", failedToValidateHostSensorPodStatus, err)
}
return nil

View File

@@ -37,7 +37,6 @@ spec:
allowPrivilegeEscalation: true
privileged: true
readOnlyRootFilesystem: true
procMount: Unmasked
ports:
- name: scanner # Do not change port name
containerPort: 7888

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"

View File

@@ -2,8 +2,9 @@ package opaprocessor
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_verify(t *testing.T) {

View File

@@ -3,6 +3,7 @@ package opaprocessor
import (
"context"
"fmt"
"slices"
"strings"
"sync"
@@ -18,12 +19,11 @@ import (
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/kubescape/opa-utils/resources"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
opaprint "github.com/open-policy-agent/opa/topdown/print"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/rego"
"github.com/open-policy-agent/opa/v1/storage"
opaprint "github.com/open-policy-agent/opa/v1/topdown/print"
"go.opentelemetry.io/otel"
"golang.org/x/exp/slices"
)
const ScoreConfigPath = "/resources/config"
@@ -324,6 +324,7 @@ func (opap *OPAProcessor) runRegoOnK8s(ctx context.Context, rule *reporthandling
// NOTE: OPA module compilation is the most resource-intensive operation.
compiled, err := ast.CompileModulesWithOpt(modules, ast.CompileOpts{
EnablePrintStatements: opap.printEnabled,
ParserOptions: ast.ParserOptions{RegoVersion: ast.RegoV0},
})
if err != nil {
return nil, fmt.Errorf("in 'runRegoOnK8s', failed to compile rule, name: %s, reason: %w", rule.Name, err)
@@ -351,6 +352,7 @@ func (opap *OPAProcessor) Print(ctx opaprint.Context, str string) error {
func (opap *OPAProcessor) regoEval(ctx context.Context, inputObj []map[string]interface{}, compiledRego *ast.Compiler, store *storage.Store) ([]reporthandling.RuleResponse, error) {
rego := rego.New(
rego.SetRegoVersion(ast.RegoV0),
rego.Query("data.armo_builtins"), // get package name from rule
rego.Compiler(compiledRego),
rego.Input(inputObj),

View File

@@ -14,14 +14,13 @@ import (
"time"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/mocks"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
"github.com/kubescape/opa-utils/resources"
"github.com/stretchr/testify/assert"
"github.com/kubescape/k8s-interface/workloadinterface"
)
var (

View File

@@ -3,8 +3,6 @@ package opaprocessor
import (
"context"
corev1 "k8s.io/api/core/v1"
"github.com/kubescape/go-logger"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
@@ -16,6 +14,7 @@ import (
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
resources "github.com/kubescape/opa-utils/resources"
"go.opentelemetry.io/otel"
corev1 "k8s.io/api/core/v1"
)
const clusterScope = "clusterScope"

View File

@@ -3,11 +3,9 @@ package opaprocessor
import (
"testing"
corev1 "k8s.io/api/core/v1"
"github.com/stretchr/testify/assert"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
)
func TestRemoveData(t *testing.T) {

View File

@@ -2,6 +2,7 @@ package opaprocessor
import (
"fmt"
"slices"
"strings"
"github.com/kubescape/go-logger"
@@ -10,11 +11,10 @@ import (
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/topdown/builtins"
"github.com/open-policy-agent/opa/types"
"golang.org/x/exp/slices"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/rego"
"github.com/open-policy-agent/opa/v1/topdown/builtins"
"github.com/open-policy-agent/opa/v1/types"
)
// convertFrameworksToPolicies convert list of frameworks to list of policies

View File

@@ -3,13 +3,12 @@ package opaprocessor
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/mocks"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
v2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/stretchr/testify/assert"
)
func TestConvertFrameworksToPolicies(t *testing.T) {

View File

@@ -158,8 +158,11 @@ func (policyHandler *PolicyHandler) downloadScanPolicies(ctx context.Context, po
if receivedFramework != nil {
frameworks = append(frameworks, *receivedFramework)
cache := getter.GetDefaultPath(rule.Identifier + ".json")
if _, ok := policyHandler.getters.PolicyGetter.(*getter.LoadPolicy); ok {
continue // skip caching for local files
}
if err := getter.SaveInFile(receivedFramework, cache); err != nil {
logger.L().Ctx(ctx).Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to cache framework", helpers.String("file", cache), helpers.Error(err))
}
}
}
@@ -178,7 +181,7 @@ func (policyHandler *PolicyHandler) downloadScanPolicies(ctx context.Context, po
cache := getter.GetDefaultPath(policy.Identifier + ".json")
if err := getter.SaveInFile(receivedControl, cache); err != nil {
logger.L().Ctx(ctx).Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to cache control", helpers.String("file", cache), helpers.Error(err))
}
}
}

View File

@@ -5,10 +5,9 @@ import (
"strings"
"time"
"github.com/kubescape/kubescape/v3/core/cautils"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/kubescape/v3/core/cautils"
)
func getScanKind(policyIdentifier []cautils.PolicyIdentifier) apisv1.NotificationPolicyKind {

View File

@@ -6,14 +6,13 @@ import (
"path/filepath"
"strings"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
"k8s.io/apimachinery/pkg/version"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/reporthandling"
"k8s.io/apimachinery/pkg/version"
)
// FileResourceHandler handle resources from files and URLs

View File

@@ -8,7 +8,6 @@ import (
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/reporthandling/apis"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
reportv2 "github.com/kubescape/opa-utils/reporthandling/v2"

View File

@@ -7,26 +7,24 @@ import (
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/metrics"
"github.com/kubescape/kubescape/v3/core/pkg/hostsensorutils"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling/apis"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/pager"
"github.com/kubescape/k8s-interface/cloudsupport"
cloudapis "github.com/kubescape/k8s-interface/cloudsupport/apis"
cloudv1 "github.com/kubescape/k8s-interface/cloudsupport/v1"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/metrics"
"github.com/kubescape/kubescape/v3/core/pkg/hostsensorutils"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling/apis"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8slabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/tools/pager"
)
type cloudResourceGetter func(string, string) (workloadinterface.IMetadata, error)
@@ -234,7 +232,7 @@ func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context,
if !strings.Contains(err.Error(), cloudv1.NotSupportedMsg) {
// Return error with useful info on how to configure credentials for getting cloud provider info
logger.L().Debug("failed to get cloud data", helpers.String("resourceKind", resourceKind), helpers.Error(err))
err = fmt.Errorf("failed to get %s descriptive information. Read more: https://hub.armosec.io/docs/kubescape-integration-with-cloud-providers", strings.ToUpper(k8sHandler.cloudProvider))
err = fmt.Errorf("failed to get %s descriptive information. Read more: https://kubescape.io/docs/integrations/kubescape-integration-with-cloud-providers/", strings.ToUpper(k8sHandler.cloudProvider))
cautils.SetInfoMapForResources(err.Error(), cloudResources, sessionObj.InfoMap)
}
@@ -480,8 +478,15 @@ func (k8sHandler *K8sResourceHandler) setCloudProvider() error {
// NoSchedule taint with empty value is usually applied to controlplane
func isMasterNodeTaints(taints []v1.Taint) bool {
for _, taint := range taints {
if taint.Effect == v1.TaintEffectNoSchedule && taint.Value == "" {
return true
if taint.Effect == v1.TaintEffectNoSchedule {
// NoSchedule taint with empty value is usually applied to controlplane
if taint.Value == "" {
return true
}
if taint.Key == "node-role.kubernetes.io/control-plane" && taint.Value == "true" {
return true
}
}
}
return false

View File

@@ -14,264 +14,264 @@ import (
)
func TestIsMasterNodeTaints(t *testing.T) {
noTaintNode := `
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
"node.alpha.kubernetes.io/ttl": "0",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2022-05-16T10:52:32Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "danielg-minikube",
"kubernetes.io/os": "linux",
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
"minikube.k8s.io/name": "danielg-minikube",
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
"minikube.k8s.io/version": "v1.25.1",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": ""
},
"name": "danielg-minikube",
"resourceVersion": "9432",
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
},
"spec": {
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
]
},
"status": {
"addresses": [
noTaintNodeJson := `
{
"address": "192.168.49.2",
"type": "InternalIP"
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
"node.alpha.kubernetes.io/ttl": "0",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2022-05-16T10:52:32Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "danielg-minikube",
"kubernetes.io/os": "linux",
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
"minikube.k8s.io/name": "danielg-minikube",
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
"minikube.k8s.io/version": "v1.25.1",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": ""
},
"name": "danielg-minikube",
"resourceVersion": "9432",
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
},
{
"address": "danielg-minikube",
"type": "Hostname"
"spec": {
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
]
},
"status": {
"addresses": [
{
"address": "192.168.49.2",
"type": "InternalIP"
},
{
"address": "danielg-minikube",
"type": "Hostname"
}
],
"allocatable": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"capacity": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"conditions": [
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has no disk pressure",
"reason": "KubeletHasNoDiskPressure",
"status": "False",
"type": "DiskPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient PID available",
"reason": "KubeletHasSufficientPID",
"status": "False",
"type": "PIDPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:45Z",
"message": "kubelet is posting ready status",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"daemonEndpoints": {
"kubeletEndpoint": {
"Port": 10250
}
},
"images": [
{
"names": [
"requarks/wiki@sha256:dd83fff15e77843ff934b25c28c865ac000edf7653e5d11adad1dd51df87439d"
],
"sizeBytes": 441083858
},
{
"names": [
"mariadb@sha256:821d0411208eaa88f9e1f0daccd1d534f88d19baf724eb9a2777cbedb10b6c66"
],
"sizeBytes": 400782682
},
{
"names": [
"k8s.gcr.io/etcd@sha256:64b9ea357325d5db9f8a723dcf503b5a449177b17ac87d69481e126bb724c263",
"k8s.gcr.io/etcd:3.5.1-0"
],
"sizeBytes": 292558922
},
{
"names": [
"kubernetesui/dashboard@sha256:ec27f462cf1946220f5a9ace416a84a57c18f98c777876a8054405d1428cc92e",
"kubernetesui/dashboard:v2.3.1"
],
"sizeBytes": 220033604
},
{
"names": [
"k8s.gcr.io/kube-apiserver@sha256:f54681a71cce62cbc1b13ebb3dbf1d880f849112789811f98b6aebd2caa2f255",
"k8s.gcr.io/kube-apiserver:v1.23.1"
],
"sizeBytes": 135162256
},
{
"names": [
"k8s.gcr.io/kube-controller-manager@sha256:a7ed87380108a2d811f0d392a3fe87546c85bc366e0d1e024dfa74eb14468604",
"k8s.gcr.io/kube-controller-manager:v1.23.1"
],
"sizeBytes": 124971684
},
{
"names": [
"k8s.gcr.io/kube-proxy@sha256:e40f3a28721588affcf187f3f246d1e078157dabe274003eaa2957a83f7170c8",
"k8s.gcr.io/kube-proxy:v1.23.1"
],
"sizeBytes": 112327826
},
{
"names": [
"quay.io/kubescape/kubescape@sha256:6196f766be50d94b45d903a911f5ee95ac99bc392a1324c3e063bec41efd98ba",
"quay.io/kubescape/kubescape:v2.0.153"
],
"sizeBytes": 110345054
},
{
"names": [
"nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d"
],
"sizeBytes": 109129446
},
{
"names": [
"quay.io/armosec/action-trigger@sha256:b93707d10ff86aac8dfa42ad37192d6bcf9aceeb4321b21756e438389c26e07c",
"quay.io/armosec/action-trigger:v0.0.5"
],
"sizeBytes": 65127067
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:a5f9ddc04a7fdce6d52ef85a21f0de567d8e04d418c2bc5bf5d72b151c997625",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.7"
],
"sizeBytes": 61446712
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:2f879858da89f6542e3223fb18d6d793810cc2ad6e398b66776475e4218b6af5",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.8"
],
"sizeBytes": 61446528
},
{
"names": [
"quay.io/armosec/cluster-collector@sha256:2c4f733d09f7f4090ace04585230bdfacbbc29a3ade38a2e1233d2c0f730d9b6",
"quay.io/armosec/cluster-collector:v0.0.9"
],
"sizeBytes": 53699576
},
{
"names": [
"k8s.gcr.io/kube-scheduler@sha256:8be4eb1593cf9ff2d91b44596633b7815a3753696031a1eb4273d1b39427fa8c",
"k8s.gcr.io/kube-scheduler:v1.23.1"
],
"sizeBytes": 53488305
},
{
"names": [
"k8s.gcr.io/coredns/coredns@sha256:5b6ec0d6de9baaf3e92d0f66cd96a25b9edbce8716f5f15dcd1a616b3abd590e",
"k8s.gcr.io/coredns/coredns:v1.8.6"
],
"sizeBytes": 46829283
},
{
"names": [
"kubernetesui/metrics-scraper@sha256:36d5b3f60e1a144cc5ada820910535074bdf5cf73fb70d1ff1681537eef4e172",
"kubernetesui/metrics-scraper:v1.0.7"
],
"sizeBytes": 34446077
},
{
"names": [
"gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944",
"gcr.io/k8s-minikube/storage-provisioner:v5"
],
"sizeBytes": 31465472
},
{
"names": [
"quay.io/armosec/notification-server@sha256:b6e9b296cd53bd3b2b42c516d8ab43db998acff1124a57aff8d66b3dd7881979",
"quay.io/armosec/notification-server:v0.0.3"
],
"sizeBytes": 20209940
},
{
"names": [
"quay.io/kubescape/host-scanner@sha256:82139d2561039726be060df2878ef023c59df7c536fbd7f6d766af5a99569fee",
"quay.io/kubescape/host-scanner:latest"
],
"sizeBytes": 11796788
},
{
"names": [
"k8s.gcr.io/pause@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db",
"k8s.gcr.io/pause:3.6"
],
"sizeBytes": 682696
}
],
"nodeInfo": {
"architecture": "amd64",
"bootID": "828cbe73-120b-43cf-aae0-9e2d15b8c873",
"containerRuntimeVersion": "docker://20.10.12",
"kernelVersion": "5.13.0-40-generic",
"kubeProxyVersion": "v1.23.1",
"kubeletVersion": "v1.23.1",
"machineID": "8de776e053e140d6a14c2d2def3d6bb8",
"operatingSystem": "linux",
"osImage": "Ubuntu 20.04.2 LTS",
"systemUUID": "da12dc19-10bf-4033-a440-2d9aa33d6fe3"
}
}
],
"allocatable": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"capacity": {
"cpu": "4",
"ephemeral-storage": "94850516Ki",
"hugepages-2Mi": "0",
"memory": "10432976Ki",
"pods": "110"
},
"conditions": [
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has no disk pressure",
"reason": "KubeletHasNoDiskPressure",
"status": "False",
"type": "DiskPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:29Z",
"message": "kubelet has sufficient PID available",
"reason": "KubeletHasSufficientPID",
"status": "False",
"type": "PIDPressure"
},
{
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
"lastTransitionTime": "2022-05-16T10:52:45Z",
"message": "kubelet is posting ready status",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"daemonEndpoints": {
"kubeletEndpoint": {
"Port": 10250
}
},
"images": [
{
"names": [
"requarks/wiki@sha256:dd83fff15e77843ff934b25c28c865ac000edf7653e5d11adad1dd51df87439d"
],
"sizeBytes": 441083858
},
{
"names": [
"mariadb@sha256:821d0411208eaa88f9e1f0daccd1d534f88d19baf724eb9a2777cbedb10b6c66"
],
"sizeBytes": 400782682
},
{
"names": [
"k8s.gcr.io/etcd@sha256:64b9ea357325d5db9f8a723dcf503b5a449177b17ac87d69481e126bb724c263",
"k8s.gcr.io/etcd:3.5.1-0"
],
"sizeBytes": 292558922
},
{
"names": [
"kubernetesui/dashboard@sha256:ec27f462cf1946220f5a9ace416a84a57c18f98c777876a8054405d1428cc92e",
"kubernetesui/dashboard:v2.3.1"
],
"sizeBytes": 220033604
},
{
"names": [
"k8s.gcr.io/kube-apiserver@sha256:f54681a71cce62cbc1b13ebb3dbf1d880f849112789811f98b6aebd2caa2f255",
"k8s.gcr.io/kube-apiserver:v1.23.1"
],
"sizeBytes": 135162256
},
{
"names": [
"k8s.gcr.io/kube-controller-manager@sha256:a7ed87380108a2d811f0d392a3fe87546c85bc366e0d1e024dfa74eb14468604",
"k8s.gcr.io/kube-controller-manager:v1.23.1"
],
"sizeBytes": 124971684
},
{
"names": [
"k8s.gcr.io/kube-proxy@sha256:e40f3a28721588affcf187f3f246d1e078157dabe274003eaa2957a83f7170c8",
"k8s.gcr.io/kube-proxy:v1.23.1"
],
"sizeBytes": 112327826
},
{
"names": [
"quay.io/kubescape/kubescape@sha256:6196f766be50d94b45d903a911f5ee95ac99bc392a1324c3e063bec41efd98ba",
"quay.io/kubescape/kubescape:v2.0.153"
],
"sizeBytes": 110345054
},
{
"names": [
"nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d"
],
"sizeBytes": 109129446
},
{
"names": [
"quay.io/armosec/action-trigger@sha256:b93707d10ff86aac8dfa42ad37192d6bcf9aceeb4321b21756e438389c26e07c",
"quay.io/armosec/action-trigger:v0.0.5"
],
"sizeBytes": 65127067
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:a5f9ddc04a7fdce6d52ef85a21f0de567d8e04d418c2bc5bf5d72b151c997625",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.7"
],
"sizeBytes": 61446712
},
{
"names": [
"quay.io/armosec/images-vulnerabilities-scan@sha256:2f879858da89f6542e3223fb18d6d793810cc2ad6e398b66776475e4218b6af5",
"quay.io/armosec/images-vulnerabilities-scan:v0.0.8"
],
"sizeBytes": 61446528
},
{
"names": [
"quay.io/armosec/cluster-collector@sha256:2c4f733d09f7f4090ace04585230bdfacbbc29a3ade38a2e1233d2c0f730d9b6",
"quay.io/armosec/cluster-collector:v0.0.9"
],
"sizeBytes": 53699576
},
{
"names": [
"k8s.gcr.io/kube-scheduler@sha256:8be4eb1593cf9ff2d91b44596633b7815a3753696031a1eb4273d1b39427fa8c",
"k8s.gcr.io/kube-scheduler:v1.23.1"
],
"sizeBytes": 53488305
},
{
"names": [
"k8s.gcr.io/coredns/coredns@sha256:5b6ec0d6de9baaf3e92d0f66cd96a25b9edbce8716f5f15dcd1a616b3abd590e",
"k8s.gcr.io/coredns/coredns:v1.8.6"
],
"sizeBytes": 46829283
},
{
"names": [
"kubernetesui/metrics-scraper@sha256:36d5b3f60e1a144cc5ada820910535074bdf5cf73fb70d1ff1681537eef4e172",
"kubernetesui/metrics-scraper:v1.0.7"
],
"sizeBytes": 34446077
},
{
"names": [
"gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944",
"gcr.io/k8s-minikube/storage-provisioner:v5"
],
"sizeBytes": 31465472
},
{
"names": [
"quay.io/armosec/notification-server@sha256:b6e9b296cd53bd3b2b42c516d8ab43db998acff1124a57aff8d66b3dd7881979",
"quay.io/armosec/notification-server:v0.0.3"
],
"sizeBytes": 20209940
},
{
"names": [
"quay.io/kubescape/host-scanner@sha256:82139d2561039726be060df2878ef023c59df7c536fbd7f6d766af5a99569fee",
"quay.io/kubescape/host-scanner:latest"
],
"sizeBytes": 11796788
},
{
"names": [
"k8s.gcr.io/pause@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db",
"k8s.gcr.io/pause:3.6"
],
"sizeBytes": 682696
}
],
"nodeInfo": {
"architecture": "amd64",
"bootID": "828cbe73-120b-43cf-aae0-9e2d15b8c873",
"containerRuntimeVersion": "docker://20.10.12",
"kernelVersion": "5.13.0-40-generic",
"kubeProxyVersion": "v1.23.1",
"kubeletVersion": "v1.23.1",
"machineID": "8de776e053e140d6a14c2d2def3d6bb8",
"operatingSystem": "linux",
"osImage": "Ubuntu 20.04.2 LTS",
"systemUUID": "da12dc19-10bf-4033-a440-2d9aa33d6fe3"
}
}
}
`
var l v1.Node
_ = json.Unmarshal([]byte(noTaintNode), &l)
assert.False(t, isMasterNodeTaints(l.Spec.Taints))
`
var noTaintNode v1.Node
_ = json.Unmarshal([]byte(noTaintNodeJson), &noTaintNode)
assert.False(t, isMasterNodeTaints(noTaintNode.Spec.Taints))
taintNode :=
taintNodeJson :=
`
{
"apiVersion": "v1",
@@ -532,8 +532,60 @@ func TestIsMasterNodeTaints(t *testing.T) {
}
}
`
_ = json.Unmarshal([]byte(taintNode), &l)
assert.True(t, isMasterNodeTaints(l.Spec.Taints))
var taintNode v1.Node
_ = json.Unmarshal([]byte(taintNodeJson), &taintNode)
assert.True(t, isMasterNodeTaints(taintNode.Spec.Taints))
taintNodeJson1 :=
`
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
"node.alpha.kubernetes.io/ttl": "0",
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2022-05-16T10:52:32Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "danielg-minikube",
"kubernetes.io/os": "linux",
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
"minikube.k8s.io/name": "danielg-minikube",
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
"minikube.k8s.io/version": "v1.25.1",
"node-role.kubernetes.io/control-plane": "",
"node-role.kubernetes.io/master": "",
"node.kubernetes.io/exclude-from-external-load-balancers": ""
},
"name": "danielg-minikube",
"resourceVersion": "9871",
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
},
"spec": {
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
],
"taints": [
{
"effect": "NoSchedule",
"key": "node-role.kubernetes.io/control-plane",
"value": "true"
}
]
},
"status": {}
}
`
var taintNode1 v1.Node
_ = json.Unmarshal([]byte(taintNodeJson1), &taintNode1)
assert.True(t, isMasterNodeTaints(taintNode1.Spec.Taints))
}
func TestSetMapNamespaceToNumOfResources(t *testing.T) {

View File

@@ -4,13 +4,12 @@ import (
"fmt"
"strings"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling"
"k8s.io/utils/strings/slices"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
)
var (

View File

@@ -1,11 +1,11 @@
package resourcehandler
import (
"testing"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSsEmptyImgVulns(t *testing.T) {

View File

@@ -246,7 +246,6 @@ func (g *GitHubRepository) getFilesFromTree(filesExtensions []string) []string {
return urls
}
func (g *GitHubRepository) rowYamlUrl() string {
return fmt.Sprintf("https://raw.githubusercontent.com/%s/%s", joinOwnerNRepo(g.owner, g.repo), g.branch)
}

View File

@@ -60,7 +60,7 @@ func NewResourcesPrioritizationHandler(ctx context.Context, attackTracksGetter g
// Store attack tracks in cache
cache := getter.GetDefaultPath(cautils.LocalAttackTracksFilename)
if err := getter.SaveInFile(tracks, cache); err != nil {
logger.L().Ctx(ctx).Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to cache attack track", helpers.String("file", cache), helpers.Error(err))
}
return handler, nil

View File

@@ -9,7 +9,7 @@ const (
emptySpace = " "
middleItem = "├── "
continueItem = "│ "
lastItem = "── "
lastItem = "── "
)
type (
@@ -66,7 +66,7 @@ func (t *tree) Items() []Tree {
return t.items
}
// Print returns an visual representation of the tree
// Print returns a visual representation of the tree
func (t *tree) Print() string {
return newPrinter().Print(t)
}

Some files were not shown because too many files have changed in this diff Show More