Compare commits

..

222 Commits

Author SHA1 Message Date
David Wertenteil
0526f58657 Merge to master - PR number: 1121 2023-02-28 07:40:20 +02:00
Alessio Greggi
e419af6c03 ci: pin workflows versions to fixed commits
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-27 21:43:09 +01:00
Matthias Bertschy
03766ec0cd Merge pull request #1120 from alegrey91/fix/remove-hostnetwork-and-hostport-from-host-scanner-deployment
fix(hostsensorsutils): remove hostNetwork and hostPort from deployment
2023-02-27 19:12:05 +01:00
Alessio Greggi
39e2e34fc0 fix(hostsensorsutils): remove hostNet and hostPort from deployment
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-27 18:20:55 +01:00
David Wertenteil
245331b82a Merge pull request #1119 from amirmalka/added-cluster-name-to-otel-init
added clusterName to otel initialization
2023-02-26 19:27:14 +02:00
Amir Malka
cec4e5ca39 added clusterName to otel initialization
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-02-26 18:07:38 +02:00
David Wertenteil
b772588e96 Merge pull request #1117 from dwertent/v2.2.1-patches
V2.2.1 patches
2023-02-26 16:25:13 +02:00
David Wertenteil
5d6ac80c38 Move GITHUB_REF to the krew step
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-26 14:47:25 +02:00
David Wertenteil
33df0e5462 add unitsests to new behavior
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-24 09:49:32 +02:00
David Wertenteil
26ab049622 Do not print table when logger level is warn
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-24 09:19:43 +02:00
David Wertenteil
ac2aa764a4 marking structs that are implementing IPrinter
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-24 09:18:54 +02:00
David Wertenteil
d02bef62d3 wip: re-arange struct
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-24 09:17:31 +02:00
David Wertenteil
46682dfe16 Override GITHUB_REF env when releasing krew
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-24 00:34:18 +02:00
David Wertenteil
01c65194a8 removing host scanner otel env
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-24 00:13:22 +02:00
David Wertenteil
25e42ee4b6 Update rbac-utils pkg
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-23 23:33:35 +02:00
David Wertenteil
7e5abbdd73 Merge pull request #1054 from fredbi/fix/1051-gc-pressure
fix(processorhandler): reduce GC pressure
2023-02-23 23:15:22 +02:00
David Wertenteil
56183ba369 Merge to master - PR number: 1115 2023-02-23 17:32:48 +02:00
David Wertenteil
a9c1ecd3b8 Merge pull request #1104 from alegrey91/fix/improve-namespace-removing-in-host-sensor-lifecycle
fix(hostsensorutils): improve namespace deletion in host-scanner lifecycle
2023-02-23 16:54:09 +02:00
Alessio Greggi
d900ce6146 fix(hostsensorutils): improve namespace deletion in host-scanner lifecycle
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-23 14:41:57 +01:00
David Wertenteil
3a80ff00b6 update opa pkg to 238
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-23 14:25:21 +02:00
David Wertenteil
b989c4c21f update opa pkg
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-23 09:48:52 +02:00
Frédéric BIDON
65c26e22cf fix(processorhandler): reduce GC pressure
* this onboards an optimization from the opa-utils package (caching
exceptions processing)

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-02-22 20:53:02 +01:00
David Wertenteil
915fa919b2 Fix HTML output (#1111)
* Fixed HTML template

Signed-off-by: David Wertenteil <dwertent@armosec.io>

* Adding HTML output format example

Signed-off-by: David Wertenteil <dwertent@armosec.io>

---------

Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-21 13:55:12 +02:00
Matthias Bertschy
8102dd93ba bump go-git-url (#1110)
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-02-21 11:42:59 +02:00
David Wertenteil
35cafa9eb4 Merge pull request #1113 from amirmalka/fix-macos-build
Fix macos build - add missing pkg-config
2023-02-21 10:19:06 +02:00
Amir Malka
cc823d7559 fix macos build - add missing pkg-config
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-02-21 10:13:08 +02:00
David Wertenteil
eaa74487c2 Merge pull request #1103 from matthyx/enable-krew
enable krew plugin publishing action
2023-02-20 17:55:44 +02:00
David Wertenteil
e8a4c2033f Merge pull request #1084 from fredbi/test/download-release-policy
test(getter): more unit tests
2023-02-20 17:55:08 +02:00
Rotem Refael
8fd9258efa Merge pull request #1101 from alegrey91/fix/improve-cloud-provider-detection 2023-02-16 15:25:38 +02:00
Alessio Greggi
159d3907b5 style(hostsensorutils): simplify code with gofmt
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-16 11:38:55 +01:00
Matthias Bertschy
cde916bec8 Merge pull request #1095 from HollowMan6/master
fix(build): LICENSE file in release tarballs
2023-02-15 15:48:25 +01:00
Matthias Bertschy
8d289bd924 Merge pull request #1105 from HollowMan6/readme
fix(README): broken links
2023-02-15 13:33:59 +01:00
Hollow Man
fda1c83d01 fix(build): LICENSE file
Signed-off-by: Hollow Man <hollowman@opensuse.org>
2023-02-15 14:21:42 +02:00
Hollow Man
31b6a3c571 fix(README): broken links
Signed-off-by: Hollow Man <hollowman@opensuse.org>
2023-02-15 14:15:38 +02:00
Matthias Bertschy
31a693e9b6 enable krew plugin publishing action
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-02-15 08:02:24 +01:00
Matthias Bertschy
5de228ce0f Merge pull request #1102 from johnmanjiro13/remove-ds-store
chore: Remove an unwanted file
2023-02-15 07:14:02 +01:00
johnmanjiro13
ed27641f04 chore: Remove an unwanted file
Signed-off-by: johnmanjiro13 <28798279+johnmanjiro13@users.noreply.github.com>
2023-02-15 00:07:12 +09:00
Amir Malka
df39e10300 Statuses (#1016) (#1082)
New statuses
2023-02-14 15:00:21 +02:00
Alessio Greggi
c7d1292c7d fix(hostsensorutils): improve cloud provider detection
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-14 13:46:09 +01:00
Matthias Bertschy
a52f13b8c9 Merge pull request #1100 from matthyx/krew-fix
modify plugin description for krew approval
2023-02-14 10:23:00 +01:00
Matthias Bertschy
16e34002f5 modify plugin description for krew approval
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-02-14 08:47:43 +01:00
Rotem Refael
3242de8a28 Merge pull request #1099 from kubescape/revert-1098-fix/controlplaneinfo-issue
Revert "fix: add check to skip controlplaneinfo in case of cloud providers"
2023-02-14 09:02:02 +02:00
yuleib
ca2730cd85 Revert "fix: add check to skip controlplaneinfo in case of cloud providers" 2023-02-14 08:59:03 +02:00
Rotem Refael
88b55cd6c3 Merge pull request #1098 from alegrey91/fix/controlplaneinfo-issue
fix: add check to skip controlplaneinfo in case of cloud providers
2023-02-14 08:56:35 +02:00
Alessio Greggi
46ca5036c4 fix: add check to skip controlplaneinfo in case of cloud providers
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-13 17:16:53 +01:00
Frederic BIDON
d8f1a25ab7 generated rego policy json fixture file, short-circuited call to github when fixture is here
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-02-10 17:29:46 +01:00
Frederic BIDON
56cfb4fcef test(getters): added unit tests for utilities
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-02-10 17:29:46 +01:00
Frederic BIDON
894d436274 test(getters): added unit tests to the kubescape API client
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-02-10 17:29:45 +01:00
Frederic BIDON
39166d40bf tests(cautils): added unit tests for released policy
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-02-10 17:29:45 +01:00
David Wertenteil
2ba3f78bfc Merge pull request #1094 from alegrey91/master
docs(contributing): add semantic commit messages best practice
2023-02-09 16:09:36 +02:00
Alessio Greggi
1d68d1ba67 docs(contributing): add semantic commit messages best practice
Signed-off-by: Alessio Greggi <ale_grey_91@hotmail.it>
2023-02-09 11:37:24 +01:00
David Wertenteil
6cc5116999 Loading kubeconfig name from env when running ms (#1089)
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-09 09:32:13 +02:00
David Wertenteil
7706c1264c removed unwnated span (#1088)
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-08 18:47:01 +02:00
Craig Box
2f299b6201 fix #1025 (#1087)
Signed-off-by: Craig Box <craigb@armosec.io>
2023-02-08 13:20:59 +02:00
David Wertenteil
f1af9d5687 Merge to master - PR number: 1086 2023-02-08 09:44:10 +02:00
David Wertenteil
d3abd66aa3 Default frameworks (#1085)
* Update default frameworks names

Signed-off-by: David Wertenteil <dwertent@armosec.io>

* Remove error loggers, print debug instead.

Signed-off-by: David Wertenteil <dwertent@armosec.io>

---------

Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-08 08:59:10 +02:00
YiscahLevySilas1
2a0a2cf95a update logs in some resource getters (#1071)
* change warning to debug

Signed-off-by: yiscah <yiscahls@armosec.io>

* update log

Signed-off-by: yiscah <yiscahls@armosec.io>

---------

Signed-off-by: yiscah <yiscahls@armosec.io>
Co-authored-by: David Wertenteil <dwertent@armosec.io>
2023-02-07 22:37:13 +02:00
YiscahLevySilas1
e90f08968f don't add host scanner as excluded ns, use exception instead (#1083)
Signed-off-by: YiscahLevySilas1 <yiscahls@armosec.io>
2023-02-07 22:36:32 +02:00
Matthias Bertschy
e6b7086961 Fixes for krew plugin submission (#1081)
* use tar.gz file for krew plugin

Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>

* add LICENSE to tar.gz archive

Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>

---------

Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-02-07 22:35:54 +02:00
kooomix
4ea35eec00 GitRegoStore moved to Regolibrary (#1058)
* GitRegoStore moved to Regolibrary

* httphandler go mod

* update go.mod

* test old regostorlibrary

* restore gitRegoStore from regolibrary

* Update 	github.com/kubescape/regolibrary to ver 249

Signed-off-by: kooomix <eranm@armosec.io>

* update go mod

Signed-off-by: kooomix <eranm@armosec.io>

* print scan_control_id mesg

Signed-off-by: kooomix <eranm@armosec.io>

* Update regolibrary version - windows host fix

Signed-off-by: kooomix <eranm@armosec.io>

---------

Signed-off-by: kooomix <eranm@armosec.io>
Co-authored-by: David Wertenteil <dwertent@armosec.io>
2023-02-07 13:18:51 +02:00
David Wertenteil
e8253d4193 Renaming the files (#1079)
* Renaming the files

Signed-off-by: David Wertenteil <dwertent@armosec.io>

* Removing un-used code

Signed-off-by: David Wertenteil <dwertent@armosec.io>

---------

Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 19:01:53 +02:00
David Wertenteil
8b8fe92072 Merge pull request #1078 from dwertent/master
Prepare for release
2023-02-06 16:31:54 +02:00
David Wertenteil
bcf9a10131 Remove host scanner warning message
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
b6d21ffd01 removed comments
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
086144c3da JSON version default v2
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
a45ee8ed42 update compliance url
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
129b0f3ee3 fix dockerbuild paths
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
01a8a34637 list files for upload
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
bcb6c06e73 update artifacts location
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
da03022b94 Change artifacts dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
17f313177c release with new tag
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
a81353aa15 Leave release var empty for tests
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
e0b82edd1e rename download dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
b675d09fe2 rename build dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
29b9448dc0 leaving release empty when testing PRs
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
e1020dd1a6 saving file under build dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
9b734b1fa4 Merge pull request #1060 from kubescape/CIS-EKS-support
CIS EKS Support
2023-02-05 17:01:21 +02:00
YiscahLevySilas1
9f97f91f32 add context
Signed-off-by: YiscahLevySilas1 <yiscahls@armosec.io>
2023-02-05 12:03:10 +02:00
YiscahLevySilas1
c6eff8cbaa minor change 2023-02-05 11:54:21 +02:00
David Wertenteil
af9df548d6 Merge branch 'master' into CIS-EKS-support 2023-02-05 09:43:41 +02:00
David Wertenteil
786f3e6b41 Merge pull request #1074 from kubescape/add-otel-client
Add otel client
2023-02-05 09:35:54 +02:00
David Wertenteil
904751e117 Merge pull request #1075 from kubescape/revert-1073-new-wf-trigger
Revert "change wf trigger"
2023-02-02 16:40:09 +02:00
David Wertenteil
ce43661307 Revert "change wf trigger" 2023-02-02 16:39:16 +02:00
David Wertenteil
cd4b601557 Merge pull request #1044 from matthyx/otel
add otel with uptrace client
2023-02-02 16:37:18 +02:00
David Wertenteil
f34f1449db Merge pull request #1073 from kubescape/new-wf-trigger
change wf trigger
2023-02-01 21:16:40 +02:00
Matan Shkalim
16c74a228f change wf trigger
Signed-off-by: Matan Shkalim <shekel8@gmail.com>
2023-02-01 16:24:11 +00:00
David Wertenteil
ad01f01a6c Merge branch 'master' into otel 2023-01-31 23:09:02 +02:00
David Wertenteil
da0b9883ea Merge to master - PR number: 1070 2023-01-31 15:19:25 +02:00
Matan Shkalim
ac60dbed5e add new workflows + action
Signed-off-by: Matan Shkalim <shekel8@gmail.com>
2023-01-31 13:01:22 +00:00
Matthias Bertschy
3a90682c9e remove otel from CLI part
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-31 10:03:56 +01:00
Matthias Bertschy
160ac0db7c add otel with uptrace client
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-31 08:06:33 +01:00
David Wertenteil
7ec4fb75e3 Merge pull request #1067 from matthyx/fix-dockerfile
fix ksserver name in Dockerfile
2023-01-31 08:28:26 +02:00
Matthias Bertschy
7e88357940 fix ksserver name in Dockerfile
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-31 07:26:18 +01:00
YiscahLevySilas1
1ac808a935 Merge pull request #1063 from YiscahLevySilas1/dev
add unit test
2023-01-30 11:06:31 +02:00
yiscah
45fcc59b5f add unit test
Signed-off-by: yiscah <yiscahls@armosec.io>
2023-01-30 11:03:10 +02:00
YiscahLevySilas1
7875c14adf Merge pull request #1062 from YiscahLevySilas1/dev
update k8s-interface - fix in get region
2023-01-29 20:44:45 +02:00
yiscah
5cddba77aa update k8s-interface - fix in get region
Signed-off-by: yiscah <yiscahls@armosec.io>
2023-01-29 20:42:44 +02:00
YiscahLevySilas1
f3058bf168 Merge pull request #1061 from YiscahLevySilas1/dev
update k8s-interface - fix in get region
2023-01-29 20:34:46 +02:00
yiscah
0d1b92c2ee update k8s-interface - fix in get region
Signed-off-by: yiscah <yiscahls@armosec.io>
2023-01-29 20:32:12 +02:00
yiscah
8de308a5b1 go mod tidy 2023-01-29 09:44:28 +02:00
YiscahLevySilas1
a7f810f0d1 Merge pull request #1043 from YiscahLevySilas1/dev
Get cloud provider resources
2023-01-29 08:59:01 +02:00
Matthias Bertschy
e4e3071f5f Merge pull request #1057 from slashben/master
Progress bar in CLI
2023-01-27 17:52:08 +01:00
yiscah
9a7e61edd1 add cloud resource ListEntitiesForPolicies 2023-01-27 13:42:15 +02:00
Ben
5368330df9 updating httphandler
Signed-off-by: Ben <ben@armosec.io>
2023-01-26 09:40:36 +02:00
Ben
5e6a4cfb3f Checking for nil interface
Signed-off-by: Ben <ben@armosec.io>
2023-01-25 16:23:03 +02:00
Matthias Bertschy
052773b0dc Merge pull request #1022 from matthyx/krew
add kubectl plugin with krew
2023-01-25 08:44:28 +01:00
Matthias Bertschy
d462224b7a add kubectl plugin with krew
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-25 08:07:31 +01:00
yiscah
de1d8a9d86 improve cloud resources getters 2023-01-24 17:18:39 +02:00
Ben
d346b05b76 Fixing system test after API change
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 14:13:19 +02:00
Ben
a3a61d65e9 Limiting the size of the name of controls in the pretty print of the tabel
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 11:55:08 +02:00
Ben
606b0e77ca fixing progress to work on stderr
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 11:55:08 +02:00
Ben
2a82d6cd21 Implementing progress bar for control processing
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 11:55:08 +02:00
yiscah
530ffde50d Merge branch 'master' of https://github.com/kubescape/kubescape into dev 2023-01-23 18:52:43 +02:00
yiscah
7cf23e9730 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2023-01-23 18:35:27 +02:00
David Wertenteil
8d5a8f8e22 Merge pull request #1056 from amirmalka/master
fixed a bug in install.sh script
2023-01-23 13:21:51 +02:00
Amir Malka
b820ce1311 fixed a bug in install.sh script in which default install directory /usr/local/bin was missing and not created
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-01-23 11:30:29 +02:00
kooomix
dae2458867 Merge pull request #1055 from kooomix/hostsensor
Support in CNIInfo hostscanner API
2023-01-23 11:02:08 +02:00
David Wertenteil
d45e636b52 Merge pull request #1050 from vladklokun/retarget-1040-empty-framework-name
chore: retarget a ListFrameworks fix, suggest targeting master in PRs
2023-01-23 10:43:05 +02:00
kooomix
8810631d5c Support in CNIInfo 2023-01-23 09:50:07 +02:00
yiscah
6cddce7399 minor improvements in getting cloud resources 2023-01-22 12:11:58 +02:00
Vlad Klokun
5d5c4f2c9f docs: suggest targeting master instead of dev
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-01-20 17:30:50 +02:00
Frédéric BIDON
e37049f68e fix ListFrameworks (could return an empty element)
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2023-01-20 17:26:27 +02:00
Vlad Klokun
c717a9233b Merge pull request #1046 from fredbi/fix/1040-empty-framework-name
fix ListFrameworks (could return an empty element)
2023-01-20 17:16:31 +02:00
Frédéric BIDON
e37f47de3a fix ListFrameworks (could return an empty element)
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2023-01-20 14:06:51 +01:00
David Wertenteil
0622a474eb Merge pull request #1045 from kubescape/cosign-controls
Cosign controls
2023-01-19 18:45:04 +02:00
Daniel-GrunbergerCA
c357f12c82 add cosign functions for signature checking 2023-01-19 17:21:00 +01:00
yiscah
2cec58384a update go mod 2023-01-18 09:36:36 +02:00
yiscah
5e4bc5ddb8 get new cloud resource - DescribeRepositories 2023-01-18 09:25:50 +02:00
yiscah
f30752d9c3 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2023-01-17 13:56:25 +02:00
David Wertenteil
a586549c57 Merge pull request #1039 from kubescape/release-on-new-tag
Update release flow
2023-01-15 14:50:54 +02:00
David Wertenteil
7c67a54230 Adding a screenshot 2023-01-15 11:20:56 +02:00
David Wertenteil
0006d7d8e7 Update release flow 2023-01-15 11:11:29 +02:00
David Wertenteil
63083ae48a Merge pull request #1037 from kubescape/dev
Release
2023-01-13 15:27:50 +02:00
David Wertenteil
2ce37bd66e Merge pull request #1036 from kubescape/update-logs
Update logs
2023-01-13 14:11:49 +02:00
David Wertenteil
13c760c116 Merge branch 'master' into dev 2023-01-13 14:06:54 +02:00
David Wertenteil
c6261e45a8 Merge pull request #1026 from kubescape/fix-command-indentation
Fix command indentation
2023-01-13 13:40:46 +02:00
David Wertenteil
0c06b6c3e6 enable --create-account flag 2023-01-13 13:27:05 +02:00
David Wertenteil
18a9ac3d6e adding debug logs 2023-01-13 13:26:36 +02:00
David Wertenteil
2bfe2a590c Merge pull request #1034 from matthyx/1032
only attempt to print existing attack graphs
2023-01-13 13:24:08 +02:00
David Wertenteil
fb54f4e6cf Merge pull request #945 from suhasgumma/fix-command1
Add support for fixing Individual Files using "fix" command
2023-01-13 13:23:37 +02:00
Vlad Klokun
9025ba5537 chore: reword unsupported scanning target error message 2023-01-13 12:47:07 +02:00
Vlad Klokun
0c23579db7 docs: clarify the comment adjusting the repoRoot 2023-01-13 12:47:07 +02:00
suhasgumma
a755f365df Fixed: Fix not working when multiple individual files are passed 2023-01-13 12:47:07 +02:00
suhasgumma
15f7b9f954 Add Comment 2023-01-13 12:47:07 +02:00
suhasgumma
92a2704fa6 Fix RelSource for Files 2023-01-13 12:47:07 +02:00
Vlad Klokun
a3defe3025 chore: keep CRLF in fixhandler test data file 2023-01-13 12:03:12 +02:00
David Wertenteil
2be0ef48d8 Merge pull request #1007 from matthyx/bitbucket
add support for Bitbucket scanning
2023-01-13 09:51:57 +02:00
Matthias Bertschy
c97513e4e8 only attempt to print existing attack graphs 2023-01-13 08:38:29 +01:00
Matthias Bertschy
1757c891aa add support for Bitbucket scanning 2023-01-13 07:35:07 +01:00
Vlad Klokun
b02410184e fix: follow newline conventions of the autofixed file
This change makes the autofix handler use the newline separator defined
in the fixed file for writing its changes.
2023-01-12 19:25:39 +02:00
David Wertenteil
b4a6a18a56 Merge pull request #1030 from dwertent/master
Update github workflows and temapltes
2023-01-12 14:14:03 +02:00
yiscah
571a68fb58 Merge branch 'dev' of https://github.com/YiscahLevySilas1/kubescape into dev 2023-01-12 14:10:30 +02:00
YiscahLevySilas1
ef306ca0bf Merge branch 'kubescape:dev' into dev 2023-01-12 14:10:20 +02:00
yiscah
1a011f4968 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2023-01-12 14:09:35 +02:00
David Wertenteil
13ca0027a2 upgrade templates 2023-01-12 14:06:04 +02:00
Moshe Rappaport
93b626bb1e Merge pull request #1029 from dwertent/update-workflow
golangci-lint will setup go version 1.19
2023-01-12 13:48:53 +02:00
David Wertenteil
6b4310cd88 golangci-lint - continue-on-error 2023-01-12 13:46:12 +02:00
David Wertenteil
c883a297b3 do not run GH WF on none code dirs 2023-01-12 13:45:10 +02:00
David Wertenteil
3af351d91f Remove new pr welcome bot 2023-01-12 13:40:34 +02:00
David Wertenteil
93cde0f1a0 golangci-lint will setup go version 1.19 2023-01-12 13:38:29 +02:00
Vlad Klokun
02720d32dd tests: extend test cases for autofix inserts
This change re-organizes the test cases for inserts performed by the
autofixing feature.
2023-01-11 20:21:22 +02:00
Vlad Klokun
ebada00cf1 tests: show diffs when comparing autofixes
This change refactors the TestApplyFixKeepsFormatting test to use
assert.Equalf so it will display a convenient diff between the expected
and actual fixing result.
2023-01-11 20:21:22 +02:00
Vlad Klokun
3b68fc94d1 tests: test fixing close to newline-separated keys in hybrid scenarios 2023-01-11 20:21:22 +02:00
Vlad Klokun
10d534b5bf tests: test autofixing files with comments between fields 2023-01-11 20:21:22 +02:00
Vlad Klokun
2d740fbf4f tests: test autofixing indented lists in hybrid scenarios 2023-01-11 20:21:22 +02:00
Vlad Klokun
d0728676ee tests: re-organize autofixing unit tests
This change:
- Changes test data naming convention to be lexicographically sortable
  and have input and expected data side-by-side.
- Executes each test case in a separate run.
2023-01-11 20:21:22 +02:00
suhasgumma
8856c84a17 fix: keep user formatting when autofixing 2023-01-11 20:21:12 +02:00
Amir Malka
0c87ff6843 Initial implementation of fix command (#898)
* Fix command initial implementation
2023-01-11 20:18:37 +02:00
David Wertenteil
a48d9be386 Merge pull request #1013 from fredbi/test/more-getter-tests
Refactored LoadPolicy getter, with unit tests
2023-01-11 13:46:08 +02:00
YiscahLevySilas1
3cece6cf35 Merge branch 'kubescape:dev' into dev 2023-01-11 12:25:16 +02:00
David Wertenteil
3c93c2c45c Merge pull request #1004 from darkweaver87/fix/panic
🐛 fix/panic
2023-01-11 12:06:01 +02:00
yiscah
7fc10e8213 revert changes 2023-01-11 12:05:56 +02:00
yiscah
bb8f0e3c46 Revert "start developing port forward to host scanner (doesn't work yet)"
This reverts commit 87e2986024.
2023-01-11 12:02:23 +02:00
yiscah
cfd85eadab Merge branch 'dev' of https://github.com/YiscahLevySilas1/kubescape into dev 2023-01-11 11:59:16 +02:00
David Wertenteil
77e0a04c99 fixed unit test 2023-01-11 11:31:30 +02:00
David Wertenteil
b8762b924c Merge branch 'dev' into test/more-getter-tests 2023-01-11 11:21:43 +02:00
David Wertenteil
025e75213a Merge pull request #1017 from matthyx/remotes
fix: branchRef.Remote can be a gitUrl
2023-01-11 09:01:31 +02:00
Matthias Bertschy
1a3a58a309 fix: branchRef.Remote can be a gitUrl 2023-01-11 07:48:31 +01:00
Matthias Bertschy
19438e6143 Merge pull request #1012 from anubhav06/azure-scanning
added Azure repo scanning support
2023-01-11 07:21:11 +01:00
Anubhav Gupta
284c8c737b Merge branch 'dev' of https://github.com/anubhav06/kubescape into azure-scanning 2023-01-10 23:30:11 +05:30
Anubhav Gupta
3441a65290 added Azure repo scanning support
Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
2023-01-10 23:29:35 +05:30
Frederic BIDON
773e43b1e1 refact(getter): refactored loadpolicy
* feat: added support for ListControls and GetFrameworks
* perf: introduced jsoniter unmarshalling for faster decoding
* introduced stricted error handling & predefined errors:
  * suppressed edge cases when a flaky value is returned instead of an error
* added full unit tests of LoadPolicy

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-09 14:48:18 +01:00
David Wertenteil
ddc0b2daf2 Merge pull request #1009 from fdingiit/v2.0.180-fix
bug fix for cannot read local artifacts
2023-01-09 13:15:59 +02:00
David Wertenteil
596686602c Merge pull request #1010 from fredbi/chore/slightly-more-linting
Chore/slightly more linting
2023-01-09 13:12:07 +02:00
Rémi BUISSON
5bb0c97f8f fix: panic on non-cluster scan 2023-01-09 10:34:08 +01:00
Rémi BUISSON
256db4abfb Revert "🐛 fix panic"
This reverts commit 08b8ae45432ddab5137b18347190b505f28e8389.
2023-01-09 08:56:48 +01:00
Rémi BUISSON
3546961a5e 🐛 fix panic 2023-01-09 08:56:48 +01:00
Frederic BIDON
e6dc7c2367 added unit tests to load policy getter
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 12:56:40 +01:00
dingfei
07fa3b4589 bug fix of cannot read local artifacts 2023-01-06 17:28:36 +08:00
Frederic BIDON
d6ed4b1aca finished added linters for this round
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:19 +01:00
Frederic BIDON
69846bb4c0 refactored load policy getter for Frameworks
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:18 +01:00
Frederic BIDON
2e5ad85fe0 simplified trivial expressions (gosimple)
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:18 +01:00
Frederic BIDON
1025431d64 fixed ineffectual assigns
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:18 +01:00
Frederic BIDON
1a863473e7 fixed goimports
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:17 +01:00
Frederic BIDON
28a44ac531 fixed leaking body on http response (bodyclose)
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:17 +01:00
Frederic BIDON
cf484c328b fixed issues reported by gover (e.g. shadowed variables)
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:17 +01:00
Frederic BIDON
668514e08d commented currently unused code
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:17 +01:00
Frederic BIDON
dc45efb6ef ensured gofmt
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:16 +01:00
Frederic BIDON
6d3844f187 follow-up on review following initial relinting
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-06 09:32:16 +01:00
Matthias Bertschy
4d6e85d4c7 Merge pull request #969 from fredbi/fix/conditional-build-for-git-support
Fix/conditional build for git support
2023-01-06 09:11:07 +01:00
Frederic BIDON
d336f4484c build(git): added build tag control over native git functionality
* fixes #964

* adapted build and ci to use build tag
* fixup error messages
* report git scan skipped warning & version
* fixed CI on windows: powershell parsing args...
* fixup leftover comment
* fixup typo in test message
* resolved merge conflicts on unit tests
* fix: added gitenabled tag to Makefile target

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2023-01-05 17:46:51 +01:00
David Wertenteil
bf263d8d51 Merge pull request #1006 from vladklokun/fix-missing-upstream-panic
Fix missing upstream panic
2023-01-05 13:35:03 +02:00
Vlad Klokun
cc3cf1932c style: go fmt the project 2023-01-05 12:49:19 +02:00
Vlad Klokun
6a4dc79689 fix: don’t panic when branch is missing remote and fallback
This change fixes the case in which Kubescape would panic when scanning
a local Git repository that:
- has the current branch that does not have an upstream set
- does not have an `origin` branch to fall back on

The panic happened because we did not check if the `origin` key exists
in the map of upstreams. This change adds a test for this scenario and
makes it pass by checking if the key exists. If it does not, it returns
an error.

Fixes #1005
2023-01-05 12:47:23 +02:00
David Wertenteil
8c189f6e3c Merge pull request #716 from pwnb0y/master
install.ps1 is modified to increase downloading speed as well as show progress bar
2023-01-05 08:28:47 +02:00
Amir Malka
b309cfca7a Print attack tree (optional, with argument) (#997)
* Print attack tree with argument

* fix
2023-01-03 08:46:50 +02:00
David Wertenteil
c4b3ef5b80 Support AKS parser (#994)
* support GKE parser

* update go mod

* Added KS desgin.drawio

* update k8s-interface pkg

* Added KS desgin.drawio

* support GKE parser

* update go mod

* update k8s-interface pkg

* Added KS desgin.drawio

* revert k8s.io to v0.25.3

* ran go mod tidy

* update sign-up url

* [wip] Adding CreateAccount support

* revert to docs URL

* update opa-utils pkg

* update opa-utils pkg
2023-01-03 08:44:29 +02:00
David Wertenteil
aba978e94a Merge pull request #996 from dwertent/bump-go-19
Bump go version to 1.19
2023-01-03 07:35:39 +02:00
David Wertenteil
a49781e9a8 Merge pull request #998 from Oshratn/patch-1
English and typos
2023-01-02 15:10:08 +02:00
Oshrat Nir
3ba19f55f1 English and typos 2023-01-02 14:55:27 +02:00
David Wertenteil
40a9b9406d Merge pull request #984 from fredbi/chore/introduce-linting
Chore/introduce linting
2023-01-02 08:48:14 +02:00
David Wertenteil
d6b8f5862f bump go version to 1.19 2022-12-28 23:18:03 +02:00
Frédéric BIDON
09f13c05e1 fixed linting issues with minimal linters config
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2022-12-26 17:47:10 +01:00
Frédéric BIDON
b1c8872a29 enabled golangci linter in CI
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2022-12-26 17:47:10 +01:00
Frédéric BIDON
22052f5869 fixed more flaky pointers in loops (resultshandling)
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2022-12-26 17:47:10 +01:00
Frédéric BIDON
afce43add6 fixed more flaky pointers in loops (registryadaptors, opaprocessor)
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2022-12-26 17:47:10 +01:00
Frédéric BIDON
4752364699 fixed flaky loop(cautils): loadpolicy getter
We should not inject pointers to the variable iterated over by the
"range" operator.

Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2022-12-26 17:47:08 +01:00
yiscah
87e2986024 start developing port forward to host scanner (doesn't work yet) 2022-12-15 19:03:44 +02:00
Vicky Aryan
6ec974f996 Merge branch 'kubescape:master' into master 2022-09-06 20:39:21 +05:30
Vicky Aryan
ebf1486a7d Merge branch 'kubescape:master' into master 2022-09-01 17:53:52 +05:30
Vicky Aryan
4d954b2ab0 Merge branch 'kubescape:master' into master 2022-08-29 17:29:26 +05:30
pwnb0y
4d155a6b4f install.ps1 is modified 2022-08-29 11:25:29 +05:30
229 changed files with 49545 additions and 1992 deletions

BIN
.DS_Store vendored

Binary file not shown.

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Keep CRLF newlines in appropriate test files to have reproducible tests
core/pkg/fixhandler/testdata/inserts/*-crlf-newlines.yaml text eol=crlf

View File

@@ -2,33 +2,32 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
labels: 'bug'
assignees: ''
---
# Describe the bug
A clear and concise description of what the bug is.
# Description
<!-- A clear and concise description of what the bug is. -->
# Environment
OS: the OS + version youre running Kubescape on, e.g Ubuntu 22.04 LTS
Version: the version that Kubescape reports when you run `kubescape version`
```
Your current version is:
```
OS: ` ` <!-- the OS + version youre running Kubescape on, e.g Ubuntu 22.04 LTS -->
Version: ` ` <!-- the version that Kubescape reports when you run `kubescape version` -->
# Steps To Reproduce
<!--
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
# Expected behavior
A clear and concise description of what you expected to happen.
<!-- A clear and concise description of what you expected to happen. -->
# Actual Behavior
A clear and concise description of what happened. If applicable, add screenshots to help explain your problem.
<!-- A clear and concise description of what happened. If applicable, add screenshots to help explain your problem. -->
# Additional context
Add any other context about the problem here.
<!-- Add any other context about the problem here. -->

View File

@@ -2,18 +2,23 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
labels: 'feature'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**</br>
> A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like.**</br>
> A clear and concise description of what you want to happen.
## Overview
<!-- A brief overview of the related current state -->
**Describe alternatives you've considered.**</br>
> A clear and concise description of any alternative solutions or features you've considered.
## Problem
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Additional context.**</br>
> Add any other context or screenshots about the feature request here.
## Solution
<!-- A clear and concise description of what you want to happen. -->
## Alternatives
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context
<!-- Add any other context or screenshots about the feature request here. -->

View File

@@ -1,13 +1,39 @@
## Describe your changes
## Overview
<!-- Please provide a brief overview of the changes made in this pull request. e.g. current behavior/future behavior -->
## Screenshots - If Any (Optional)
<!--
## Additional Information
## This PR fixes:
> Any additional information that may be useful for reviewers to know
-->
* Resolved #
<!--
## How to Test
> Please provide instructions on how to test the changes made in this pull request
-->
<!--
## Examples/Screenshots
> Here you add related screenshots
-->
<!--
## Related issues/PRs:
Here you add related issues and PRs.
If this resolved an issue, write "Resolved #<issue number>
e.g. If this PR resolves issues 1 and 2, it should look as follows:
* Resolved #1
* Resolved #2
-->
<!--
## Checklist before requesting a review
<!-- put an [x] in the box to get it checked -->
put an [x] in the box to get it checked
- [ ] My code follows the style guidelines of this project
- [ ] I have commented on my code, particularly in hard-to-understand areas
@@ -15,4 +41,4 @@
- [ ] If it is a core feature, I have added thorough tests.
- [ ] New and existing unit tests pass locally with my changes
**Please open the PR against the `dev` branch (Unless the PR contains only documentation changes)**
-->

37
.github/actions/tag-action/action.yaml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: 'Tag validator and retag'
description: 'This action will check if the tag is rc and create a new tag for release'
inputs:
ORIGINAL_TAG: # id of input
description: 'Original tag'
required: true
default: ${{ github.ref_name }}
SUB_STRING:
description: 'Sub string for rc tag'
required: true
default: "-rc"
outputs:
NEW_TAG:
description: "The new tag for release"
value: ${{ steps.retag.outputs.NEW_TAG }}
runs:
using: "composite"
steps:
- run: |
SUB='-rc'
if [[ "${{ inputs.ORIGINAL_TAG }}" == *"${{ inputs.SUB_STRING }}"* ]]; then
echo "Release candidate tag found."
else
echo "Release candidate tag not found."
exit 1
fi
shell: bash
- id: retag
run: |
NEW_TAG=
echo "Original tag: ${{ inputs.ORIGINAL_TAG }}"
NEW_TAG=$(echo ${{ inputs.ORIGINAL_TAG }} | awk -F '-rc' '{print $1}')
echo "New tag: $NEW_TAG"
echo "NEW_TAG=$NEW_TAG" >> $GITHUB_OUTPUT
shell: bash

29
.github/workflows/00-pr-scanner.yaml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: 00-pr_scanner
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
branches:
- 'master'
- 'main'
- 'dev'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true
jobs:
pr-scanner:
permissions:
pull-requests: write
uses: ./.github/workflows/a-pr-scanner.yaml
with:
RELEASE: ""
CLIENT: test
secrets: inherit

View File

@@ -0,0 +1,48 @@
name: 01-code_review_approved
on:
pull_request_review:
types: [submitted]
branches:
- 'master'
- 'main'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
concurrency:
group: code-review-approved
cancel-in-progress: true
jobs:
binary-build:
if: ${{ github.event.review.state == 'approved' && contains( github.event.pull_request.labels.*.name, 'trigger-integration-test') && github.event.pull_request.base.ref == 'master' }} ## run only if labeled as "trigger-integration-test" and base branch is master
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
with:
COMPONENT_NAME: kubescape
CGO_ENABLED: 1
GO111MODULE: ""
GO_VERSION: "1.19"
RELEASE: ""
CLIENT: test
secrets: inherit
merge-to-master:
needs: binary-build
env:
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
if: ${{ (github.event.review.state == 'approved' && github.event.pull_request.base.ref == 'master') && (always() && (contains(needs.*.result, 'success') || contains(needs.*.result, 'skipped')) && !(contains(needs.*.result, 'failure')) && !(contains(needs.*.result, 'cancelled'))) }}
runs-on: ubuntu-latest
steps:
- name: merge-to-master
if: ${{ env.GH_PERSONAL_ACCESS_TOKEN }}
uses: pascalgn/automerge-action@eb68b061739cb9d81564f8e812d0b3c45f0fb09a # ratchet:pascalgn/automerge-action@v0.15.5
env:
GITHUB_TOKEN: "${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}"
MERGE_COMMIT_MESSAGE: "Merge to master - PR number: {pullRequest.number}"
MERGE_ERROR_FAIL: "true"
MERGE_METHOD: "merge"
MERGE_LABELS: ""
UPDATE_LABELS: ""

View File

@@ -1,54 +0,0 @@
name: golangci-lint
on:
push:
branches:
- dev
pull_request:
types: [ edited, opened, synchronize, reopened ]
branches: [ master, dev ]
paths-ignore:
- '**.yaml'
- '**.md'
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-20.04
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install libgit2
run: make libgit2
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
args: --timeout 10m --build-tags=static
#--new-from-rev dev
# Optional: show only new issues if it's a pull request. The default value is `false`.
only-new-issues: true
# Optional: if set to true then the all caching functionality will be complete disabled,
# takes precedence over all other caching options.
# skip-cache: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

65
.github/workflows/02-release.yaml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: 02-create_release
on:
push:
tags:
- 'v*.*.*-rc.*'
jobs:
retag:
outputs:
NEW_TAG: ${{ steps.tag-calculator.outputs.NEW_TAG }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
- id: tag-calculator
uses: ./.github/actions/tag-action
with:
SUB_STRING: "-rc"
binary-build:
needs: [retag]
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
with:
COMPONENT_NAME: kubescape
CGO_ENABLED: 1
GO111MODULE: ""
GO_VERSION: "1.19"
RELEASE: ${{ needs.retag.outputs.NEW_TAG }}
CLIENT: release
secrets: inherit
create-release:
permissions:
contents: write
needs: [retag, binary-build]
uses: ./.github/workflows/c-create-release.yaml
with:
RELEASE_NAME: "Release ${{ needs.retag.outputs.NEW_TAG }}"
TAG: ${{ needs.retag.outputs.NEW_TAG }}
DRAFT: false
secrets: inherit
publish-krew-plugin:
name: Publish Krew plugin
runs-on: ubuntu-latest
if: "${{ github.repository_owner }} == kubescape"
needs: create-release
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
with:
submodules: recursive
- name: Update new version in krew-index
env:
# overriding the GITHUB_REF so the action can extract the right tag -> https://github.com/rajatjindal/krew-release-bot/blob/v0.0.43/pkg/cicd/github/actions.go#L25
GITHUB_REF: refs/tags/${{ needs.retag.outputs.NEW_TAG }}
uses: rajatjindal/krew-release-bot@92da038bbf995803124a8e50ebd438b2f37bbbb0 # ratchet:rajatjindal/krew-release-bot@v0.0.43
publish-image:
permissions:
id-token: write
packages: write
contents: read
uses: ./.github/workflows/d-publish-image.yaml
needs: [create-release, retag]
with:
client: "image-release"
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
image_tag: ${{ needs.retag.outputs.NEW_TAG }}
support_platforms: true
cosign: true
secrets: inherit

17
.github/workflows/03-post-release.yaml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: 03-create_release_digests
on:
release:
types: [published]
branches:
- 'master'
- 'main'
jobs:
create_release_digests:
name: Creating digests
runs-on: ubuntu-latest
steps:
- name: Digest
uses: MCJack123/ghaction-generate-release-hashes@c03f3111b39432dde3edebe401c5a8d1ffbbf917 # ratchet:MCJack123/ghaction-generate-release-hashes@v1
with:
hash-type: sha1
file-name: kubescape-release-digests

52
.github/workflows/README.md vendored Normal file
View File

@@ -0,0 +1,52 @@
# Kubescape workflows
Tag terminology: `v<major>.<minor>.<patch>`
## Developing process
Kubescape's main branch is `main`, any PR will be opened against the main branch.
### Opening a PR
When a user opens a PR, this will trigger some basic tests (units, license, etc.)
### Reviewing a PR
The reviewer/maintainer of a PR will decide whether the PR introduces changes that require running the E2E system tests. If so, the reviewer will add the `trigger-integration-test` label.
### Approving a PR
Once a maintainer approves the PR, if the `trigger-integration-test` label was added to the PR, the GitHub actions will trigger the system test. The PR will be merged only after the system tests passed successfully. If the label was not added, the PR can be merged.
### Merging a PR
The code is merged, no other actions are needed
## Release process
Every two weeks, we will create a new tag by bumping the minor version, this will create the release and publish the artifacts.
If we are introducing breaking changes, we will update the `major` version instead.
When we wish to push a hot-fix/feature within the two weeks, we will bump the `patch`.
### Creating a new tag
Every two weeks or upon the decision of the maintainers, a maintainer can create a tag.
The tag should look as follows: `v<A>.<B>.<C>-rc.D` (release candidate).
When creating a tag, GitHub will trigger the following actions:
1. Basic tests - unit tests, license, etc.
2. System tests (integration tests). If the tests fail, the actions will stop here.
3. Create a new tag: `v<A>.<B>.<C>` (same tag just without the `rc` suffix)
4. Create a release
5. Publish artifacts
6. Build and publish the docker image (this is meanwhile until we separate the microservice code from the LCI codebase)
## Additional Information
The "callers" have the alphabetic prefix and the "executes" have the numeric prefix
## Screenshot
<img width="1469" alt="image" src="https://user-images.githubusercontent.com/64066841/212532727-e82ec9e7-263d-408b-b4b0-a8c943f0109a.png">

159
.github/workflows/a-pr-scanner.yaml vendored Normal file
View File

@@ -0,0 +1,159 @@
name: a-pr-scanner
on:
workflow_call:
inputs:
RELEASE:
description: 'release'
required: true
type: string
CLIENT:
description: 'Client name'
required: true
type: string
jobs:
scanners:
env:
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
name: PR Scanner
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # Install go because go-licenses use it ratchet:actions/setup-go@v3
name: Installing go
with:
go-version: '1.19'
cache: true
- name: Scanning - Forbidden Licenses (go-licenses)
id: licenses-scan
continue-on-error: true
run: |
echo "## Installing go-licenses tool"
go install github.com/google/go-licenses@latest
echo "## Scanning for forbiden licenses ##"
go-licenses check .
- name: Scanning - Credentials (GitGuardian)
if: ${{ env.GITGUARDIAN_API_KEY }}
continue-on-error: true
id: credentials-scan
uses: GitGuardian/ggshield-action@4ab2994172fadab959240525e6b833d9ae3aca61 # ratchet:GitGuardian/ggshield-action@master
with:
args: -v --all-policies
env:
GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }}
GITHUB_PUSH_BASE_SHA: ${{ github.event.base }}
GITHUB_PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }}
GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
- name: Scanning - Vulnerabilities (Snyk)
if: ${{ env.SNYK_TOKEN }}
id: vulnerabilities-scan
continue-on-error: true
uses: snyk/actions/golang@806182742461562b67788a64410098c9d9b96adb # ratchet:snyk/actions/golang@master
with:
command: test --all-projects
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- 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
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
Scan results:
- License scan: ${{ steps.licenses-scan.outcome }}
- Credentials scan: ${{ steps.credentials-scan.outcome }}
- Vulnerabilities scan: ${{ steps.vulnerabilities-scan.outcome }}
reactions: 'eyes'
basic-tests:
needs: scanners
name: Create cross-platform build
runs-on: ${{ matrix.os }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE: ${{ inputs.RELEASE }}
CLIENT: ${{ inputs.CLIENT }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # ratchet:actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # ratchet:actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # ratchet:actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # ratchet:actions/setup-go@v3
with:
go-version: 1.19
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install pkg-config (macOS)
run: brew install pkg-config
if: matrix.os == 'macos-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Test core pkg
run: go test "-tags=static,gitenabled" -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test "-tags=static,gitenabled" -v ./...
- name: Build
env:
RELEASE: ${{ inputs.RELEASE }}
CLIENT: ${{ inputs.CLIENT }}
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Smoke Testing (Windows / MacOS)
env:
RELEASE: ${{ inputs.RELEASE }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/kubescape-${{ matrix.os }}
if: matrix.os != 'ubuntu-20.04'
- name: Smoke Testing (Linux)
env:
RELEASE: ${{ inputs.RELEASE }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/kubescape-ubuntu-latest
if: matrix.os == 'ubuntu-20.04'
- name: golangci-lint
if: matrix.os == 'ubuntu-20.04'
continue-on-error: true
uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # ratchet:golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout 10m --build-tags=static
only-new-issues: true

View File

@@ -0,0 +1,220 @@
name: b-binary-build-and-e2e-tests
on:
workflow_call:
inputs:
COMPONENT_NAME:
required: true
type: string
RELEASE:
required: true
type: string
CLIENT:
required: true
type: string
GO_VERSION:
type: string
default: "1.19"
GO111MODULE:
required: true
type: string
CGO_ENABLED:
type: number
default: 1
BINARY_TESTS:
type: string
default: '[ "scan_nsa", "scan_mitre", "scan_with_exceptions", "scan_repository", "scan_local_file", "scan_local_glob_files", "scan_local_list_of_files", "scan_nsa_and_submit_to_backend", "scan_mitre_and_submit_to_backend", "scan_local_repository_and_submit_to_backend", "scan_repository_from_url_and_submit_to_backend", "scan_with_exception_to_backend", "scan_with_custom_framework", "scan_customer_configuration", "host_scanner" ]'
jobs:
check-secret:
name: secret-validator
runs-on: ubuntu-latest
outputs:
is-secret-set: ${{ steps.check-secret-set.outputs.is-secret-set }}
steps:
- name: check if the necessary secrets are set in github secrets
id: check-secret-set
env:
CUSTOMER: ${{ secrets.CUSTOMER }}
USERNAME: ${{ secrets.USERNAME }}
PASSWORD: ${{ secrets.PASSWORD }}
CLIENT_ID: ${{ secrets.CLIENT_ID_PROD }}
SECRET_KEY: ${{ secrets.SECRET_KEY_PROD }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: "echo \"is-secret-set=${{ env.CUSTOMER != '' && \n env.USERNAME != '' &&\n env.PASSWORD != '' &&\n env.CLIENT_ID != '' &&\n env.SECRET_KEY != '' &&\n env.REGISTRY_USERNAME != '' &&\n env.REGISTRY_PASSWORD != ''\n }}\" >> $GITHUB_OUTPUT\n"
binary-build:
name: Create cross-platform build
outputs:
TEST_NAMES: ${{ steps.export_tests_to_env.outputs.TEST_NAMES }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-20.04'
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # ratchet:actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # ratchet:actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # ratchet:actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # ratchet:actions/setup-go@v3
name: Installing go
with:
go-version: ${{ inputs.GO_VERSION }}
cache: true
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install pkg-config (macOS)
run: brew install pkg-config
if: matrix.os == 'macos-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Test core pkg
run: go test "-tags=static,gitenabled" -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test "-tags=static,gitenabled" -v ./...
- name: Build
env:
RELEASE: ${{ inputs.RELEASE }}
CLIENT: ${{ inputs.CLIENT }}
CGO_ENABLED: ${{ inputs.CGO_ENABLED }}
run: python3 --version && python3 build.py
- name: Smoke Testing (Windows / MacOS)
env:
RELEASE: ${{ inputs.RELEASE }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/kubescape-${{ matrix.os }}
if: matrix.os != 'ubuntu-20.04'
- name: Smoke Testing (Linux)
env:
RELEASE: ${{ inputs.RELEASE }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/kubescape-ubuntu-latest
if: matrix.os == 'ubuntu-20.04'
- name: golangci-lint
if: matrix.os == 'ubuntu-20.04'
continue-on-error: true
uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # ratchet:golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout 10m --build-tags=static
only-new-issues: true
- id: export_tests_to_env
name: set test name
run: |
echo "TEST_NAMES=$input" >> $GITHUB_OUTPUT
env:
input: ${{ inputs.BINARY_TESTS }}
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # ratchet:actions/upload-artifact@v3.1.1
name: Upload artifact (Linux)
if: matrix.os == 'ubuntu-20.04'
with:
name: kubescape-ubuntu-latest
path: build/
if-no-files-found: error
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # ratchet:actions/upload-artifact@v3.1.1
name: Upload artifact (MacOS, Win)
if: matrix.os != 'ubuntu-20.04'
with:
name: kubescape-${{ matrix.os }}
path: build/
if-no-files-found: error
run-tests:
strategy:
fail-fast: false
matrix:
TEST: ${{ fromJson(needs.binary-build.outputs.TEST_NAMES) }}
needs: [check-secret, binary-build]
if: needs.check-secret.outputs.is-secret-set == 'true'
runs-on: ubuntu-latest # This cannot change
steps:
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3.0.2
id: download-artifact
with:
name: kubescape-ubuntu-latest
path: "~"
- run: ls -laR
- name: chmod +x
run: chmod +x -R ${{steps.download-artifact.outputs.download-path}}/kubescape-ubuntu-latest
- name: Checkout systests repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
with:
repository: armosec/system-tests
path: .
- uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # ratchet:actions/setup-python@v4
with:
python-version: '3.8.13'
cache: 'pip'
- name: create env
run: ./create_env.sh
- name: Generate uuid
id: uuid
run: |
echo "RANDOM_UUID=$(uuidgen)" >> $GITHUB_OUTPUT
- name: Create k8s Kind Cluster
id: kind-cluster-install
uses: helm/kind-action@d08cf6ff1575077dee99962540d77ce91c62387d # ratchet:helm/kind-action@v1.3.0
with:
cluster_name: ${{ steps.uuid.outputs.RANDOM_UUID }}
- name: run-tests
env:
CUSTOMER: ${{ secrets.CUSTOMER }}
USERNAME: ${{ secrets.USERNAME }}
PASSWORD: ${{ secrets.PASSWORD }}
CLIENT_ID: ${{ secrets.CLIENT_ID_PROD }}
SECRET_KEY: ${{ secrets.SECRET_KEY_PROD }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
echo "Test history:"
echo " ${{ matrix.TEST }} " >/tmp/testhistory
cat /tmp/testhistory
source systests_python_env/bin/activate
python3 systest-cli.py \
-t ${{ matrix.TEST }} \
-b production \
-c CyberArmorTests \
--duration 3 \
--logger DEBUG \
--kwargs kubescape=${{steps.download-artifact.outputs.download-path}}/kubescape-ubuntu-latest
deactivate
- name: Test Report
uses: mikepenz/action-junit-report@6e9933f4a97f4d2b99acef4d7b97924466037882 # ratchet:mikepenz/action-junit-report@v3.6.1
if: always() # always run even if the previous step fails
with:
report_paths: '**/results_xml_format/**.xml'
commit: ${{github.event.workflow_run.head_sha}}

View File

@@ -1,115 +0,0 @@
name: build
on:
push:
branches: [ master ]
paths-ignore:
- '**.md'
jobs:
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test
create-release:
uses: ./.github/workflows/release.yaml
needs: test
with:
release_name: "Release v2.0.${{ github.run_number }}"
tag_name: "v2.0.${{ github.run_number }}"
secrets: inherit
publish-artifacts:
name: Build and publish artifacts
needs: create-release
runs-on: ${{ matrix.os }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}
CLIENT: release
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Upload release binaries (Windows / MacOS)
id: upload-release-asset-win-macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/${{ matrix.os }}/kubescape
asset_name: kubescape-${{ matrix.os }}
asset_content_type: application/octet-stream
if: matrix.os != 'ubuntu-20.04'
- name: Upload release binaries (Linux)
id: upload-release-asset-linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/ubuntu-latest/kubescape
asset_name: kubescape-ubuntu-latest
asset_content_type: application/octet-stream
if: matrix.os == 'ubuntu-20.04'
- name: Upload release hash (Windows / MacOS)
id: upload-release-hash-win-macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/${{ matrix.os }}/kubescape.sha256
asset_name: kubescape-${{ matrix.os }}-sha256
asset_content_type: application/octet-stream
if: matrix.os != 'ubuntu-20.04'
- name: Upload release hash (Linux)
id: upload-release-hash-linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/ubuntu-latest/kubescape.sha256
asset_name: kubescape-ubuntu-latest-sha256
asset_content_type: application/octet-stream
if: matrix.os == 'ubuntu-20.04'
publish-image:
uses: ./.github/workflows/build-image.yaml
needs: create-release
with:
client: "image-release"
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
image_tag: "v2.0.${{ github.run_number }}"
support_platforms: true
cosign: true
secrets: inherit

View File

@@ -1,25 +0,0 @@
name: build-dev
on:
push:
branches: [ dev ]
paths-ignore:
# Do not run the pipeline if only Markdown files changed
- '**.md'
jobs:
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test
# publish-dev-image:
# uses: ./.github/workflows/build-image.yaml
# needs: test
# with:
# client: "image-dev"
# image_name: "quay.io/${{ github.repository_owner }}/kubescape"
# image_tag: "dev-v2.0.${{ github.run_number }}"
# support_platforms: true
# cosign: true
# secrets: inherit

52
.github/workflows/c-create-release.yaml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: c-create_release
on:
workflow_call:
inputs:
RELEASE_NAME:
description: 'Release name'
required: true
type: string
TAG:
description: 'Tag name'
required: true
type: string
DRAFT:
description: 'Create draft release'
required: false
type: boolean
default: false
jobs:
create-release:
name: create-release
runs-on: ubuntu-latest
# permissions:
# contents: write
steps:
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3.0.2
id: download-artifact
with:
path: .
- name: Release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # ratchet:softprops/action-gh-release@v1
env:
MAC_OS: macos-latest
UBUNTU_OS: ubuntu-latest
WINDOWS_OS: windows-latest
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ inputs.RELEASE_NAME }}
tag_name: ${{ inputs.TAG }}
body: ${{ github.event.pull_request.body }}
draft: ${{ inputs.DRAFT }}
fail_on_unmatched_files: true
prerelease: false
files: |
./kubescape-${{ env.MAC_OS }}/kubescape-${{ env.MAC_OS }}
./kubescape-${{ env.MAC_OS }}/kubescape-${{ env.MAC_OS }}.sha256
./kubescape-${{ env.MAC_OS }}/kubescape-${{ env.MAC_OS }}.tar.gz
./kubescape-${{ env.UBUNTU_OS }}/kubescape-${{ env.UBUNTU_OS }}
./kubescape-${{ env.UBUNTU_OS }}/kubescape-${{ env.UBUNTU_OS }}.sha256
./kubescape-${{ env.UBUNTU_OS }}/kubescape-${{ env.UBUNTU_OS }}.tar.gz
./kubescape-${{ env.WINDOWS_OS }}/kubescape-${{ env.WINDOWS_OS }}
./kubescape-${{ env.WINDOWS_OS }}/kubescape-${{ env.WINDOWS_OS }}.sha256
./kubescape-${{ env.WINDOWS_OS }}/kubescape-${{ env.WINDOWS_OS }}.tar.gz

View File

@@ -1,22 +0,0 @@
on:
fork:
issues:
types: [opened]
issue_comment:
types: [created]
pull_request_target:
types: [opened]
pull_request_review_comment:
types: [created]
jobs:
welcome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: EddieHubCommunity/gh-action-community/src/welcome@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: '<h3>Hi! Welcome to Kubescape. Thank you for taking the time and reporting an issue</h3>'
pr-message: '<h3>Hi! Welcome to Kubescape. Thank you for taking the time and contributing to the open source community</h3>'
footer: '<h4>We will try to review as soon as possible!</h4>'

View File

@@ -1,5 +1,4 @@
name: build
name: d-publish-image
on:
workflow_call:
inputs:
@@ -25,7 +24,6 @@ on:
default: true
type: boolean
description: 'support amd64/arm64'
jobs:
check-secret:
name: check if QUAYIO_REGISTRY_USERNAME & QUAYIO_REGISTRY_PASSWORD is set in github secrets
@@ -33,51 +31,39 @@ jobs:
outputs:
is-secret-set: ${{ steps.check-secret-set.outputs.is-secret-set }}
steps:
- name: Check whether unity activation requests should be done
- name: check if QUAYIO_REGISTRY_USERNAME & QUAYIO_REGISTRY_PASSWORD is set in github secrets
id: check-secret-set
env:
QUAYIO_REGISTRY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
QUAYIO_REGISTRY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAYIO_REGISTRY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
QUAYIO_REGISTRY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
run: |
echo "is-secret-set=${{ env.QUAYIO_REGISTRY_USERNAME != '' && env.QUAYIO_REGISTRY_PASSWORD != '' }}" >> $GITHUB_OUTPUT
echo "is-secret-set=${{ env.QUAYIO_REGISTRY_USERNAME != '' && env.QUAYIO_REGISTRY_PASSWORD != '' }}" >> $GITHUB_OUTPUT
build-image:
needs: [check-secret]
if: needs.check-secret.outputs.is-secret-set == 'true'
name: Build image and upload to registry
runs-on: ubuntu-latest
permissions:
id-token: write
packages: write
contents: read
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
with:
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # ratchet:docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # ratchet:docker/setup-buildx-action@v2
- 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
- name: Build and push image
if: ${{ inputs.support_platforms }}
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push --platform linux/amd64,linux/arm64
- name: Build and push image without amd64/arm64 support
if: ${{ !inputs.support_platforms }}
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push
- name: Install cosign
uses: sigstore/cosign-installer@main
uses: sigstore/cosign-installer@4079ad3567a89f68395480299c77e40170430341 # ratchet:sigstore/cosign-installer@main
with:
cosign-release: 'v1.12.0'
- name: sign kubescape container image
@@ -85,5 +71,4 @@ jobs:
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign sign --force ${{ inputs.image_name }}
cosign sign --force ${{ inputs.image_name }}

View File

@@ -1,17 +0,0 @@
name: create release digests
on:
release:
types: [ published]
branches: [ master ]
jobs:
once:
name: Creating digests
runs-on: ubuntu-latest
steps:
- name: Digest
uses: MCJack123/ghaction-generate-release-hashes@v1
with:
hash-type: sha1
file-name: kubescape-release-digests

View File

@@ -1,16 +0,0 @@
name: pr-checks
on:
pull_request:
branches: [ master, dev ]
types: [ edited, opened, synchronize, reopened ]
paths-ignore:
# Do not run the pipeline if only Markdown files changed
- '**.yaml'
- '**.md'
jobs:
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test

View File

@@ -1,41 +0,0 @@
name: build
on:
workflow_call:
inputs:
release_name:
description: 'release'
required: true
type: string
tag_name:
description: 'tag'
required: true
type: string
draft:
description: 'create draft release'
required: false
type: boolean
default: false
outputs:
upload_url:
description: "The first output string"
value: ${{ jobs.release.outputs.upload_url }}
jobs:
release:
name: Create release
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create a release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ inputs.tag_name }}
release_name: ${{ inputs.release_name }}
draft: ${{ inputs.draft }}
prerelease: false

View File

@@ -1,100 +0,0 @@
name: test
on:
workflow_call:
inputs:
release:
description: 'release'
required: true
type: string
client:
description: 'Client name'
required: true
type: string
jobs:
build:
name: Create cross-platform build
runs-on: ${{ matrix.os }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-20.04'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Test core pkg
run: go test -tags=static -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -tags=static -v ./...
- name: Build
env:
RELEASE: ${{ inputs.release }}
CLIENT: test
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Smoke Testing (Windows / MacOS)
env:
RELEASE: ${{ inputs.release }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
if: matrix.os != 'ubuntu-20.04'
- name: Smoke Testing (Linux)
env:
RELEASE: ${{ inputs.release }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/ubuntu-latest/kubescape
if: matrix.os == 'ubuntu-20.04'

View File

@@ -1,23 +1,19 @@
on:
issues:
types: [opened, labeled]
jobs:
open_PR_message:
if: github.event.label.name == 'typo'
runs-on: ubuntu-latest
steps:
- uses: ben-z/actions-comment-on-issue@1.0.2
- uses: ben-z/actions-comment-on-issue@10be23f9c43ac792663043420fda29dde07e2f0f # ratchet: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@v2
- uses: lee-dohm/close-matching-issues@e9e43aad2fa6f06a058cedfd8fb975fd93b56d8f # ratchet:lee-dohm/close-matching-issues@v2
with:
query: 'label:typo'
token: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@
.history
ca.srl
*.out
ks

View File

@@ -14,23 +14,21 @@ linters:
- gosec
- staticcheck
- nolintlint
- gofmt
- unused
- govet
- bodyclose
- typecheck
- goimports
- ineffassign
- gosimple
disable:
# temporarily disabled
- varcheck
- ineffassign
- unused
- typecheck
- errcheck
- govet
- gosimple
- deadcode
- gofmt
- goimports
- bodyclose
- dupl
- gocognit
- gocritic
- goimports
- gocognit
- nakedret
- revive
- stylecheck
@@ -38,6 +36,7 @@ linters:
- unparam
#- forbidigo # <- see later
# should remain disabled
- deadcode # deprecated linter
- maligned
- lll
- gochecknoinits

30
.krew.yaml Normal file
View File

@@ -0,0 +1,30 @@
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: kubescape
spec:
homepage: https://github.com/kubescape/kubescape/
shortDescription: Scan resources and cluster configs against security frameworks.
version: {{ .TagName }}
description: |
It includes risk analysis, security compliance, and misconfiguration scanning
with an easy-to-use CLI interface, flexible output formats, and automated scanning capabilities.
platforms:
- selector:
matchLabels:
os: darwin
arch: amd64
{{ addURIAndSha "https://github.com/kubescape/kubescape/releases/download/{{ .TagName }}/kubescape-macos-latest.tar.gz" .TagName }}
bin: kubescape
- selector:
matchLabels:
os: linux
arch: amd64
{{ addURIAndSha "https://github.com/kubescape/kubescape/releases/download/{{ .TagName }}/kubescape-ubuntu-latest.tar.gz" .TagName }}
bin: kubescape
- selector:
matchLabels:
os: windows
arch: amd64
{{ addURIAndSha "https://github.com/kubescape/kubescape/releases/download/{{ .TagName }}/kubescape-windows-latest.tar.gz" .TagName }}
bin: kubescape

View File

@@ -21,7 +21,7 @@ Please follow our [code of conduct](CODE_OF_CONDUCT.md) in all of your interacti
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Open Pull Request to `dev` branch - we test the component before merging into the `master` branch
3. Open Pull Request to the `master` branch.
4. We will merge the Pull Request once you have the sign-off.
## Developer Certificate of Origin
@@ -59,6 +59,36 @@ curl -Ls https://gist.githubusercontent.com/dixudx/7d7edea35b4d91e1a2a8fbf41d095
chmod +x .git/hooks/prepare-commit-msg
```
### Use semantic commit messages (optional)
When contributing, you could consider using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), in order to improve logs readability and help us to automatically generate `CHANGELOG`s.
Format: `<type>(<scope>): <subject>`
`<scope>` is optional
#### Example
```
feat(cmd): add kubectl plugin
^--^ ^-^ ^----------------^
| | |
| | +-> subject: summary in present tense.
| |
| +-------> scope: point of interest
|
+-------> type: chore, docs, feat, fix, refactor, style, or test.
```
More Examples:
* `feat`: new feature for the user, not a new feature for build script
* `fix`: bug fix for the user, not a fix to a build script
* `docs`: changes to the documentation
* `style`: formatting, missing semi colons, etc; no production code change
* `refactor`: refactoring production code, eg. renaming a variable
* `test`: adding missing tests, refactoring tests; no production code change
* `chore`: updating grunt tasks etc; no production code change
## Fixing a commit where the DCO failed
Check out [this guide](https://github.com/src-d/guide/blob/master/developer-community/fix-DCO.md).

View File

@@ -11,7 +11,7 @@ libgit2:
cd git2go; make install-static
# go build tags
TAGS = "static"
TAGS = "gitenabled,static"
build:
go build -v -tags=$(TAGS) .

View File

@@ -1,5 +1,5 @@
[![Version](https://img.shields.io/github/v/release/kubescape/kubescape)](releases)
[![build](https://github.com/kubescape/kubescape/actions/workflows/build.yaml/badge.svg)](https://github.com/kubescape/kubescape/actions/workflows/build.yaml)
[![Version](https://img.shields.io/github/v/release/kubescape/kubescape)](https://github.com/kubescape/kubescape/releases)
[![build](https://github.com/kubescape/kubescape/actions/workflows/02-release.yaml/badge.svg)](https://github.com/kubescape/kubescape/actions/workflows/02-release.yaml)
[![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)

View File

@@ -3,9 +3,16 @@ import sys
import hashlib
import platform
import subprocess
import tarfile
BASE_GETTER_CONST = "github.com/kubescape/kubescape/v2/core/cautils/getter"
platformSuffixes = {
"Windows": "windows-latest",
"Linux": "ubuntu-latest",
"Darwin": "macos-latest",
}
def check_status(status, msg):
if status != 0:
sys.stderr.write(msg)
@@ -13,21 +20,15 @@ def check_status(status, msg):
def get_build_dir():
current_platform = platform.system()
build_dir = ""
if current_platform == "Windows": build_dir = "windows-latest"
elif current_platform == "Linux": build_dir = "ubuntu-latest"
elif current_platform == "Darwin": build_dir = "macos-latest"
else: raise OSError("Platform %s is not supported!" % (current_platform))
return os.path.join("build", build_dir)
return "build"
def get_package_name():
package_name = "kubescape"
current_platform = platform.system()
return package_name
if current_platform not in platformSuffixes: raise OSError("Platform %s is not supported!" % (current_platform))
return "kubescape-" + platformSuffixes[current_platform]
def main():
@@ -40,12 +41,13 @@ def main():
client_var = "github.com/kubescape/kubescape/v2/core/cautils.Client"
client_name = os.getenv("CLIENT")
# Create build directory
build_dir = get_build_dir()
ks_file = os.path.join(build_dir, package_name)
hash_file = ks_file + ".sha256"
tar_file = ks_file + ".tar.gz"
if not os.path.isdir(build_dir):
os.makedirs(build_dir)
@@ -56,15 +58,15 @@ def main():
ldflags += " -X {}={}".format(build_url, release_version)
if client_name:
ldflags += " -X {}={}".format(client_var, client_name)
build_command = ["go", "build", "-buildmode=pie", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
build_command = ["go", "build", "-buildmode=pie", "-tags=static,gitenabled", "-o", ks_file, "-ldflags" ,ldflags]
print("Building kubescape and saving here: {}".format(ks_file))
print("Build command: {}".format(" ".join(build_command)))
status = subprocess.call(build_command)
check_status(status, "Failed to build kubescape")
sha256 = hashlib.sha256()
with open(ks_file, "rb") as kube:
sha256.update(kube.read())
@@ -73,8 +75,12 @@ def main():
print("kubescape hash: {}, file: {}".format(hash, hash_file))
kube_sha.write(sha256.hexdigest())
with tarfile.open(tar_file, 'w:gz') as archive:
archive.add(ks_file, "kubescape")
archive.add("LICENSE", "LICENSE")
print("Build Done")
if __name__ == "__main__":
main()

View File

@@ -25,13 +25,13 @@ RUN rm -rf git2go && make libgit2
# build kubescape server
WORKDIR /work/httphandler
RUN python build.py
RUN ls -ltr build/ubuntu-latest
RUN ls -ltr build/
# build kubescape cmd
WORKDIR /work
RUN python build.py
RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts
RUN /work/build/kubescape-ubuntu-latest download artifacts -o /work/artifacts
FROM alpine:3.16.2
@@ -45,7 +45,7 @@ USER ks
WORKDIR /home/ks
COPY --from=builder /work/httphandler/build/ubuntu-latest/kubescape /usr/bin/ksserver
COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape
COPY --from=builder /work/httphandler/build/kubescape-ubuntu-latest /usr/bin/ksserver
COPY --from=builder /work/build/kubescape-ubuntu-latest /usr/bin/kubescape
ENTRYPOINT ["ksserver"]

View File

@@ -1,23 +1,23 @@
package completion
import (
"fmt"
"os"
"strings"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/spf13/cobra"
)
var completionCmdExamples = `
var completionCmdExamples = fmt.Sprintf(`
# Enable BASH shell autocompletion
$ source <(kubescape completion bash)
$ echo 'source <(kubescape completion bash)' >> ~/.bashrc
$ source <(%[1]s completion bash)
$ echo 'source <(%[1]s completion bash)' >> ~/.bashrc
# Enable ZSH shell autocompletion
$ source <(kubectl completion zsh)
$ echo 'source <(kubectl completion zsh)' >> "${fpath[1]}/_kubectl"
`
`, cautils.ExecName())
func GetCompletionCmd() *cobra.Command {
completionCmd := &cobra.Command{

View File

@@ -1,34 +1,37 @@
package config
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var (
configExample = `
configExample = fmt.Sprintf(`
# View cached configurations
kubescape config view
%[1]s config view
# Delete cached configurations
kubescape config delete
%[1]s config delete
# Set cached configurations
kubescape config set --help
`
setConfigExample = `
%[1]s config set --help
`, cautils.ExecName())
setConfigExample = fmt.Sprintf(`
# Set account id
kubescape config set accountID <account id>
%[1]s config set accountID <account id>
# Set client id
kubescape config set clientID <client id>
%[1]s config set clientID <client id>
# Set access key
kubescape config set secretKey <access key>
%[1]s config set secretKey <access key>
# Set cloudAPIURL
kubescape config set cloudAPIURL <cloud API URL>
`
%[1]s config set cloudAPIURL <cloud API URL>
`, cautils.ExecName())
)
func GetConfigCmd(ks meta.IKubescape) *cobra.Command {

View File

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

View File

@@ -1,18 +1,21 @@
package delete
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var deleteExceptionsExamples = `
var deleteExceptionsExamples = fmt.Sprintf(`
# Delete single exception
kubescape delete exceptions "exception name"
%[1]s delete exceptions "exception name"
# Delete multiple exceptions
kubescape delete exceptions "first exception;second exception;third exception"
`
%[1]s delete exceptions "first exception;second exception;third exception"
`, cautils.ExecName())
func GetDeleteCmd(ks meta.IKubescape) *cobra.Command {
var deleteInfo v1.Delete

View File

@@ -5,6 +5,7 @@ import (
"strings"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
@@ -13,7 +14,7 @@ import (
func getExceptionsCmd(ks meta.IKubescape, deleteInfo *v1.Delete) *cobra.Command {
return &cobra.Command{
Use: "exceptions <exception name>",
Short: "Delete exceptions from Kubescape SaaS version. Run 'kubescape list exceptions' for all exceptions names",
Short: fmt.Sprintf("Delete exceptions from Kubescape SaaS version. Run '%[1]s list exceptions' for all exceptions names", cautils.ExecName()),
Example: deleteExceptionsExamples,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {

View File

@@ -1,6 +1,7 @@
package download
import (
"context"
"fmt"
"path/filepath"
"strings"
@@ -14,34 +15,34 @@ import (
)
var (
downloadExample = `
downloadExample = fmt.Sprintf(`
# Download all artifacts and save them in the default path (~/.kubescape)
kubescape download artifacts
%[1]s download artifacts
# Download all artifacts and save them in /tmp path
kubescape download artifacts --output /tmp
%[1]s download artifacts --output /tmp
# Download the NSA framework. Run 'kubescape list frameworks' for all frameworks names
kubescape download framework nsa
# Download the NSA framework. Run '%[1]s list frameworks' for all frameworks names
%[1]s download framework nsa
# Download the "C-0001" control. Run 'kubescape list controls --id' for all controls ids
kubescape download control "C-0001"
# Download the "C-0001" control. Run '%[1]s list controls --id' for all controls ids
%[1]s download control "C-0001"
# Download the "C-0001" control. Run 'kubescape list controls --id' for all controls ids
kubescape download control C-0001
# Download the "C-0001" control. Run '%[1]s list controls --id' for all controls ids
%[1]s download control C-0001
# Download the configured exceptions
kubescape download exceptions
%[1]s download exceptions
# Download the configured controls-inputs
kubescape download controls-inputs
%[1]s download controls-inputs
# Download the attack tracks
kubescape download attack-tracks
`
%[1]s download attack-tracks
`, cautils.ExecName())
)
func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
func GetDownloadCmd(ks meta.IKubescape) *cobra.Command {
var downloadInfo = v1.DownloadInfo{}
downloadCmd := &cobra.Command{
@@ -74,7 +75,7 @@ func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
downloadInfo.Identifier = args[1]
}
if err := ks.Download(&downloadInfo); err != nil {
if err := ks.Download(context.TODO(), &downloadInfo); err != nil {
logger.L().Fatal(err.Error())
}
return nil

48
cmd/fix/fix.go Normal file
View File

@@ -0,0 +1,48 @@
package fix
import (
"context"
"errors"
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var fixCmdExamples = fmt.Sprintf(`
Fix command is for fixing kubernetes manifest files based on a scan command output.
Use with caution, this command will change your files in-place.
# Fix kubernetes YAML manifest files based on a scan command output (output.json)
1) %[1]s scan --format json --format-version v2 --output output.json
2) %[1]s fix output.json
`, cautils.ExecName())
func GetFixCmd(ks meta.IKubescape) *cobra.Command {
var fixInfo metav1.FixInfo
fixCmd := &cobra.Command{
Use: "fix <report output file>",
Short: "Fix misconfiguration in files",
Long: ``,
Example: fixCmdExamples,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("report output file is required")
}
fixInfo.ReportFile = args[0]
return ks.Fix(context.TODO(), &fixInfo)
},
}
fixCmd.PersistentFlags().BoolVar(&fixInfo.NoConfirm, "no-confirm", false, "No confirmation will be given to the user before applying the fix (default false)")
fixCmd.PersistentFlags().BoolVar(&fixInfo.DryRun, "dry-run", false, "No changes will be applied (default false)")
fixCmd.PersistentFlags().BoolVar(&fixInfo.SkipUserValues, "skip-user-values", true, "Changes which involve user-defined values will be skipped")
return fixCmd
}

View File

@@ -1,6 +1,7 @@
package list
import (
"context"
"fmt"
"strings"
@@ -13,19 +14,19 @@ import (
)
var (
listExample = `
listExample = fmt.Sprintf(`
# List default supported frameworks names
kubescape list frameworks
%[1]s list frameworks
# List all supported frameworks names
kubescape list frameworks --account <account id>
%[1]s list frameworks --account <account id>
# List all supported controls names with ids
kubescape list controls
%[1]s list controls
Control documentation:
https://hub.armosec.io/docs/controls
`
`, cautils.ExecName())
)
func GetListCmd(ks meta.IKubescape) *cobra.Command {
@@ -55,7 +56,7 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
listPolicies.Target = args[0]
if err := ks.List(&listPolicies); err != nil {
if err := ks.List(context.TODO(), &listPolicies); err != nil {
logger.L().Fatal(err.Error())
}
return nil
@@ -65,7 +66,7 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.ClientID, "client-id", "", "", "Kubescape SaaS client ID. Default will load client ID from cache, read more - https://hub.armosec.io/docs/authentication")
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.SecretKey, "secret-key", "", "", "Kubescape SaaS secret key. Default will load secret key from cache, read more - https://hub.armosec.io/docs/authentication")
listCmd.PersistentFlags().StringVar(&listPolicies.Format, "format", "pretty-print", "output format. supported: 'pretty-print'/'json'")
listCmd.PersistentFlags().MarkDeprecated("id", "Control ID's are included in list outpus")
listCmd.PersistentFlags().MarkDeprecated("id", "Control ID's are included in list outputs")
return listCmd
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/kubescape/kubescape/v2/cmd/config"
"github.com/kubescape/kubescape/v2/cmd/delete"
"github.com/kubescape/kubescape/v2/cmd/download"
"github.com/kubescape/kubescape/v2/cmd/fix"
"github.com/kubescape/kubescape/v2/cmd/list"
"github.com/kubescape/kubescape/v2/cmd/scan"
"github.com/kubescape/kubescape/v2/cmd/submit"
@@ -25,19 +26,19 @@ import (
var rootInfo cautils.RootInfo
var ksExamples = `
var ksExamples = fmt.Sprintf(`
# Scan command
kubescape scan
%[1]s scan
# List supported frameworks
kubescape list frameworks
%[1]s list frameworks
# Download artifacts (air-gapped environment support)
kubescape download artifacts
%[1]s download artifacts
# View cached configurations
kubescape config view
`
%[1]s config view
`, cautils.ExecName())
func NewDefaultKubescapeCommand() *cobra.Command {
ks := core.NewKubescape()
@@ -52,6 +53,16 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
Example: ksExamples,
}
if cautils.IsKrewPlugin() {
// Invoked as a kubectl plugin.
// Cobra doesn't have a way to specify a two word command (i.e. "kubectl kubescape"), so set a custom usage template
// with kubectl in it. Cobra will use this template for the root and all child commands.
oldUsageTemplate := rootCmd.UsageTemplate()
newUsageTemplate := strings.NewReplacer("{{.UseLine}}", "kubectl {{.UseLine}}", "{{.CommandPath}}", "kubectl {{.CommandPath}}").Replace(oldUsageTemplate)
rootCmd.SetUsageTemplate(newUsageTemplate)
}
rootCmd.PersistentFlags().StringVar(&rootInfo.KSCloudBEURLsDep, "environment", "", envFlagUsage)
rootCmd.PersistentFlags().StringVar(&rootInfo.KSCloudBEURLs, "env", "", envFlagUsage)
rootCmd.PersistentFlags().MarkDeprecated("environment", "use 'env' instead")
@@ -70,7 +81,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
// Supported commands
rootCmd.AddCommand(scan.GetScanCommand(ks))
rootCmd.AddCommand(download.GeDownloadCmd(ks))
rootCmd.AddCommand(download.GetDownloadCmd(ks))
rootCmd.AddCommand(delete.GetDeleteCmd(ks))
rootCmd.AddCommand(list.GetListCmd(ks))
rootCmd.AddCommand(submit.GetSubmitCmd(ks))
@@ -78,6 +89,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd.AddCommand(version.GetVersionCmd())
rootCmd.AddCommand(config.GetConfigCmd(ks))
rootCmd.AddCommand(update.GetUpdateCmd())
rootCmd.AddCommand(fix.GetFixCmd(ks))
return rootCmd
}

View File

@@ -1,6 +1,7 @@
package scan
import (
"context"
"fmt"
"io"
"os"
@@ -18,28 +19,28 @@ import (
)
var (
controlExample = `
controlExample = fmt.Sprintf(`
# Scan the 'privileged container' control
kubescape scan control "privileged container"
%[1]s scan control "privileged container"
# Scan list of controls separated with a comma
kubescape scan control "privileged container","HostPath mount"
%[1]s scan control "privileged container","HostPath mount"
# Scan list of controls using the control ID separated with a comma
kubescape scan control C-0058,C-0057
%[1]s scan control C-0058,C-0057
Run 'kubescape list controls' for the list of supported controls
Run '%[1]s list controls' for the list of supported controls
Control documentation:
https://hub.armosec.io/docs/controls
`
`, cautils.ExecName())
)
// controlCmd represents the control command
func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
return &cobra.Command{
Use: "control <control names list>/<control ids list>",
Short: "The controls you wish to use. Run 'kubescape list controls' for the list of supported controls",
Short: fmt.Sprintf("The controls you wish to use. Run '%[1]s list controls' for the list of supported controls", cautils.ExecName()),
Example: controlExample,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
@@ -67,7 +68,7 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
if len(args) == 0 {
scanInfo.ScanAll = true
} else { // expected control or list of control sepparated by ","
} else { // expected control or list of control separated by ","
// Read controls from input args
scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), apisv1.KindControl)
@@ -96,11 +97,12 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
return err
}
results, err := ks.Scan(scanInfo)
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err := results.HandleResults(); err != nil {
if err := results.HandleResults(ctx); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {

View File

@@ -1,6 +1,7 @@
package scan
import (
"context"
"errors"
"fmt"
"io"
@@ -14,30 +15,31 @@ import (
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var (
frameworkExample = `
frameworkExample = fmt.Sprintf(`
# Scan all frameworks
kubescape scan framework all
%[1]s scan framework all
# Scan the NSA framework
kubescape scan framework nsa
%[1]s scan framework nsa
# Scan the NSA and MITRE framework
kubescape scan framework nsa,mitre
%[1]s scan framework nsa,mitre
# Scan all frameworks
kubescape scan framework all
%[1]s scan framework all
# Scan kubernetes YAML manifest files (single file or glob)
kubescape scan framework nsa .
%[1]s scan framework nsa .
Run 'kubescape list frameworks' for the list of supported frameworks
`
Run '%[1]s list frameworks' for the list of supported frameworks
`, cautils.ExecName())
ErrUnknownSeverity = errors.New("unknown severity")
)
@@ -46,7 +48,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
return &cobra.Command{
Use: "framework <framework names list> [`<glob pattern>`/`-`] [flags]",
Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks",
Short: fmt.Sprintf("The framework you wish to use. Run '%[1]s list frameworks' for the list of supported frameworks", cautils.ExecName()),
Example: frameworkExample,
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
Args: func(cmd *cobra.Command, args []string) error {
@@ -71,6 +73,9 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
}
scanInfo.FrameworkScan = true
// We do not scan all frameworks by default when triggering scan from the CLI
scanInfo.ScanAll = false
var frameworks []string
if len(args) == 0 { // scan all frameworks
@@ -80,11 +85,12 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
frameworks = strings.Split(args[0], ",")
if cautils.StringInSlice(frameworks, "all") != cautils.ValueNotFound {
scanInfo.ScanAll = true
frameworks = []string{}
frameworks = getter.NativeFrameworks
}
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
logger.L().Debug("List of input files", helpers.Interface("patterns", scanInfo.InputPatterns))
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
@@ -103,12 +109,13 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
scanInfo.SetPolicyIdentifiers(frameworks, apisv1.KindFramework)
results, err := ks.Scan(scanInfo)
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(); err != nil {
if err = results.HandleResults(ctx); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
@@ -161,14 +168,14 @@ func countersExceedSeverityThreshold(severityCounters reportsummary.ISeverityCou
}
// terminateOnExceedingSeverity terminates the application on exceeding severity
func terminateOnExceedingSeverity(scanInfo *cautils.ScanInfo, l logger.ILogger) {
func terminateOnExceedingSeverity(scanInfo *cautils.ScanInfo, l helpers.ILogger) {
l.Fatal("result exceeds severity threshold", helpers.String("set severity threshold", scanInfo.FailThresholdSeverity))
}
// enforceSeverityThresholds ensures that the scan results are below the defined severity threshold
//
// The function forces the application to terminate with an exit code 1 if at least one control failed control that exceeds the set severity threshold
func enforceSeverityThresholds(severityCounters reportsummary.ISeverityCounters, scanInfo *cautils.ScanInfo, onExceed func(*cautils.ScanInfo, logger.ILogger)) {
func enforceSeverityThresholds(severityCounters reportsummary.ISeverityCounters, scanInfo *cautils.ScanInfo, onExceed func(*cautils.ScanInfo, helpers.ILogger)) {
// If a severity threshold is not set, we dont need to enforce it
if scanInfo.FailThresholdSeverity == "" {
return

View File

@@ -3,32 +3,33 @@ package scan
import (
"flag"
"fmt"
"strings"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var scanCmdExamples = `
var scanCmdExamples = fmt.Sprintf(`
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defined frameworks
# Scan current cluster with all frameworks
kubescape scan --enable-host-scan --verbose
%[1]s scan --enable-host-scan --verbose
# Scan kubernetes YAML manifest files
kubescape scan .
%[1]s scan .
# Scan and save the results in the JSON format
kubescape scan --format json --output results.json --format-version=v2
%[1]s scan --format json --output results.json --format-version=v2
# Display all resources
kubescape scan --verbose
%[1]s scan --verbose
# Scan different clusters from the kubectl context
kubescape scan --kube-context <kubernetes context>
`
%[1]s scan --kube-context <kubernetes context>
`, cautils.ExecName())
func GetScanCommand(ks meta.IKubescape) *cobra.Command {
var scanInfo cautils.ScanInfo
@@ -42,7 +43,6 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
if args[0] != "framework" && args[0] != "control" {
scanInfo.ScanAll = true
return getFrameworkCmd(ks, &scanInfo).RunE(cmd, append([]string{"all"}, args...))
}
}
@@ -51,8 +51,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
scanInfo.ScanAll = true
return getFrameworkCmd(ks, &scanInfo).RunE(cmd, []string{"all"})
return getFrameworkCmd(ks, &scanInfo).RunE(cmd, []string{strings.Join(getter.NativeFrameworks, ",")})
}
return nil
},
@@ -66,7 +65,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
}
scanCmd.PersistentFlags().StringVarP(&scanInfo.Credentials.Account, "account", "", "", "Kubescape SaaS account ID. Default will load account ID from cache")
// scanCmd.PersistentFlags().BoolVar(&scanInfo.CreateAccount, "create-account", false, "Create a Kubescape SaaS account ID account ID is not found in cache. After creating the account, the account ID will be saved in cache. In addition, the scanning results will be uploaded to the Kubescape SaaS")
scanCmd.PersistentFlags().BoolVar(&scanInfo.CreateAccount, "create-account", false, "Create a Kubescape SaaS account ID account ID is not found in cache. After creating the account, the account ID will be saved in cache. In addition, the scanning results will be uploaded to the Kubescape SaaS")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Credentials.ClientID, "client-id", "", "", "Kubescape SaaS client ID. Default will load client ID from cache, read more - https://hub.armosec.io/docs/authentication")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Credentials.SecretKey, "secret-key", "", "", "Kubescape SaaS secret key. Default will load secret key from cache, read more - https://hub.armosec.io/docs/authentication")
scanCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context")
@@ -87,7 +86,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host scanner DaemonSet. Use this flag cautiously")
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v1", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v2", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
scanCmd.PersistentFlags().StringVar(&scanInfo.CustomClusterName, "cluster-name", "", "Set the custom name of the cluster. Not same as the kube-context flag")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Submit the scan results to Kubescape SaaS where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.OmitRawResources, "omit-raw-resources", "", false, "Omit raw resources from the output. By default the raw resources are included in the output")

View File

@@ -1,7 +1,8 @@
package scan
import (
logger "github.com/kubescape/go-logger"
"context"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -160,7 +161,7 @@ func Test_enforceSeverityThresholds(t *testing.T) {
want := tc.Want
got := false
onExceed := func(*cautils.ScanInfo, logger.ILogger) {
onExceed := func(*cautils.ScanInfo, helpers.ILogger) {
got = true
}
@@ -193,6 +194,7 @@ 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) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]

View File

@@ -1,8 +1,9 @@
package scan
import (
"github.com/kubescape/kubescape/v2/core/cautils"
"testing"
"github.com/kubescape/kubescape/v2/core/cautils"
)
// Test_validateControlScanInfo tests how scan info is validated for the `scan control` command

View File

@@ -1,6 +1,7 @@
package submit
import (
"context"
"fmt"
logger "github.com/kubescape/go-logger"
@@ -26,7 +27,7 @@ func getExceptionsCmd(ks meta.IKubescape, submitInfo *metav1.Submit) *cobra.Comm
logger.L().Fatal(err.Error())
}
if err := ks.SubmitExceptions(&submitInfo.Credentials, args[0]); err != nil {
if err := ks.SubmitExceptions(context.TODO(), &submitInfo.Credentials, args[0]); err != nil {
logger.L().Fatal(err.Error())
}
},

View File

@@ -1,6 +1,7 @@
package submit
import (
"context"
"fmt"
"github.com/google/uuid"
@@ -19,13 +20,13 @@ import (
)
var (
rbacExamples = `
rbacExamples = fmt.Sprintf(`
# Submit cluster's Role-Based Access Control(RBAC)
kubescape submit rbac
%[1]s submit rbac
# Submit cluster's Role-Based Access Control(RBAC) with account ID
kubescape submit rbac --account <account-id>
`
%[1]s submit rbac --account <account-id>
`, cautils.ExecName())
)
// getRBACCmd represents the RBAC command
@@ -36,7 +37,7 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Example: rbacExamples,
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
if err := flagValidationSubmit(submitInfo); err != nil {
return err
@@ -51,7 +52,7 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
}
if clusterConfig.GetAccountID() == "" {
return fmt.Errorf("account ID is not set, run 'kubescape submit rbac --account <account-id>'")
return fmt.Errorf("account ID is not set, run '%[1]s submit rbac --account <account-id>'", cautils.ExecName())
}
// list RBAC
@@ -66,7 +67,7 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Reporter: r,
}
if err := ks.Submit(submitInterfaces); err != nil {
if err := ks.Submit(context.TODO(), submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil

View File

@@ -1,11 +1,13 @@
package submit
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/google/uuid"
"github.com/kubescape/kubescape/v2/core/cautils"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
logger "github.com/kubescape/go-logger"
@@ -50,7 +52,7 @@ func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinter
func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
var resultsCmd = &cobra.Command{
Use: "results <json file>\nExample:\n$ kubescape submit results path/to/results.json --format-version v2",
Use: fmt.Sprintf("results <json file>\nExample:\n$ %[1]s submit results path/to/results.json --format-version v2", cautils.ExecName()),
Short: "Submit a pre scanned results file. The file must be in json format",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -81,13 +83,13 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Reporter: r,
}
if err := ks.Submit(submitInterfaces); err != nil {
if err := ks.Submit(context.TODO(), submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v2", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
return resultsCmd
}

View File

@@ -1,18 +1,21 @@
package submit
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var submitCmdExamples = `
var submitCmdExamples = fmt.Sprintf(`
# Submit Kubescape scan results file
kubescape submit results
%[1]s submit results
# Submit exceptions file to Kubescape SaaS
kubescape submit exceptions
`
%[1]s submit exceptions
`, cautils.ExecName())
func GetSubmitCmd(ks meta.IKubescape) *cobra.Command {
var submitInfo metav1.Submit

View File

@@ -5,6 +5,7 @@ package update
// kubescape update
import (
"fmt"
"os/exec"
"runtime"
@@ -13,11 +14,17 @@ import (
"github.com/spf13/cobra"
)
var updateCmdExamples = fmt.Sprintf(`
# Update to the latest kubescape release
%[1]s update
`, cautils.ExecName())
func GetUpdateCmd() *cobra.Command {
updateCmd := &cobra.Command{
Use: "update",
Short: "Update your version",
Long: ``,
Use: "update",
Short: "Update your version",
Long: ``,
Example: updateCmdExamples,
RunE: func(_ *cobra.Command, args []string) error {
//Checking the user's version of kubescape to the latest release
if cautils.BuildNumber == cautils.LatestReleaseVersion {

View File

@@ -0,0 +1,7 @@
//go:build !gitenabled
package version
func isGitEnabled() bool {
return false
}

View File

@@ -0,0 +1,7 @@
//go:build gitenabled
package version
func isGitEnabled() bool {
return true
}

View File

@@ -1,6 +1,7 @@
package version
import (
"context"
"fmt"
"os"
@@ -14,9 +15,14 @@ func GetVersionCmd() *cobra.Command {
Short: "Get current version",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "version"))
fmt.Fprintln(os.Stdout, "Your current version is: "+cautils.BuildNumber)
ctx := context.TODO()
v := cautils.NewIVersionCheckHandler(ctx)
v.CheckLatestVersion(ctx, cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "version"))
fmt.Fprintf(os.Stdout,
"Your current version is: %s [git enabled in build: %t]\n",
cautils.BuildNumber,
isGitEnabled(),
)
return nil
},
}

View File

@@ -70,7 +70,7 @@ type ITenantConfig interface {
// set
SetTenant() error
UpdateCachedConfig() error
DeleteCachedConfig() error
DeleteCachedConfig(ctx context.Context) error
// getters
GetContextName() string
@@ -175,9 +175,9 @@ func (lc *LocalConfig) UpdateCachedConfig() error {
return updateConfigFile(lc.configObj)
}
func (lc *LocalConfig) DeleteCachedConfig() error {
func (lc *LocalConfig) DeleteCachedConfig(ctx context.Context) error {
if err := DeleteConfigFile(); err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
return nil
}
@@ -330,12 +330,12 @@ func (c *ClusterConfig) UpdateCachedConfig() error {
return updateConfigFile(c.configObj)
}
func (c *ClusterConfig) DeleteCachedConfig() error {
func (c *ClusterConfig) DeleteCachedConfig(ctx context.Context) error {
if err := c.deleteConfigMap(); err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if err := DeleteConfigFile(); err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
return nil
}

View File

@@ -1,6 +1,8 @@
package cautils
import (
"context"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
@@ -36,7 +38,7 @@ type OPASessionObj struct {
OmitRawResources bool // omit raw resources from output
}
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
return &OPASessionObj{
Report: &reporthandlingv2.PostureReport{},
Policies: frameworks,
@@ -48,7 +50,7 @@ func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SRe
ResourceToControlsMap: make(map[string][]string),
ResourceSource: make(map[string]reporthandling.Source),
SessionID: scanInfo.ScanID,
Metadata: scanInfoToScanMetadata(scanInfo),
Metadata: scanInfoToScanMetadata(ctx, scanInfo),
OmitRawResources: scanInfo.OmitRawResources,
}
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/armosec/utils-go/boolutils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
func NewPolicies() *Policies {
@@ -29,7 +30,16 @@ func (policies *Policies) Set(frameworks []reporthandling.Framework, version str
if len(compatibleRules) > 0 {
frameworks[i].Controls[j].Rules = compatibleRules
policies.Controls[frameworks[i].Controls[j].ControlID] = frameworks[i].Controls[j]
} else { // if the control type is manual review, add it to the list of controls
actionRequiredStr := frameworks[i].Controls[j].GetActionRequiredAttribute()
if actionRequiredStr == "" {
continue
}
if actionRequiredStr == string(apis.SubStatusManualReview) {
policies.Controls[frameworks[i].Controls[j].ControlID] = frameworks[i].Controls[j]
}
}
}
}

View File

@@ -7,6 +7,7 @@ import (
spinnerpkg "github.com/briandowns/spinner"
"github.com/fatih/color"
"github.com/mattn/go-isatty"
"github.com/schollz/progressbar/v3"
)
var FailureDisplay = color.New(color.Bold, color.FgHiRed).FprintfFunc()
@@ -39,3 +40,28 @@ func StopSpinner() {
}
spinner.Stop()
}
type ProgressHandler struct {
title string
pb *progressbar.ProgressBar
}
func NewProgressHandler(title string) *ProgressHandler {
return &ProgressHandler{title: title}
}
func (p *ProgressHandler) Start(allSteps int) {
if isatty.IsTerminal(os.Stderr.Fd()) {
p.pb = progressbar.Default(int64(allSteps), p.title)
} else {
p.pb = progressbar.DefaultSilent(int64(allSteps), p.title)
}
}
func (p *ProgressHandler) ProgressJob(step int, message string) {
p.pb.Add(step)
p.pb.Describe(message)
}
func (p *ProgressHandler) Stop() {
}

View File

@@ -2,6 +2,7 @@ package cautils
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
@@ -31,7 +32,7 @@ const (
)
// LoadResourcesFromHelmCharts scans a given path (recursively) for helm charts, renders the templates and returns a map of workloads and a map of chart names
func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterface.IMetadata, map[string]string) {
func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, map[string]string) {
directories, _ := listDirs(basePath)
helmDirectories := make([]string, 0)
for _, dir := range directories {
@@ -47,7 +48,7 @@ func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterfac
if err == nil {
wls, errs := chart.GetWorkloadsWithDefaultValues()
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("Rendering of Helm chart template '%s', failed: %v", chart.GetName(), errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("Rendering of Helm chart template '%s', failed: %v", chart.GetName(), errs))
continue
}
@@ -63,7 +64,7 @@ func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterfac
// If the contents at given path is a Kustomize Directory, LoadResourcesFromKustomizeDirectory will
// generate yaml files using "Kustomize" & renders a map of workloads from those yaml files
func LoadResourcesFromKustomizeDirectory(basePath string) (map[string][]workloadinterface.IMetadata, string) {
func LoadResourcesFromKustomizeDirectory(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, string) {
isKustomizeDirectory := IsKustomizeDirectory(basePath)
isKustomizeFile := IsKustomizeFile(basePath)
if ok := isKustomizeDirectory || isKustomizeFile; !ok {
@@ -87,7 +88,7 @@ func LoadResourcesFromKustomizeDirectory(basePath string) (map[string][]workload
kustomizeDirectoryName := GetKustomizeDirectoryName(newBasePath)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("Rendering yaml from Kustomize failed: %v", errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("Rendering yaml from Kustomize failed: %v", errs))
}
for k, v := range wls {
@@ -96,10 +97,10 @@ func LoadResourcesFromKustomizeDirectory(basePath string) (map[string][]workload
return sourceToWorkloads, kustomizeDirectoryName
}
func LoadResourcesFromFiles(input, rootPath string) map[string][]workloadinterface.IMetadata {
func LoadResourcesFromFiles(ctx context.Context, input, rootPath string) map[string][]workloadinterface.IMetadata {
files, errs := listFiles(input)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("%v", errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("%v", errs))
}
if len(files) == 0 {
return nil
@@ -107,7 +108,7 @@ func LoadResourcesFromFiles(input, rootPath string) map[string][]workloadinterfa
workloads, errs := loadFiles(rootPath, files)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("%v", errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("%v", errs))
}
return workloads

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"os"
"path/filepath"
"strings"
@@ -30,7 +31,7 @@ func TestListFiles(t *testing.T) {
}
func TestLoadResourcesFromFiles(t *testing.T) {
workloads := LoadResourcesFromFiles(onlineBoutiquePath(), "")
workloads := LoadResourcesFromFiles(context.TODO(), onlineBoutiquePath(), "")
assert.Equal(t, 12, len(workloads))
for i, w := range workloads {
@@ -44,7 +45,7 @@ func TestLoadResourcesFromFiles(t *testing.T) {
}
func TestLoadResourcesFromHelmCharts(t *testing.T) {
sourceToWorkloads, sourceToChartName := LoadResourcesFromHelmCharts(helmChartPath())
sourceToWorkloads, sourceToChartName := LoadResourcesFromHelmCharts(context.TODO(), helmChartPath())
assert.Equal(t, 6, len(sourceToWorkloads))
for file, workloads := range sourceToWorkloads {

View File

@@ -5,9 +5,10 @@ import (
"strings"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/opa-utils/gitregostore"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
"github.com/kubescape/regolibrary/gitregostore"
)
// =======================================================================================================================
@@ -105,19 +106,6 @@ func (drp *DownloadReleasedPolicy) SetRegoObjects() error {
return drp.gs.SetRegoObjects()
}
func isNativeFramework(framework string) bool {
return contains(NativeFrameworks, framework)
}
func contains(s []string, str string) bool {
for _, v := range s {
if strings.EqualFold(v, str) {
return true
}
}
return false
}
func (drp *DownloadReleasedPolicy) GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
exceptions, err := drp.gs.GetSystemPostureExceptionPolicies()
if err != nil {

View File

@@ -0,0 +1,170 @@
package getter
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestReleasedPolicy(t *testing.T) {
t.Parallel()
p := NewDownloadReleasedPolicy()
t.Run("should initialize objects", func(t *testing.T) {
t.Parallel()
// acquire from github or from local fixture
hydrateReleasedPolicyFromMock(t, p)
require.NoError(t, p.SetRegoObjects())
t.Run("with ListControls", func(t *testing.T) {
t.Parallel()
controlIDs, err := p.ListControls()
require.NoError(t, err)
require.NotEmpty(t, controlIDs)
sampleSize := min(len(controlIDs), 10)
for _, toPin := range controlIDs[:sampleSize] {
// Example of a returned "ID": `C-0154|Ensure_that_the_--client-cert-auth_argument_is_set_to_true|`
controlString := toPin
parts := strings.Split(controlString, "|")
controlID := parts[0]
t.Run(fmt.Sprintf("with GetControl(%q)", controlID), func(t *testing.T) {
t.Parallel()
ctrl, err := p.GetControl(controlID)
require.NoError(t, err)
require.NotEmpty(t, ctrl)
require.Equal(t, controlID, ctrl.ControlID)
})
}
t.Run("with unknown GetControl()", func(t *testing.T) {
t.Parallel()
ctrl, err := p.GetControl("zork")
require.Error(t, err)
require.Nil(t, ctrl)
})
})
t.Run("with GetFrameworks", func(t *testing.T) {
t.Parallel()
frameworks, err := p.GetFrameworks()
require.NoError(t, err)
require.NotEmpty(t, frameworks)
for _, toPin := range frameworks {
framework := toPin
require.NotEmpty(t, framework)
require.NotEmpty(t, framework.Name)
t.Run(fmt.Sprintf("with GetFramework(%q)", framework.Name), func(t *testing.T) {
t.Parallel()
fw, err := p.GetFramework(framework.Name)
require.NoError(t, err)
require.NotNil(t, fw)
require.EqualValues(t, framework, *fw)
})
}
t.Run("with unknown GetFramework()", func(t *testing.T) {
t.Parallel()
ctrl, err := p.GetFramework("zork")
require.Error(t, err)
require.Nil(t, ctrl)
})
t.Run("with ListFrameworks", func(t *testing.T) {
t.Parallel()
frameworkIDs, err := p.ListFrameworks()
require.NoError(t, err)
require.NotEmpty(t, frameworkIDs)
require.Len(t, frameworkIDs, len(frameworks))
})
})
t.Run("with GetControlsInput", func(t *testing.T) {
t.Parallel()
controlInputs, err := p.GetControlsInputs("") // NOTE: cluster name currently unused
require.NoError(t, err)
require.NotEmpty(t, controlInputs)
})
t.Run("with GetAttackTracks", func(t *testing.T) {
t.Parallel()
attackTracks, err := p.GetAttackTracks()
require.NoError(t, err)
require.NotEmpty(t, attackTracks)
})
t.Run("with GetExceptions", func(t *testing.T) {
t.Parallel()
exceptions, err := p.GetExceptions("") // NOTE: cluster name currently unused
require.NoError(t, err)
require.NotEmpty(t, exceptions)
})
})
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func hydrateReleasedPolicyFromMock(t testing.TB, p *DownloadReleasedPolicy) {
regoFile := testRegoFile("policy")
if _, err := os.Stat(regoFile); errors.Is(err, fs.ErrNotExist) {
// retrieve fixture from latest released policy from github.
//
// NOTE: to update the mock, just delete the testdata/policy.json file and run the tests again.
t.Logf("updating fixture file %q from github", regoFile)
require.NoError(t, p.SetRegoObjects())
require.NotNil(t, p.gs)
require.NoError(t,
SaveInFile(p.gs, regoFile),
)
return
}
// we have a mock fixture: load this rather than calling github
t.Logf("populating rego policy from fixture file %q", regoFile)
buf, err := os.ReadFile(regoFile)
require.NoError(t, err)
require.NoError(t,
json.Unmarshal(buf, p.gs),
)
}
func testRegoFile(framework string) string {
return filepath.Join(currentDir(), "testdata", fmt.Sprintf("%s.json", framework))
}

View File

@@ -2,12 +2,10 @@ package getter
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
"path/filepath"
"strings"
)
@@ -24,10 +22,10 @@ func SaveInFile(policy interface{}, pathStr string) error {
err = os.WriteFile(pathStr, encodedData, 0644) //nolint:gosec
if err != nil {
if os.IsNotExist(err) {
pathDir := path.Dir(pathStr)
pathDir := filepath.Dir(pathStr)
// pathDir could contain subdirectories
if err := os.MkdirAll(pathDir, 0755); err != nil {
return err
if erm := os.MkdirAll(pathDir, 0755); erm != nil {
return erm
}
} else {
return err
@@ -41,13 +39,6 @@ func SaveInFile(policy interface{}, pathStr string) error {
return nil
}
// JSONDecoder returns JSON decoder for given string
func JSONDecoder(origin string) *json.Decoder {
dec := json.NewDecoder(strings.NewReader(origin))
dec.UseNumber()
return dec
}
func HttpDelete(httpClient *http.Client, fullURL string, headers map[string]string) (string, error) {
req, err := http.NewRequest("DELETE", fullURL, nil)
@@ -66,6 +57,7 @@ func HttpDelete(httpClient *http.Client, fullURL string, headers map[string]stri
}
return respStr, nil
}
func HttpGetter(httpClient *http.Client, fullURL string, headers map[string]string) (string, error) {
req, err := http.NewRequest("GET", fullURL, nil)

View File

@@ -0,0 +1,68 @@
package getter
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestGetDefaultPath(t *testing.T) {
t.Parallel()
const name = "mine"
pth := GetDefaultPath(name)
require.Equal(t, name, filepath.Base(pth))
require.Equal(t, ".kubescape", filepath.Base(filepath.Dir(pth)))
}
func TestSaveInFile(t *testing.T) {
t.Parallel()
dir, err := os.MkdirTemp(".", "test")
require.NoError(t, err)
defer func() {
_ = os.RemoveAll(dir)
}()
policy := map[string]interface{}{
"key": "value",
"number": 1.00,
}
t.Run("should save data as JSON (target folder exists)", func(t *testing.T) {
target := filepath.Join(dir, "target.json")
require.NoError(t, SaveInFile(policy, target))
buf, err := os.ReadFile(target)
require.NoError(t, err)
var retrieved interface{}
require.NoError(t, json.Unmarshal(buf, &retrieved))
require.EqualValues(t, policy, retrieved)
})
t.Run("should save data as JSON (new target folder)", func(t *testing.T) {
target := filepath.Join(dir, "subdir", "target.json")
require.NoError(t, SaveInFile(policy, target))
buf, err := os.ReadFile(target)
require.NoError(t, err)
var retrieved interface{}
require.NoError(t, json.Unmarshal(buf, &retrieved))
require.EqualValues(t, policy, retrieved)
})
t.Run("should error", func(t *testing.T) {
badPolicy := map[string]interface{}{
"key": "value",
"number": 1.00,
"err": func() {},
}
target := filepath.Join(dir, "error.json")
require.Error(t, SaveInFile(badPolicy, target))
})
}

View File

@@ -0,0 +1,26 @@
package getter
import (
"strings"
stdjson "encoding/json"
jsoniter "github.com/json-iterator/go"
)
var (
json jsoniter.API
)
func init() {
// NOTE(fredbi): attention, this configuration rounds floats down to 6 digits
// For finer-grained config, see: https://pkg.go.dev/github.com/json-iterator/go#section-readme
json = jsoniter.ConfigFastest
}
// JSONDecoder returns JSON decoder for given string
func JSONDecoder(origin string) *stdjson.Decoder {
dec := stdjson.NewDecoder(strings.NewReader(origin))
dec.UseNumber()
return dec
}

View File

@@ -0,0 +1,32 @@
package getter
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestJSONDecoder(t *testing.T) {
t.Run("should decode json string", func(t *testing.T) {
const input = `"xyz"`
d := JSONDecoder(input)
var receiver string
require.NoError(t, d.Decode(&receiver))
require.Equal(t, "xyz", receiver)
})
t.Run("should decode json number", func(t *testing.T) {
const input = `123.01`
d := JSONDecoder(input)
var receiver float64
require.NoError(t, d.Decode(&receiver))
require.Equal(t, 123.01, receiver)
})
t.Run("requires json quotes", func(t *testing.T) {
const input = `xyz`
d := JSONDecoder(input)
var receiver string
require.Error(t, d.Decode(&receiver))
})
}

View File

@@ -2,7 +2,6 @@ package getter
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"

View File

@@ -0,0 +1,273 @@
package getter
import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
)
func mockAttackTracks() []v1alpha1.AttackTrack {
return []v1alpha1.AttackTrack{
{
ApiVersion: "v1",
Kind: "track",
Metadata: map[string]interface{}{"label": "name"},
Spec: v1alpha1.AttackTrackSpecification{
Version: "v2",
Description: "a mock",
Data: v1alpha1.AttackTrackStep{
Name: "track1",
Description: "mock-step",
SubSteps: []v1alpha1.AttackTrackStep{
{
Name: "track1",
Description: "mock-step",
Controls: []v1alpha1.IAttackTrackControl{
mockControlPtr("control-1"),
},
},
},
Controls: []v1alpha1.IAttackTrackControl{
mockControlPtr("control-2"),
mockControlPtr("control-3"),
},
},
},
},
{
ApiVersion: "v1",
Kind: "track",
Metadata: map[string]interface{}{"label": "stuff"},
Spec: v1alpha1.AttackTrackSpecification{
Version: "v1",
Description: "another mock",
Data: v1alpha1.AttackTrackStep{
Name: "track2",
Description: "mock-step2",
SubSteps: []v1alpha1.AttackTrackStep{
{
Name: "track3",
Description: "mock-step",
Controls: []v1alpha1.IAttackTrackControl{
mockControlPtr("control-4"),
},
},
},
Controls: []v1alpha1.IAttackTrackControl{
mockControlPtr("control-5"),
mockControlPtr("control-6"),
},
},
},
},
}
}
func mockFrameworks() []reporthandling.Framework {
id1s := []string{"control-1", "control-2"}
id2s := []string{"control-3", "control-4"}
id3s := []string{"control-5", "control-6"}
return []reporthandling.Framework{
{
PortalBase: armotypes.PortalBase{
Name: "mock-1",
},
CreationTime: "now",
Description: "mock-1",
Controls: []reporthandling.Control{
mockControl("control-1"),
mockControl("control-2"),
},
ControlsIDs: &id1s,
SubSections: map[string]*reporthandling.FrameworkSubSection{
"section1": {
ID: "section-id",
ControlIDs: id1s,
},
},
},
{
PortalBase: armotypes.PortalBase{
Name: "mock-2",
},
CreationTime: "then",
Description: "mock-2",
Controls: []reporthandling.Control{
mockControl("control-3"),
mockControl("control-4"),
},
ControlsIDs: &id2s,
SubSections: map[string]*reporthandling.FrameworkSubSection{
"section2": {
ID: "section-id",
ControlIDs: id2s,
},
},
},
{
PortalBase: armotypes.PortalBase{
Name: "nsa",
},
CreationTime: "tomorrow",
Description: "nsa mock",
Controls: []reporthandling.Control{
mockControl("control-5"),
mockControl("control-6"),
},
ControlsIDs: &id3s,
SubSections: map[string]*reporthandling.FrameworkSubSection{
"section2": {
ID: "section-id",
ControlIDs: id3s,
},
},
},
}
}
func mockControl(controlID string) reporthandling.Control {
return reporthandling.Control{
ControlID: controlID,
}
}
func mockControlPtr(controlID string) *reporthandling.Control {
val := mockControl(controlID)
return &val
}
func mockExceptions() []armotypes.PostureExceptionPolicy {
return []armotypes.PostureExceptionPolicy{
{
PolicyType: "postureExceptionPolicy",
CreationTime: "now",
Actions: []armotypes.PostureExceptionPolicyActions{
"alertOnly",
},
Resources: []armotypes.PortalDesignator{
{
DesignatorType: "Attributes",
Attributes: map[string]string{
"kind": "Pod",
"name": "coredns-[A-Za-z0-9]+-[A-Za-z0-9]+",
"namespace": "kube-system",
},
},
{
DesignatorType: "Attributes",
Attributes: map[string]string{
"kind": "Pod",
"name": "etcd-.*",
"namespace": "kube-system",
},
},
},
PosturePolicies: []armotypes.PosturePolicy{
{
FrameworkName: "MITRE",
ControlID: "C-.*",
},
{
FrameworkName: "another-framework",
ControlID: "a regexp",
},
},
},
{
PolicyType: "postureExceptionPolicy",
CreationTime: "then",
Actions: []armotypes.PostureExceptionPolicyActions{
"alertOnly",
},
Resources: []armotypes.PortalDesignator{
{
DesignatorType: "Attributes",
Attributes: map[string]string{
"kind": "Deployment",
"name": "my-regexp",
},
},
{
DesignatorType: "Attributes",
Attributes: map[string]string{
"kind": "Secret",
"name": "another-regexp",
},
},
},
PosturePolicies: []armotypes.PosturePolicy{
{
FrameworkName: "yet-another-framework",
ControlID: "a regexp",
},
},
},
}
}
func mockTenantResponse() *TenantResponse {
return &TenantResponse{
TenantID: "id",
Token: "token",
Expires: "expiry-time",
AdminMail: "admin@example.com",
}
}
func mockCustomerConfig(cluster, scope string) func() *armotypes.CustomerConfig {
if cluster == "" {
cluster = "my-cluster"
}
if scope == "" {
scope = "default"
}
return func() *armotypes.CustomerConfig {
return &armotypes.CustomerConfig{
Name: "user",
Attributes: map[string]interface{}{
"label": "value",
},
Scope: armotypes.PortalDesignator{
DesignatorType: "Attributes",
Attributes: map[string]string{
"kind": "Cluster",
"name": cluster,
"scope": scope,
},
},
Settings: armotypes.Settings{
PostureControlInputs: map[string][]string{
"inputs-1": {"x1", "y2"},
"inputs-2": {"x2", "y2"},
},
PostureScanConfig: armotypes.PostureScanConfig{
ScanFrequency: armotypes.ScanFrequency("weekly"),
},
VulnerabilityScanConfig: armotypes.VulnerabilityScanConfig{
ScanFrequency: armotypes.ScanFrequency("daily"),
CriticalPriorityThreshold: 1,
HighPriorityThreshold: 2,
MediumPriorityThreshold: 3,
ScanNewDeployment: true,
AllowlistRegistries: []string{"a", "b"},
BlocklistRegistries: []string{"c", "d"},
},
SlackConfigurations: armotypes.SlackSettings{
Token: "slack-token",
},
},
}
}
}
func mockLoginResponse() *FeLoginResponse {
return &FeLoginResponse{
Token: "access-token",
RefreshToken: "refresh-token",
Expires: "expiry-time",
ExpiresIn: 123,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,13 @@ package getter
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
var NativeFrameworks = []string{"nsa", "mitre", "armobest", "devopsbest"}
var NativeFrameworks = []string{"allcontrols", "nsa", "mitre"}
func (api *KSCloudAPI) getFrameworkURL(frameworkName string) string {
u := url.URL{}
@@ -185,3 +184,16 @@ func parseHost(host string) (string, string) {
// default scheme
return "https", strings.Replace(host, "https://", "", 1)
}
func isNativeFramework(framework string) bool {
return contains(NativeFrameworks, framework)
}
func contains(s []string, str string) bool {
for _, v := range s {
if strings.EqualFold(v, str) {
return true
}
}
return false
}

View File

@@ -1,7 +1,7 @@
package getter
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
@@ -15,7 +15,19 @@ import (
// =======================================================================================================================
// ============================================== LoadPolicy =============================================================
// =======================================================================================================================
var DefaultLocalStore = getCacheDir()
var (
DefaultLocalStore = getCacheDir()
ErrNotImplemented = errors.New("feature is currently not supported")
ErrNotFound = errors.New("name not found")
ErrNameRequired = errors.New("missing required input framework name")
ErrIDRequired = errors.New("missing required input control ID")
ErrFrameworkNotMatching = errors.New("framework from file not matching")
ErrControlNotMatching = errors.New("framework from file not matching")
_ IPolicyGetter = &LoadPolicy{}
_ IExceptionsGetter = &LoadPolicy{}
)
func getCacheDir() string {
defaultDirPath := ".kubescape"
@@ -25,11 +37,12 @@ func getCacheDir() string {
return defaultDirPath
}
// Load policies from a local repository
// LoadPolicy loads policies from a local repository.
type LoadPolicy struct {
filePaths []string
}
// NewLoadPolicy builds a LoadPolicy.
func NewLoadPolicy(filePaths []string) *LoadPolicy {
return &LoadPolicy{
filePaths: filePaths,
@@ -38,118 +51,211 @@ func NewLoadPolicy(filePaths []string) *LoadPolicy {
// GetControl returns a control from the policy file.
func (lp *LoadPolicy) GetControl(controlID string) (*reporthandling.Control, error) {
control := &reporthandling.Control{}
filePath := lp.filePath()
if controlID == "" {
return nil, ErrIDRequired
}
// NOTE: this assumes that only the first path contains either a valid control descriptor or a framework descriptor
filePath := lp.filePath()
buf, err := os.ReadFile(filePath)
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, control); err != nil {
return control, err
// check if the file is a control descriptor: a ControlID field is populated.
var control reporthandling.Control
if err = json.Unmarshal(buf, &control); err == nil && control.ControlID != "" {
if strings.EqualFold(controlID, control.ControlID) {
return &control, nil
}
return nil, fmt.Errorf("controlID: %s: %w", controlID, ErrControlNotMatching)
}
if controlID == "" || strings.EqualFold(controlID, control.ControlID) {
return control, nil
}
framework, err := lp.GetFramework(control.Name)
if err != nil {
return nil, fmt.Errorf("control from file not matching")
// check if the file is a framework descriptor
var framework reporthandling.Framework
if err = json.Unmarshal(buf, &framework); err != nil {
return nil, err
}
for _, toPin := range framework.Controls {
ctrl := toPin
if strings.EqualFold(ctrl.ControlID, controlID) {
control = &ctrl
break
if strings.EqualFold(ctrl.ControlID, controlID) {
return &ctrl, nil
}
}
return control, nil
return nil, fmt.Errorf("controlID: %s: %w", controlID, ErrControlNotMatching)
}
// GetFramework retrieves a framework configuration from the policy paths.
func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framework, error) {
var framework reporthandling.Framework
var err error
if frameworkName == "" {
return nil, ErrNameRequired
}
for _, filePath := range lp.filePaths {
framework = reporthandling.Framework{}
f, err := os.ReadFile(filePath)
buf, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, &framework); err != nil {
var framework reporthandling.Framework
if err = json.Unmarshal(buf, &framework); err != nil {
return nil, err
}
if strings.EqualFold(frameworkName, framework.Name) {
break
return &framework, nil
}
}
if frameworkName != "" && !strings.EqualFold(frameworkName, framework.Name) {
return nil, fmt.Errorf("framework from file not matching")
}
return &framework, err
return nil, fmt.Errorf("framework: %s: %w", frameworkName, ErrFrameworkNotMatching)
}
// GetFrameworks returns all configured framework descriptors.
func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) {
frameworks := []reporthandling.Framework{}
var err error
return frameworks, err
}
frameworks := make([]reporthandling.Framework, 0, 10)
seenFws := make(map[string]struct{})
func (lp *LoadPolicy) ListFrameworks() ([]string, error) {
fwNames := []string{}
framework := &reporthandling.Framework{}
for _, f := range lp.filePaths {
file, err := os.ReadFile(f)
if err == nil {
if err := json.Unmarshal(file, framework); err == nil {
if !contains(fwNames, framework.Name) {
fwNames = append(fwNames, framework.Name)
}
}
buf, err := os.ReadFile(f)
if err != nil {
return nil, err
}
var framework reporthandling.Framework
if err = json.Unmarshal(buf, &framework); err != nil {
// ignore invalid framework files
continue
}
// dedupe
_, alreadyLoaded := seenFws[framework.Name]
if alreadyLoaded {
continue
}
seenFws[framework.Name] = struct{}{}
frameworks = append(frameworks, framework)
}
return fwNames, nil
return frameworks, nil
}
// ListFrameworks lists the names of all configured frameworks in this policy.
func (lp *LoadPolicy) ListFrameworks() ([]string, error) {
frameworkNames := make([]string, 0, 10)
for _, f := range lp.filePaths {
buf, err := os.ReadFile(f)
if err != nil {
return nil, err
}
var framework reporthandling.Framework
if err := json.Unmarshal(buf, &framework); err != nil {
continue
}
if framework.Name == "" || contains(frameworkNames, framework.Name) {
continue
}
frameworkNames = append(frameworkNames, framework.Name)
}
return frameworkNames, nil
}
// ListControls returns the list of controls for this framework.
//
// At this moment, controls are listed for one single configured framework.
func (lp *LoadPolicy) ListControls() ([]string, error) {
// TODO - Support
return []string{}, fmt.Errorf("loading controls list from file is not supported")
}
func (lp *LoadPolicy) GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
controlIDs := make([]string, 0, 100)
filePath := lp.filePath()
exception := []armotypes.PostureExceptionPolicy{}
f, err := os.ReadFile(filePath)
buf, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
err = json.Unmarshal(f, &exception)
var framework reporthandling.Framework
if err = json.Unmarshal(buf, &framework); err != nil {
return nil, err
}
for _, ctrl := range framework.Controls {
controlIDs = append(controlIDs, ctrl.ControlID)
}
return controlIDs, nil
}
// GetExceptions retrieves configured exceptions.
//
// NOTE: the cluster parameter is not used at this moment.
func (lp *LoadPolicy) GetExceptions(_ /* clusterName */ string) ([]armotypes.PostureExceptionPolicy, error) {
// NOTE: this assumes that the first path contains a valid exceptions descriptor
filePath := lp.filePath()
buf, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
exception := make([]armotypes.PostureExceptionPolicy, 0, 300)
err = json.Unmarshal(buf, &exception)
return exception, err
}
func (lp *LoadPolicy) GetControlsInputs(clusterName string) (map[string][]string, error) {
// GetControlsInputs retrieves the map of control configs.
//
// NOTE: the cluster parameter is not used at this moment.
func (lp *LoadPolicy) GetControlsInputs(_ /* clusterName */ string) (map[string][]string, error) {
// NOTE: this assumes that only the first path contains a valid control inputs descriptor
filePath := lp.filePath()
accountConfig := &armotypes.CustomerConfig{}
f, err := os.ReadFile(filePath)
fileName := filepath.Base(filePath)
buf, err := os.ReadFile(filePath)
if err != nil {
formattedError := fmt.Errorf("Error opening %s file, \"controls-config\" will be downloaded from ARMO management portal", fileName)
formattedError := fmt.Errorf(
`Error opening %s file, "controls-config" will be downloaded from ARMO management portal`,
fileName,
)
return nil, formattedError
}
if err = json.Unmarshal(f, &accountConfig.Settings.PostureControlInputs); err == nil {
return accountConfig.Settings.PostureControlInputs, nil
controlInputs := make(map[string][]string, 100) // from armotypes.Settings.PostureControlInputs
if err = json.Unmarshal(buf, &controlInputs); err != nil {
formattedError := fmt.Errorf(
`Error reading %s file, %v, "controls-config" will be downloaded from ARMO management portal`,
fileName, err,
)
return nil, formattedError
}
formattedError := fmt.Errorf("Error reading %s file, %s, \"controls-config\" will be downloaded from ARMO management portal", fileName, err.Error())
return controlInputs, nil
}
return nil, formattedError
// GetAttackTracks yields the attack tracks from a config file.
func (lp *LoadPolicy) GetAttackTracks() ([]v1alpha1.AttackTrack, error) {
attackTracks := make([]v1alpha1.AttackTrack, 0, 20)
buf, err := os.ReadFile(lp.filePath())
if err != nil {
return nil, err
}
if err = json.Unmarshal(buf, &attackTracks); err != nil {
return nil, err
}
return attackTracks, nil
}
// temporary support for a list of files
@@ -159,18 +265,3 @@ func (lp *LoadPolicy) filePath() string {
}
return ""
}
func (lp *LoadPolicy) GetAttackTracks() ([]v1alpha1.AttackTrack, error) {
attackTracks := []v1alpha1.AttackTrack{}
f, err := os.ReadFile(lp.filePath())
if err != nil {
return nil, err
}
if err := json.Unmarshal(f, &attackTracks); err != nil {
return nil, err
}
return attackTracks, nil
}

View File

@@ -1,13 +1,416 @@
package getter
import (
"fmt"
"os"
"path/filepath"
)
"runtime"
"testing"
var mockFrameworkBasePath = filepath.Join("examples", "mocks", "frameworks")
"github.com/stretchr/testify/require"
)
func MockNewLoadPolicy() *LoadPolicy {
return &LoadPolicy{
filePaths: []string{""},
}
}
func TestLoadPolicy(t *testing.T) {
t.Parallel()
const (
testFramework = "MITRE"
testControl = "C-0053"
)
t.Run("with GetFramework", func(t *testing.T) {
t.Run("should retrieve named framework", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
fw, err := p.GetFramework(testFramework)
require.NoError(t, err)
require.NotNil(t, fw)
require.Equal(t, testFramework, fw.Name)
})
t.Run("should fail to retrieve framework", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
fw, err := p.GetFramework("wrong")
require.Error(t, err)
require.Nil(t, fw)
})
t.Run("edge case: should error on empty framework", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
fw, err := p.GetFramework("")
require.ErrorIs(t, err, ErrNameRequired)
require.Nil(t, fw)
})
t.Run("edge case: corrupted json", func(t *testing.T) {
t.Parallel()
const invalidFramework = "invalid-fw"
p := NewLoadPolicy([]string{testFrameworkFile(invalidFramework)})
fw, err := p.GetFramework(invalidFramework)
require.Error(t, err)
require.Nil(t, fw)
})
t.Run("edge case: missing json", func(t *testing.T) {
t.Parallel()
const invalidFramework = "nowheretobefound"
p := NewLoadPolicy([]string{testFrameworkFile(invalidFramework)})
_, err := p.GetFramework(invalidFramework)
require.Error(t, err)
})
})
t.Run("with GetControl", func(t *testing.T) {
t.Run("should retrieve named control from framework", func(t *testing.T) {
t.Parallel()
const (
expectedControlName = "Access container service account"
)
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
ctrl, err := p.GetControl(testControl)
require.NoError(t, err)
require.NotNil(t, ctrl)
require.Equal(t, testControl, ctrl.ControlID)
require.Equal(t, expectedControlName, ctrl.Name)
})
t.Run("with single control descriptor", func(t *testing.T) {
const (
singleControl = "C-0001"
expectedControlName = "Forbidden Container Registries"
)
t.Run("should retrieve named control from control descriptor", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(singleControl)})
ctrl, err := p.GetControl(singleControl)
require.NoError(t, err)
require.NotNil(t, ctrl)
require.Equal(t, singleControl, ctrl.ControlID)
require.Equal(t, expectedControlName, ctrl.Name)
})
t.Run("should fail to retrieve named control from control descriptor", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(singleControl)})
ctrl, err := p.GetControl("wrong")
require.Error(t, err)
require.Nil(t, ctrl)
})
})
t.Run("with framework descriptor", func(t *testing.T) {
t.Run("should fail to retrieve named control", func(t *testing.T) {
t.Parallel()
const testControl = "wrong"
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
ctrl, err := p.GetControl(testControl)
require.ErrorIs(t, err, ErrControlNotMatching)
require.Nil(t, ctrl)
})
})
t.Run("edge case: corrupted json", func(t *testing.T) {
t.Parallel()
const invalidControl = "invalid-fw"
p := NewLoadPolicy([]string{testFrameworkFile(invalidControl)})
_, err := p.GetControl(invalidControl)
require.Error(t, err)
})
t.Run("edge case: missing json", func(t *testing.T) {
t.Parallel()
const invalidControl = "nowheretobefound"
p := NewLoadPolicy([]string{testFrameworkFile(invalidControl)})
_, err := p.GetControl(invalidControl)
require.Error(t, err)
})
t.Run("edge case: should error on empty control", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
ctrl, err := p.GetControl("")
require.ErrorIs(t, err, ErrIDRequired)
require.Nil(t, ctrl)
})
})
t.Run("with ListFrameworks", func(t *testing.T) {
t.Run("should return all frameworks in the policy path", func(t *testing.T) {
t.Parallel()
const (
extraFramework = "NSA"
attackTracks = "attack-tracks"
)
p := NewLoadPolicy([]string{
testFrameworkFile(testFramework),
testFrameworkFile(extraFramework),
testFrameworkFile(extraFramework), // should be deduped
testFrameworkFile(attackTracks), // should be ignored
})
fws, err := p.ListFrameworks()
require.NoError(t, err)
require.Len(t, fws, 2)
require.Equal(t, testFramework, fws[0])
require.Equal(t, extraFramework, fws[1])
})
t.Run("should not return an empty framework", func(t *testing.T) {
t.Parallel()
const (
extraFramework = "NSA"
attackTracks = "attack-tracks"
controlsInputs = "controls-inputs"
)
p := NewLoadPolicy([]string{
testFrameworkFile(testFramework),
testFrameworkFile(extraFramework),
testFrameworkFile(attackTracks), // should be ignored
testFrameworkFile(controlsInputs), // should be ignored
})
fws, err := p.ListFrameworks()
require.NoError(t, err)
require.Len(t, fws, 2)
require.NotContains(t, fws, "")
require.Equal(t, testFramework, fws[0])
require.Equal(t, extraFramework, fws[1])
})
t.Run("should fail on file error", func(t *testing.T) {
t.Parallel()
const (
extraFramework = "NSA"
nowhere = "nowheretobeseen"
)
p := NewLoadPolicy([]string{
testFrameworkFile(testFramework),
testFrameworkFile(extraFramework),
testFrameworkFile(nowhere), // should raise an error
})
fws, err := p.ListFrameworks()
require.Error(t, err)
require.Nil(t, fws)
})
})
t.Run("edge case: policy without path", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{})
require.Empty(t, p.filePath())
})
t.Run("with GetFrameworks", func(t *testing.T) {
const extraFramework = "NSA"
t.Run("should return all configured frameworks", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{
testFrameworkFile(testFramework),
testFrameworkFile(extraFramework),
})
fws, err := p.GetFrameworks()
require.NoError(t, err)
require.Len(t, fws, 2)
require.Equal(t, testFramework, fws[0].Name)
require.Equal(t, extraFramework, fws[1].Name)
})
t.Run("should return dedupe configured frameworks", func(t *testing.T) {
t.Parallel()
const attackTracks = "attack-tracks"
p := NewLoadPolicy([]string{
testFrameworkFile(testFramework),
testFrameworkFile(extraFramework),
testFrameworkFile(extraFramework),
testFrameworkFile(attackTracks), // should be ignored
})
fws, err := p.GetFrameworks()
require.NoError(t, err)
require.Len(t, fws, 2)
require.Equal(t, testFramework, fws[0].Name)
require.Equal(t, extraFramework, fws[1].Name)
})
})
t.Run("with ListControls", func(t *testing.T) {
t.Run("should return controls", func(t *testing.T) {
t.Parallel()
p := NewLoadPolicy([]string{testFrameworkFile(testFramework)})
controlIDs, err := p.ListControls()
require.NoError(t, err)
require.Greater(t, len(controlIDs), 0)
require.Equal(t, testControl, controlIDs[0])
})
})
t.Run("with GetAttackTracks", func(t *testing.T) {
t.Run("should return attack tracks", func(t *testing.T) {
t.Parallel()
const attackTracks = "attack-tracks"
p := NewLoadPolicy([]string{testFrameworkFile(attackTracks)})
tracks, err := p.GetAttackTracks()
require.NoError(t, err)
require.Greater(t, len(tracks), 0)
for _, track := range tracks {
require.Equal(t, "AttackTrack", track.Kind)
}
})
t.Run("edge case: corrupted json", func(t *testing.T) {
t.Parallel()
const invalidTracks = "invalid-fw"
p := NewLoadPolicy([]string{testFrameworkFile(invalidTracks)})
_, err := p.GetAttackTracks()
require.Error(t, err)
})
t.Run("edge case: missing json", func(t *testing.T) {
t.Parallel()
const invalidTracks = "nowheretobefound"
p := NewLoadPolicy([]string{testFrameworkFile(invalidTracks)})
_, err := p.GetAttackTracks()
require.Error(t, err)
})
})
t.Run("with GetControlsInputs", func(t *testing.T) {
const cluster = "dummy" // unused parameter at the moment
t.Run("should return control inputs for a cluster", func(t *testing.T) {
t.Parallel()
fixture, expected := writeTempJSONControlInputs(t)
t.Cleanup(func() {
_ = os.Remove(fixture)
})
p := NewLoadPolicy([]string{fixture})
inputs, err := p.GetControlsInputs(cluster)
require.NoError(t, err)
require.EqualValues(t, expected, inputs)
})
t.Run("edge case: corrupted json", func(t *testing.T) {
t.Parallel()
const invalidInputs = "invalid-fw"
p := NewLoadPolicy([]string{testFrameworkFile(invalidInputs)})
_, err := p.GetControlsInputs(cluster)
require.Error(t, err)
})
t.Run("edge case: missing json", func(t *testing.T) {
t.Parallel()
const invalidInputs = "nowheretobefound"
p := NewLoadPolicy([]string{testFrameworkFile(invalidInputs)})
_, err := p.GetControlsInputs(cluster)
require.Error(t, err)
})
})
t.Run("with GetExceptions", func(t *testing.T) {
const cluster = "dummy" // unused parameter at the moment
t.Run("should return exceptions", func(t *testing.T) {
t.Parallel()
const exceptions = "exceptions"
p := NewLoadPolicy([]string{testFrameworkFile(exceptions)})
exceptionPolicies, err := p.GetExceptions(cluster)
require.NoError(t, err)
require.Greater(t, len(exceptionPolicies), 0)
t.Logf("len=%d", len(exceptionPolicies))
for _, policy := range exceptionPolicies {
require.NotEmpty(t, policy.Name)
}
})
t.Run("edge case: corrupted json", func(t *testing.T) {
t.Parallel()
const invalidInputs = "invalid-fw"
p := NewLoadPolicy([]string{testFrameworkFile(invalidInputs)})
_, err := p.GetExceptions(cluster)
require.Error(t, err)
})
t.Run("edge case: missing json", func(t *testing.T) {
t.Parallel()
const invalidInputs = "nowheretobefound"
p := NewLoadPolicy([]string{testFrameworkFile(invalidInputs)})
_, err := p.GetExceptions(cluster)
require.Error(t, err)
})
})
}
func testFrameworkFile(framework string) string {
return filepath.Join(currentDir(), "testdata", fmt.Sprintf("%s.json", framework))
}
func writeTempJSONControlInputs(t testing.TB) (string, map[string][]string) {
fileName := testFrameworkFile("control-inputs")
mock := map[string][]string{
"key1": {
"val1", "val2",
},
"key2": {
"val3", "val4",
},
}
buf, err := json.Marshal(mock)
require.NoError(t, err)
require.NoError(t, os.WriteFile(fileName, buf, 0600))
return fileName, mock
}
func currentDir() string {
_, filename, _, _ := runtime.Caller(1)
return filepath.Dir(filename)
}

View File

@@ -0,0 +1,85 @@
{
"guid": "",
"name": "Forbidden Container Registries",
"attributes": {
"armoBuiltin": true,
"attackTracks": [
{
"attackTrack": "container",
"categories": [
"Initial access"
]
}
],
"controlTypeTags": [
"security",
"compliance"
],
"microsoftMitreColumns": [
"Initial Access"
]
},
"id": "C-0001",
"controlID": "C-0001",
"creationTime": "",
"description": "In cases where the Kubernetes cluster is provided by a CSP (e.g., AKS in Azure, GKE in GCP, or EKS in AWS), compromised cloud credential can lead to the cluster takeover. Attackers may abuse cloud account credentials or IAM mechanism to the clusters management layer.",
"remediation": "Limit the registries from which you pull container images from",
"rules": [
{
"guid": "",
"name": "rule-identify-blocklisted-image-registries",
"attributes": {
"armoBuiltin": true,
"m$K8sThreatMatrix": "Initial Access::Compromised images in registry"
},
"creationTime": "",
"rule": "package armo_builtins\nimport data\n# Check for images from blocklisted repos\n\nuntrustedImageRepo[msga] {\n\tpod := input[_]\n\tk := pod.kind\n\tk == \"Pod\"\n\tcontainer := pod.spec.containers[i]\n\tpath := sprintf(\"spec.containers[%v].image\", [format_int(i, 10)])\n\timage := container.image\n untrusted_or_public_registries(image)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' comes from untrusted registry\", [image, container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 2,\n\t\t\"fixPaths\": [],\n\t\t\"failedPaths\": [path],\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n }\n}\n\nuntrustedImageRepo[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainer := wl.spec.template.spec.containers[i]\n\tpath := sprintf(\"spec.template.spec.containers[%v].image\", [format_int(i, 10)])\n\timage := container.image\n untrusted_or_public_registries(image)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' comes from untrusted registry\", [image, container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 2,\n\t\t\"fixPaths\": [],\n\t\t\"failedPaths\": [path],\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n }\n}\n\nuntrustedImageRepo[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer := wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tpath := sprintf(\"spec.jobTemplate.spec.template.spec.containers[%v].image\", [format_int(i, 10)])\n\timage := container.image\n untrusted_or_public_registries(image)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' comes from untrusted registry\", [image, container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 2,\n\t\t\"fixPaths\": [],\n\t\t\"failedPaths\": [path],\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n }\n}\n\nuntrusted_or_public_registries(image){\n\t# see default-config-inputs.json for list values\n\tuntrusted_registries := data.postureControlInputs.untrustedRegistries\n\trepo_prefix := untrusted_registries[_]\n\tstartswith(image, repo_prefix)\n}\n\nuntrusted_or_public_registries(image){\n\t# see default-config-inputs.json for list values\n\tpublic_registries := data.postureControlInputs.publicRegistries\n\trepo_prefix := public_registries[_]\n\tstartswith(image, repo_prefix)\n}",
"resourceEnumerator": "",
"ruleLanguage": "Rego",
"match": [
{
"apiGroups": [
"*"
],
"apiVersions": [
"*"
],
"resources": [
"Pod",
"Deployment",
"ReplicaSet",
"DaemonSet",
"StatefulSet",
"Job",
"CronJob"
]
}
],
"ruleDependencies": [],
"configInputs": [
"settings.postureControlInputs.publicRegistries",
"settings.postureControlInputs.untrustedRegistries"
],
"controlConfigInputs": [
{
"path": "settings.postureControlInputs.publicRegistries",
"name": "Public registries",
"description": "Kubescape checks none of these public registries are in use."
},
{
"path": "settings.postureControlInputs.untrustedRegistries",
"name": "Registries block list",
"description": "Kubescape checks none of the following registries are in use."
}
],
"description": "Identifying if pod container images are from unallowed registries",
"remediation": "Use images from safe registry",
"ruleQuery": "",
"relevantCloudProviders": null
}
],
"rulesIDs": [
""
],
"baseScore": 7
}

2832
core/cautils/getter/testdata/MITRE.json vendored Normal file

File diff suppressed because one or more lines are too long

2249
core/cautils/getter/testdata/NSA.json vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,136 @@
[
{
"apiVersion": "regolibrary.kubescape/v1alpha1",
"kind": "AttackTrack",
"metadata": {
"name": "node"
},
"spec": {
"data": {
"name": "Initial access",
"subSteps": [
{
"name": "Execution",
"subSteps": [
{
"name": "Persistence"
},
{
"name": "Credential access"
},
{
"name": "Defense evasion"
},
{
"name": "Discovery"
},
{
"name": "Lateral movement"
},
{
"name": "Impact - data theft"
},
{
"name": "Impact - data destruction"
},
{
"name": "Impact - service injection"
}
]
}
]
}
}
},
{
"apiVersion": "regolibrary.kubescape/v1alpha1",
"kind": "AttackTrack",
"metadata": {
"name": "kubeapi"
},
"spec": {
"data": {
"name": "Initial access",
"subSteps": [
{
"name": "Persistence"
},
{
"name": "Privilege escalation"
},
{
"name": "Credential access"
},
{
"name": "Discovery"
},
{
"name": "Lateral movement"
},
{
"name": "Defense evasion"
},
{
"name": "Impact - data destruction"
},
{
"name": "Impact - service injection"
}
]
}
}
},
{
"apiVersion": "regolibrary.kubescape/v1alpha1",
"kind": "AttackTrack",
"metadata": {
"name": "container"
},
"spec": {
"data": {
"name": "Initial access",
"subSteps": [
{
"name": "Execution",
"subSteps": [
{
"name": "Privilege escalation"
},
{
"name": "Credential access",
"subSteps": [
{
"name": "Impact - service access"
},
{
"name": "Impact - K8s API access",
"subSteps": [
{
"name": "Defense evasion - KubeAPI"
}
]
}
]
},
{
"name": "Discovery"
},
{
"name": "Lateral movement"
},
{
"name": "Impact - Data access in container"
},
{
"name": "Persistence"
}
]
},
{
"name": "Impact - service destruction"
}
]
}
}
}
]

View File

@@ -0,0 +1,125 @@
{
"publicRegistries": [],
"untrustedRegistries": [],
"listOfDangerousArtifacts": [
"bin/bash",
"sbin/sh",
"bin/ksh",
"bin/tcsh",
"bin/zsh",
"usr/bin/scsh",
"bin/csh",
"bin/busybox",
"usr/bin/busybox"
],
"sensitiveKeyNames": [
"aws_access_key_id",
"aws_secret_access_key",
"azure_batchai_storage_account",
"azure_batchai_storage_key",
"azure_batch_account",
"azure_batch_key",
"secret",
"key",
"password",
"pwd",
"token",
"jwt",
"bearer",
"credential"
],
"servicesNames": [
"nifi-service",
"argo-server",
"minio",
"postgres",
"workflow-controller-metrics",
"weave-scope-app",
"kubernetes-dashboard"
],
"memory_limit_max": [],
"cpu_request_min": [],
"wlKnownNames": [
"coredns",
"kube-proxy",
"event-exporter-gke",
"kube-dns",
"17-default-backend",
"metrics-server",
"ca-audit",
"ca-dashboard-aggregator",
"ca-notification-server",
"ca-ocimage",
"ca-oracle",
"ca-posture",
"ca-rbac",
"ca-vuln-scan",
"ca-webhook",
"ca-websocket",
"clair-clair"
],
"sensitiveInterfaces": [
"nifi",
"argo-server",
"weave-scope-app",
"kubeflow",
"kubernetes-dashboard",
"jenkins",
"prometheus-deployment"
],
"max_high_vulnerabilities": [
"10"
],
"sensitiveValues": [
"BEGIN \\w+ PRIVATE KEY",
"PRIVATE KEY",
"eyJhbGciO",
"JWT",
"Bearer",
"_key_",
"_secret_"
],
"memory_request_max": [],
"memory_request_min": [],
"cpu_request_max": [],
"cpu_limit_max": [],
"cpu_limit_min": [],
"insecureCapabilities": [
"SETPCAP",
"NET_ADMIN",
"NET_RAW",
"SYS_MODULE",
"SYS_RAWIO",
"SYS_PTRACE",
"SYS_ADMIN",
"SYS_BOOT",
"MAC_OVERRIDE",
"MAC_ADMIN",
"PERFMON",
"ALL",
"BPF"
],
"max_critical_vulnerabilities": [
"5"
],
"sensitiveValuesAllowed": [],
"memory_limit_min": [],
"recommendedLabels": [
"app",
"tier",
"phase",
"version",
"owner",
"env"
],
"k8sRecommendedLabels": [
"app.kubernetes.io/name",
"app.kubernetes.io/instance",
"app.kubernetes.io/version",
"app.kubernetes.io/component",
"app.kubernetes.io/part-of",
"app.kubernetes.io/managed-by",
"app.kubernetes.io/created-by"
],
"imageRepositoryAllowList": []
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
{
"guid": "",
}

25821
core/cautils/getter/testdata/policy.json vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
//go:build !gitenabled
package cautils
import (
"errors"
"github.com/kubescape/go-git-url/apis"
)
var ErrFatalNotSupportedByBuild = errors.New(`git scan not supported by this build. Build with tag "gitenabled" to enable the git scan feature`)
type gitRepository struct {
}
func newGitRepository(root string) (*gitRepository, error) {
return &gitRepository{}, ErrWarnNotSupportedByBuild
}
func (g *gitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error) {
return nil, ErrFatalNotSupportedByBuild
}

View File

@@ -0,0 +1,11 @@
//go:build !gitenabled
package cautils
func (s *LocalGitRepositoryTestSuite) TestGetLastCommit() {
s.T().Log("warn: skipped testing native git functionality [GetLastCommit]")
}
func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
s.T().Log("warn: skipped testing native git functionality [GetFileLastCommit]")
}

View File

@@ -0,0 +1,141 @@
//go:build gitenabled
package cautils
import (
"fmt"
"time"
"github.com/kubescape/go-git-url/apis"
git2go "github.com/libgit2/git2go/v33"
)
type gitRepository struct {
git2GoRepo *git2go.Repository
fileToLastCommit map[string]*git2go.Commit
}
func newGitRepository(root string) (*gitRepository, error) {
git2GoRepo, err := git2go.OpenRepository(root)
if err != nil {
return nil, err
}
return &gitRepository{
git2GoRepo: git2GoRepo,
}, nil
}
func (g *gitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error) {
if len(g.fileToLastCommit) == 0 {
filePathToCommitTime := map[string]time.Time{}
filePathToCommit := map[string]*git2go.Commit{}
allCommits, _ := g.getAllCommits()
// builds a map of all files to their last commit
for _, commit := range allCommits {
// Ignore merge commits (2+ parents)
if commit.ParentCount() <= 1 {
tree, err := commit.Tree()
if err != nil {
continue
}
// ParentCount can be either 1 or 0 (initial commit)
// In case it's the initial commit, prevTree is nil
var prevTree *git2go.Tree
if commit.ParentCount() == 1 {
prevCommit := commit.Parent(0)
prevTree, err = prevCommit.Tree()
if err != nil {
continue
}
}
diff, err := g.git2GoRepo.DiffTreeToTree(prevTree, tree, nil)
if err != nil {
continue
}
numDeltas, err := diff.NumDeltas()
if err != nil {
continue
}
for i := 0; i < numDeltas; i++ {
delta, err := diff.Delta(i)
if err != nil {
continue
}
deltaFilePath := delta.NewFile.Path
commitTime := commit.Author().When
// In case we have the commit information for the file which is not the latest - we override it
if currentCommitTime, exists := filePathToCommitTime[deltaFilePath]; exists {
if currentCommitTime.Before(commitTime) {
filePathToCommitTime[deltaFilePath] = commitTime
filePathToCommit[deltaFilePath] = commit
}
} else {
filePathToCommitTime[deltaFilePath] = commitTime
filePathToCommit[deltaFilePath] = commit
}
}
}
}
g.fileToLastCommit = filePathToCommit
}
if relevantCommit, exists := g.fileToLastCommit[filePath]; exists {
return g.getCommit(relevantCommit), nil
}
return nil, fmt.Errorf("failed to get commit information for file: %s", filePath)
}
func (g *gitRepository) getAllCommits() ([]*git2go.Commit, error) {
logItr, itrErr := g.git2GoRepo.Walk()
if itrErr != nil {
return nil, itrErr
}
pushErr := logItr.PushHead()
if pushErr != nil {
return nil, pushErr
}
var allCommits []*git2go.Commit
err := logItr.Iterate(func(commit *git2go.Commit) bool {
if commit != nil {
allCommits = append(allCommits, commit)
return true
}
return false
})
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
return allCommits, nil
}
func (g *gitRepository) getCommit(commit *git2go.Commit) *apis.Commit {
return &apis.Commit{
SHA: commit.Id().String(),
Author: apis.Committer{
Name: commit.Author().Name,
Email: commit.Author().Email,
Date: commit.Author().When,
},
Message: commit.Message(),
Committer: apis.Committer{},
Files: []apis.Files{},
}
}

View File

@@ -0,0 +1,44 @@
//go:build gitenabled
package cautils
func (s *LocalGitRepositoryTestSuite) TestGetLastCommit() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if commit, err := localRepo.GetLastCommit(); s.NoError(err) {
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
s.Equal("added file B\n", commit.Message)
}
}
}
func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
s.Run("fileA", func() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if commit, err := localRepo.GetFileLastCommit("fileA"); s.NoError(err) {
s.Equal("9fae4be19624297947d2b605cefbff516628612d", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 18:55:48 +0300 +0300", commit.Author.Date.String())
s.Equal("added file A\n", commit.Message)
}
}
})
s.Run("fileB", func() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if commit, err := localRepo.GetFileLastCommit("dirA/fileB"); s.NoError(err) {
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
s.Equal("added file B\n", commit.Message)
}
}
})
}

20
core/cautils/krewutils.go Normal file
View File

@@ -0,0 +1,20 @@
package cautils
import (
"os"
"path/filepath"
"strings"
)
// ExecName returns the correct name to use in examples depending on how kubescape is invoked
func ExecName() string {
n := "kubescape"
if IsKrewPlugin() {
return "kubectl " + n
}
return n
}
func IsKrewPlugin() bool {
return strings.HasPrefix(filepath.Base(os.Args[0]), "kubectl-")
}

View File

@@ -1,26 +1,26 @@
package cautils
import (
"errors"
"fmt"
"path"
"strings"
"time"
gitv5 "github.com/go-git/go-git/v5"
configv5 "github.com/go-git/go-git/v5/config"
plumbingv5 "github.com/go-git/go-git/v5/plumbing"
"github.com/kubescape/go-git-url/apis"
git2go "github.com/libgit2/git2go/v33"
)
type LocalGitRepository struct {
goGitRepo *gitv5.Repository
git2GoRepo *git2go.Repository
head *plumbingv5.Reference
config *configv5.Config
fileToLastCommit map[string]*git2go.Commit
*gitRepository
goGitRepo *gitv5.Repository
head *plumbingv5.Reference
config *configv5.Config
}
var ErrWarnNotSupportedByBuild = errors.New(`git commits retrieval not supported by this build. Build with tag "gitenabled" to enable the full git scan feature`)
func NewLocalGitRepository(path string) (*LocalGitRepository, error) {
goGitRepo, err := gitv5.PlainOpenWithOptions(path, &gitv5.PlainOpenOptions{DetectDotGit: true})
if err != nil {
@@ -52,11 +52,12 @@ func NewLocalGitRepository(path string) (*LocalGitRepository, error) {
}
if repoRoot, err := l.GetRootDir(); err == nil {
git2GoRepo, err := git2go.OpenRepository(repoRoot)
if err != nil {
gitRepository, err := newGitRepository(repoRoot)
if err != nil && !errors.Is(err, ErrWarnNotSupportedByBuild) {
return l, err
}
l.git2GoRepo = git2GoRepo
l.gitRepository = gitRepository
}
return l, nil
@@ -72,6 +73,10 @@ func (g *LocalGitRepository) GetRemoteUrl() (string, error) {
branchName := g.GetBranchName()
if branchRef, branchFound := g.config.Branches[branchName]; branchFound {
remoteName := branchRef.Remote
// branchRef.Remote can be a reference to a config.Remotes entry or directly a gitUrl
if _, found := g.config.Remotes[remoteName]; !found {
return remoteName, nil
}
if len(g.config.Remotes[remoteName].URLs) == 0 {
return "", fmt.Errorf("expected to find URLs for remote '%s', branch '%s'", remoteName, branchName)
}
@@ -79,10 +84,13 @@ func (g *LocalGitRepository) GetRemoteUrl() (string, error) {
}
const defaultRemoteName string = "origin"
if len(g.config.Remotes[defaultRemoteName].URLs) == 0 {
defaultRemote, ok := g.config.Remotes[defaultRemoteName]
if !ok {
return "", fmt.Errorf("did not find a default remote with name '%s'", defaultRemoteName)
} else if len(defaultRemote.URLs) == 0 {
return "", fmt.Errorf("expected to find URLs for remote '%s'", defaultRemoteName)
}
return g.config.Remotes[defaultRemoteName].URLs[0], nil
return defaultRemote.URLs[0], nil
}
// GetName get origin name without the .git suffix
@@ -122,120 +130,6 @@ func (g *LocalGitRepository) GetLastCommit() (*apis.Commit, error) {
}, nil
}
func (g *LocalGitRepository) getAllCommits() ([]*git2go.Commit, error) {
logItr, itrErr := g.git2GoRepo.Walk()
if itrErr != nil {
return nil, itrErr
}
pushErr := logItr.PushHead()
if pushErr != nil {
return nil, pushErr
}
var allCommits []*git2go.Commit
err := logItr.Iterate(func(commit *git2go.Commit) bool {
if commit != nil {
allCommits = append(allCommits, commit)
return true
}
return false
})
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
return allCommits, nil
}
func (g *LocalGitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error) {
if len(g.fileToLastCommit) == 0 {
filePathToCommitTime := map[string]time.Time{}
filePathToCommit := map[string]*git2go.Commit{}
allCommits, _ := g.getAllCommits()
// builds a map of all files to their last commit
for _, commit := range allCommits {
// Ignore merge commits (2+ parents)
if commit.ParentCount() <= 1 {
tree, err := commit.Tree()
if err != nil {
continue
}
// ParentCount can be either 1 or 0 (initial commit)
// In case it's the initial commit, prevTree is nil
var prevTree *git2go.Tree
if commit.ParentCount() == 1 {
prevCommit := commit.Parent(0)
prevTree, err = prevCommit.Tree()
if err != nil {
continue
}
}
diff, err := g.git2GoRepo.DiffTreeToTree(prevTree, tree, nil)
if err != nil {
continue
}
numDeltas, err := diff.NumDeltas()
if err != nil {
continue
}
for i := 0; i < numDeltas; i++ {
delta, err := diff.Delta(i)
if err != nil {
continue
}
deltaFilePath := delta.NewFile.Path
commitTime := commit.Author().When
// In case we have the commit information for the file which is not the latest - we override it
if currentCommitTime, exists := filePathToCommitTime[deltaFilePath]; exists {
if currentCommitTime.Before(commitTime) {
filePathToCommitTime[deltaFilePath] = commitTime
filePathToCommit[deltaFilePath] = commit
}
} else {
filePathToCommitTime[deltaFilePath] = commitTime
filePathToCommit[deltaFilePath] = commit
}
}
}
}
g.fileToLastCommit = filePathToCommit
}
if relevantCommit, exists := g.fileToLastCommit[filePath]; exists {
return g.getCommit(relevantCommit), nil
}
return nil, fmt.Errorf("failed to get commit information for file: %s", filePath)
}
func (g *LocalGitRepository) getCommit(commit *git2go.Commit) *apis.Commit {
return &apis.Commit{
SHA: commit.Id().String(),
Author: apis.Committer{
Name: commit.Author().Name,
Email: commit.Author().Email,
Date: commit.Author().When,
},
Message: commit.Message(),
Committer: apis.Committer{},
Files: []apis.Files{},
}
}
func (g *LocalGitRepository) GetRootDir() (string, error) {
wt, err := g.goGitRepo.Worktree()
if err != nil {

View File

@@ -9,6 +9,8 @@ import (
"strings"
"testing"
configv5 "github.com/go-git/go-git/v5/config"
plumbingv5 "github.com/go-git/go-git/v5/plumbing"
"github.com/stretchr/testify/suite"
)
@@ -26,40 +28,58 @@ func unzipFile(zipPath, destinationFolder string) (*zip.ReadCloser, error) {
if err != nil {
return nil, err
}
for _, f := range archive.File {
filePath := filepath.Join(destinationFolder, f.Name) //nolint:gosec
if !strings.HasPrefix(filePath, filepath.Clean(destinationFolder)+string(os.PathSeparator)) {
return nil, fmt.Errorf("invalid file path")
}
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return nil, err
if erc := copyFileInFolder(filePath, f); erc != nil {
return nil, erc
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return nil, err
}
fileInArchive, err := f.Open()
if err != nil {
return nil, err
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil { //nolint:gosec
return nil, err
}
dstFile.Close()
fileInArchive.Close()
}
return archive, err
}
func copyFileInFolder(filePath string, f *zip.File) (err error) {
if err = os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return err
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer func() {
_ = dstFile.Close()
}()
fileInArchive, err := f.Open()
if err != nil {
return err
}
defer func() {
_ = fileInArchive.Close()
}()
_, err = io.Copy(dstFile, fileInArchive) //nolint:gosec
if err = dstFile.Close(); err != nil {
return err
}
if err = fileInArchive.Close(); err != nil {
return err
}
return err
}
func (s *LocalGitRepositoryTestSuite) SetupSuite() {
@@ -132,44 +152,49 @@ func (s *LocalGitRepositoryTestSuite) TestGetOriginUrl() {
}
}
func (s *LocalGitRepositoryTestSuite) TestGetLastCommit() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if commit, err := localRepo.GetLastCommit(); s.NoError(err) {
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
s.Equal("added file B\n", commit.Message)
}
func TestGetRemoteUrl(t *testing.T) {
testCases := []struct {
Name string
LocalRepo LocalGitRepository
Want string
WantErr error
}{
{
Name: "Branch with missing upstream and missing 'origin' fallback should return an error",
LocalRepo: LocalGitRepository{
config: &configv5.Config{
Branches: make(map[string]*configv5.Branch),
Remotes: make(map[string]*configv5.RemoteConfig),
},
head: plumbingv5.NewReferenceFromStrings("HEAD", "ref: refs/heads/v4"),
},
Want: "",
WantErr: fmt.Errorf("did not find a default remote with name 'origin'"),
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
localRepo := LocalGitRepository{
config: &configv5.Config{
Branches: make(map[string]*configv5.Branch),
Remotes: make(map[string]*configv5.RemoteConfig),
},
head: plumbingv5.NewReferenceFromStrings("HEAD", "ref: refs/heads/v4"),
}
want := tc.Want
wantErr := tc.WantErr
got, gotErr := localRepo.GetRemoteUrl()
if got != want {
t.Errorf("Remote URLs dont match: got '%s', want '%s'", got, want)
}
if gotErr.Error() != wantErr.Error() {
t.Errorf("Errors dont match: got '%v', want '%v'", gotErr, wantErr)
}
},
)
}
}
func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
s.Run("fileA", func() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if commit, err := localRepo.GetFileLastCommit("fileA"); s.NoError(err) {
s.Equal("9fae4be19624297947d2b605cefbff516628612d", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 18:55:48 +0300 +0300", commit.Author.Date.String())
s.Equal("added file A\n", commit.Message)
}
}
})
s.Run("fileB", func() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if commit, err := localRepo.GetFileLastCommit("dirA/fileB"); s.NoError(err) {
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
s.Equal("added file B\n", commit.Message)
}
}
})
}

View File

@@ -3,7 +3,6 @@ package cautils
import (
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
@@ -72,9 +71,9 @@ func controlReportV2ToV1(opaSessionObj *OPASessionObj, frameworkName string, con
}
rulev1 := rulesv1[rulev2.GetName()]
status := rulev2.GetStatus(&helpersv1.Filters{FrameworkNames: []string{frameworkName}})
status := rulev2.GetStatus(nil)
if status.IsFailed() || status.IsExcluded() {
if status.IsFailed() {
// rule response
ruleResponse := reporthandling.RuleResponse{}

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -8,13 +9,12 @@ import (
"strings"
"github.com/armosec/armoapi-go/armotypes"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
giturl "github.com/kubescape/go-git-url"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/reporthandling"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
@@ -104,6 +104,7 @@ type ScanInfo struct {
PolicyIdentifier []PolicyIdentifier // TODO - remove from object
UseExceptions string // Load file with exceptions configuration
ControlsInputs string // Load file with inputs for controls
AttackTracks string // Load file with attack tracks
UseFrom []string // Load framework from local file (instead of download). Use when running offline
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
UseArtifactsFrom string // Load artifacts from local path. Use when running offline
@@ -111,7 +112,7 @@ type ScanInfo struct {
View string // Display all of the input resources and not only failed resources
Format string // Format results (table, json, junit ...)
Output string // Store results in an output file, Output file name
FormatVersion string // Output object can be differnet between versions, this is for testing and backward compatibility
FormatVersion string // Output object can be different between versions, this is for testing and backward compatibility
CustomClusterName string // Set the custom name of the cluster
ExcludedNamespaces string // used for host scanner namespace
IncludeNamespaces string //
@@ -140,16 +141,16 @@ type Getters struct {
AttackTracksGetter getter.IAttackTracksGetter
}
func (scanInfo *ScanInfo) Init() {
func (scanInfo *ScanInfo) Init(ctx context.Context) {
scanInfo.setUseFrom()
scanInfo.setUseArtifactsFrom()
scanInfo.setUseArtifactsFrom(ctx)
if scanInfo.ScanID == "" {
scanInfo.ScanID = uuid.NewString()
}
}
func (scanInfo *ScanInfo) setUseArtifactsFrom() {
func (scanInfo *ScanInfo) setUseArtifactsFrom(ctx context.Context) {
if scanInfo.UseArtifactsFrom == "" {
return
}
@@ -163,7 +164,7 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() {
// set frameworks files
files, err := os.ReadDir(scanInfo.UseArtifactsFrom)
if err != nil {
logger.L().Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(err))
logger.L().Ctx(ctx).Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(err))
}
framework := &reporthandling.Framework{}
for _, f := range files {
@@ -179,6 +180,9 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() {
scanInfo.ControlsInputs = filepath.Join(scanInfo.UseArtifactsFrom, localControlInputsFilename)
// set exceptions
scanInfo.UseExceptions = filepath.Join(scanInfo.UseArtifactsFrom, LocalExceptionsFilename)
// set attack tracks
scanInfo.AttackTracks = filepath.Join(scanInfo.UseArtifactsFrom, LocalAttackTracksFilename)
}
func (scanInfo *ScanInfo) setUseFrom() {
@@ -219,7 +223,7 @@ func (scanInfo *ScanInfo) contains(policyName string) bool {
return false
}
func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
func scanInfoToScanMetadata(ctx context.Context, scanInfo *ScanInfo) *reporthandlingv2.Metadata {
metadata := &reporthandlingv2.Metadata{}
metadata.ScanMetadata.Format = scanInfo.Format
@@ -273,7 +277,7 @@ func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
}
setContextMetadata(&metadata.ContextMetadata, inputFiles)
setContextMetadata(ctx, &metadata.ContextMetadata, inputFiles)
return metadata
}
@@ -317,7 +321,7 @@ func GetScanningContext(input string) ScanningContext {
// dir/glob
return ContextDir
}
func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input string) {
func setContextMetadata(ctx context.Context, contextMetadata *reporthandlingv2.ContextMetadata, input string) {
switch GetScanningContext(input) {
case ContextCluster:
contextMetadata.ClusterContextMetadata = &reporthandlingv2.ClusterMetadata{
@@ -327,7 +331,7 @@ func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input
// url
context, err := metadataGitURL(input)
if err != nil {
logger.L().Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
logger.L().Ctx(ctx).Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
}
contextMetadata.RepoContextMetadata = context
case ContextDir:
@@ -344,7 +348,7 @@ func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input
// local
context, err := metadataGitLocal(input)
if err != nil {
logger.L().Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
logger.L().Ctx(ctx).Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
}
contextMetadata.RepoContextMetadata = context
}

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"testing"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
@@ -10,7 +11,7 @@ import (
func TestSetContextMetadata(t *testing.T) {
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "")
setContextMetadata(context.TODO(), &ctx, "")
assert.NotNil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -10,7 +11,7 @@ import (
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"go.opentelemetry.io/otel"
"golang.org/x/mod/semver"
)
@@ -25,12 +26,12 @@ var LatestReleaseVersion string
const UnknownBuildNumber = "unknown"
type IVersionCheckHandler interface {
CheckLatestVersion(*VersionCheckRequest) error
CheckLatestVersion(context.Context, *VersionCheckRequest) error
}
func NewIVersionCheckHandler() IVersionCheckHandler {
func NewIVersionCheckHandler(ctx context.Context) IVersionCheckHandler {
if BuildNumber == "" {
logger.L().Warning("unknown build number, this might affect your scan results. Please make sure you are updated to latest version")
logger.L().Ctx(ctx).Warning("unknown build number, this might affect your scan results. Please make sure you are updated to latest version")
}
if v, ok := os.LookupEnv(CLIENT_ENV); ok && v != "" {
@@ -98,15 +99,17 @@ func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanni
}
}
func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRequest) error {
func (v *VersionCheckHandlerMock) CheckLatestVersion(_ context.Context, _ *VersionCheckRequest) error {
logger.L().Info("Skipping version check")
return nil
}
func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error {
func (v *VersionCheckHandler) CheckLatestVersion(ctx context.Context, versionData *VersionCheckRequest) error {
ctx, span := otel.Tracer("").Start(ctx, "versionCheckHandler.CheckLatestVersion")
defer span.End()
defer func() {
if err := recover(); err != nil {
logger.L().Warning("failed to get latest version", helpers.Interface("error", err))
logger.L().Ctx(ctx).Warning("failed to get latest version", helpers.Interface("error", err))
}
}()
@@ -119,7 +122,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && semver.Compare(BuildNumber, LatestReleaseVersion) == -1 {
logger.L().Warning(warningMessage(LatestReleaseVersion))
logger.L().Ctx(ctx).Warning(warningMessage(LatestReleaseVersion))
}
}

View File

@@ -4,6 +4,7 @@ import (
"strings"
"github.com/kubescape/k8s-interface/cloudsupport"
cloudapis "github.com/kubescape/k8s-interface/cloudsupport/apis"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
@@ -20,9 +21,12 @@ var (
"KubeProxyInfo",
"ControlPlaneInfo",
"CloudProviderInfo",
"CNIInfo",
}
CloudResources = []string{
"ClusterDescribe",
cloudapis.CloudProviderDescribeKind,
cloudapis.CloudProviderDescribeRepositoriesKind,
cloudapis.CloudProviderListEntitiesForPoliciesKind,
string(cloudsupport.TypeApiServerInfo),
}
)

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"fmt"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
@@ -42,8 +43,8 @@ func (ks *Kubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
return nil
}
func (ks *Kubescape) DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error {
func (ks *Kubescape) DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error {
tenant := getTenantConfig(nil, "", "", getKubernetesApi()) // change k8sinterface
return tenant.DeleteCachedConfig()
return tenant.DeleteCachedConfig(ctx)
}

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -21,7 +22,7 @@ const (
TargetAttackTracks = "attack-tracks"
)
var downloadFunc = map[string]func(*metav1.DownloadInfo) error{
var downloadFunc = map[string]func(context.Context, *metav1.DownloadInfo) error{
TargetControlsInputs: downloadConfigInputs,
TargetExceptions: downloadExceptions,
TargetControl: downloadControl,
@@ -38,20 +39,20 @@ func DownloadSupportCommands() []string {
return commands
}
func (ks *Kubescape) Download(downloadInfo *metav1.DownloadInfo) error {
func (ks *Kubescape) Download(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
setPathandFilename(downloadInfo)
if err := os.MkdirAll(downloadInfo.Path, os.ModePerm); err != nil {
return err
}
if err := downloadArtifact(downloadInfo, downloadFunc); err != nil {
if err := downloadArtifact(ctx, downloadInfo, downloadFunc); err != nil {
return err
}
return nil
}
func downloadArtifact(downloadInfo *metav1.DownloadInfo, downloadArtifactFunc map[string]func(*metav1.DownloadInfo) error) error {
func downloadArtifact(ctx context.Context, downloadInfo *metav1.DownloadInfo, downloadArtifactFunc map[string]func(context.Context, *metav1.DownloadInfo) error) error {
if f, ok := downloadArtifactFunc[downloadInfo.Target]; ok {
if err := f(downloadInfo); err != nil {
if err := f(ctx, downloadInfo); err != nil {
return err
}
return nil
@@ -73,26 +74,26 @@ func setPathandFilename(downloadInfo *metav1.DownloadInfo) {
}
}
func downloadArtifacts(downloadInfo *metav1.DownloadInfo) error {
func downloadArtifacts(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
downloadInfo.FileName = ""
var artifacts = map[string]func(*metav1.DownloadInfo) error{
var artifacts = map[string]func(context.Context, *metav1.DownloadInfo) error{
"controls-inputs": downloadConfigInputs,
"exceptions": downloadExceptions,
"framework": downloadFramework,
"attack-tracks": downloadAttackTracks,
}
for artifact := range artifacts {
if err := downloadArtifact(&metav1.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
logger.L().Error("error downloading", helpers.String("artifact", artifact), helpers.Error(err))
if err := downloadArtifact(ctx, &metav1.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
logger.L().Ctx(ctx).Error("error downloading", helpers.String("artifact", artifact), helpers.Error(err))
}
}
return nil
}
func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
func downloadConfigInputs(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Identifier, tenant.GetAccountID(), nil)
controlsInputsGetter := getConfigInputsGetter(ctx, downloadInfo.Identifier, tenant.GetAccountID(), nil)
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetContextName())
if err != nil {
return err
@@ -112,9 +113,9 @@ func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
return nil
}
func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
func downloadExceptions(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
exceptionsGetter := getExceptionsGetter("", tenant.GetAccountID(), nil)
exceptionsGetter := getExceptionsGetter(ctx, "", tenant.GetAccountID(), nil)
exceptions, err := exceptionsGetter.GetExceptions(tenant.GetContextName())
if err != nil {
@@ -129,15 +130,15 @@ func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
if err != nil {
return err
}
logger.L().Success("Downloaded", helpers.String("artifact", downloadInfo.Target), helpers.String("path", filepath.Join(downloadInfo.Path, downloadInfo.FileName)))
logger.L().Ctx(ctx).Success("Downloaded", helpers.String("artifact", downloadInfo.Target), helpers.String("path", filepath.Join(downloadInfo.Path, downloadInfo.FileName)))
return nil
}
func downloadAttackTracks(downloadInfo *metav1.DownloadInfo) error {
func downloadAttackTracks(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
var err error
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
attackTracksGetter := getAttackTracksGetter(tenant.GetAccountID(), nil)
attackTracksGetter := getAttackTracksGetter(ctx, "", tenant.GetAccountID(), nil)
attackTracks, err := attackTracksGetter.GetAttackTracks()
if err != nil {
@@ -157,11 +158,11 @@ func downloadAttackTracks(downloadInfo *metav1.DownloadInfo) error {
}
func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
func downloadFramework(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
g := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), true, nil)
if downloadInfo.Identifier == "" {
// if framework name not specified - download all frameworks
@@ -199,11 +200,11 @@ func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
return nil
}
func downloadControl(downloadInfo *metav1.DownloadInfo) error {
func downloadControl(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
g := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), false, nil)
if downloadInfo.Identifier == "" {
// TODO - support

73
core/core/fix.go Normal file
View File

@@ -0,0 +1,73 @@
package core
import (
"context"
"fmt"
"strings"
logger "github.com/kubescape/go-logger"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v2/core/pkg/fixhandler"
)
const NoChangesApplied = "No changes were applied."
const NoResourcesToFix = "No issues to fix."
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 {
logger.L().Info("Reading report file...")
handler, err := fixhandler.NewFixHandler(fixInfo)
if err != nil {
return err
}
resourcesToFix := handler.PrepareResourcesToFix(ctx)
if len(resourcesToFix) == 0 {
logger.L().Info(NoResourcesToFix)
return nil
}
handler.PrintExpectedChanges(resourcesToFix)
if fixInfo.DryRun {
logger.L().Info(NoChangesApplied)
return nil
}
if !fixInfo.NoConfirm && !userConfirmed() {
logger.L().Info(NoChangesApplied)
return nil
}
updatedFilesCount, errors := handler.ApplyChanges(ctx, resourcesToFix)
logger.L().Info(fmt.Sprintf("Fixed resources in %d files.", updatedFilesCount))
if len(errors) > 0 {
for _, err := range errors {
logger.L().Ctx(ctx).Error(err.Error())
}
return fmt.Errorf("Failed to fix some resources, check the logs for more details")
}
return nil
}
func userConfirmed() bool {
var input string
for {
fmt.Printf(ConfirmationQuestion)
if _, err := fmt.Scanln(&input); err != nil {
continue
}
input = strings.ToLower(input)
if input == "y" || input == "yes" {
return true
} else if input == "n" || input == "no" {
return false
}
}
}

View File

@@ -1,10 +1,11 @@
package core
import (
"context"
"fmt"
"os"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -15,6 +16,7 @@ import (
printerv2 "github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
reporterv2 "github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter/v2"
"go.opentelemetry.io/otel"
"github.com/google/uuid"
@@ -35,7 +37,7 @@ func getTenantConfig(credentials *cautils.Credentials, clusterName string, custo
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
}
func getExceptionsGetter(useExceptions string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IExceptionsGetter {
func getExceptionsGetter(ctx context.Context, useExceptions string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IExceptionsGetter {
if useExceptions != "" {
// load exceptions from file
return getter.NewLoadPolicy([]string{useExceptions})
@@ -49,7 +51,7 @@ func getExceptionsGetter(useExceptions string, accountID string, downloadRelease
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull attack tracks, fallback to cache
logger.L().Warning("failed to get exceptions from github release, loading attack tracks from cache", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to get exceptions from github release, loading attack tracks from cache", helpers.Error(err))
return getter.NewLoadPolicy([]string{getter.GetDefaultPath(cautils.LocalExceptionsFilename)})
}
return downloadReleasedPolicy
@@ -63,7 +65,9 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
return nil
}
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport {
func getReporter(ctx context.Context, tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport {
ctx, span := otel.Tracer("").Start(ctx, "getReporter")
defer span.End()
if submit {
submitData := reporterv2.SubmitContextScan
if scanningContext != cautils.ContextCluster {
@@ -83,17 +87,19 @@ func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fw
return reporterv2.NewReportMock("", message)
}
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler {
func getResourceHandler(ctx context.Context, scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler {
ctx, span := otel.Tracer("").Start(ctx, "getResourceHandler")
defer span.End()
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
// scanInfo.HostSensor.SetBool(false)
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns, registryAdaptors)
return resourcehandler.NewFileResourceHandler(ctx, scanInfo.InputPatterns, registryAdaptors)
}
getter.GetKSCloudAPIConnector()
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors)
}
func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
func getHostSensorHandler(ctx context.Context, scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
return &hostsensorutils.HostSensorHandlerMock{}
}
@@ -102,12 +108,11 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet
// we need to determined which controls needs host scanner
if scanInfo.HostSensorEnabled.Get() == nil && hasHostSensorControls {
scanInfo.HostSensorEnabled.SetBool(false) // default - do not run host scanner
logger.L().Warning("Kubernetes cluster nodes scanning is disabled. This is required to collect valuable data for certain controls. You can enable it using the --enable-host-scan flag")
}
if hostSensorVal := scanInfo.HostSensorEnabled.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s, scanInfo.HostSensorYamlPath)
if err != nil {
logger.L().Warning(fmt.Sprintf("failed to create host scanner: %s", err.Error()))
logger.L().Ctx(ctx).Warning(fmt.Sprintf("failed to create host scanner: %s", err.Error()))
return &hostsensorutils.HostSensorHandlerMock{}
}
return hostSensorHandler
@@ -189,7 +194,7 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
}
// setPolicyGetter set the policy getter - local file/github release/Kubescape Cloud API
func getPolicyGetter(loadPoliciesFromFile []string, tenantEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getPolicyGetter(ctx context.Context, loadPoliciesFromFile []string, tenantEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if len(loadPoliciesFromFile) > 0 {
return getter.NewLoadPolicy(loadPoliciesFromFile)
}
@@ -200,12 +205,12 @@ func getPolicyGetter(loadPoliciesFromFile []string, tenantEmail string, framewor
if downloadReleasedPolicy == nil {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
return getDownloadReleasedPolicy(downloadReleasedPolicy)
return getDownloadReleasedPolicy(ctx, downloadReleasedPolicy)
}
// setConfigInputsGetter sets the config input getter - local file/github release/Kubescape Cloud API
func getConfigInputsGetter(ControlsInputs string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IControlsInputsGetter {
func getConfigInputsGetter(ctx context.Context, ControlsInputs string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IControlsInputsGetter {
if len(ControlsInputs) > 0 {
return getter.NewLoadPolicy([]string{ControlsInputs})
}
@@ -217,14 +222,14 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull config inputs, fallback to BE
logger.L().Warning("failed to get config inputs from github release, this may affect the scanning results", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to get config inputs from github release, this may affect the scanning results", helpers.Error(err))
}
return downloadReleasedPolicy
}
func getDownloadReleasedPolicy(downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getDownloadReleasedPolicy(ctx context.Context, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache
logger.L().Warning("failed to get policies from github release, loading policies from cache", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to get policies from github release, loading policies from cache", helpers.Error(err))
return getter.NewLoadPolicy(getDefaultFrameworksPaths())
} else {
return downloadReleasedPolicy
@@ -247,7 +252,10 @@ func listFrameworksNames(policyGetter getter.IPolicyGetter) []string {
return getter.NativeFrameworks
}
func getAttackTracksGetter(accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IAttackTracksGetter {
func getAttackTracksGetter(ctx context.Context, attackTracks, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IAttackTracksGetter {
if len(attackTracks) > 0 {
return getter.NewLoadPolicy([]string{attackTracks})
}
if accountID != "" {
g := getter.GetKSCloudAPIConnector() // download attack tracks from Kubescape Cloud backend
return g
@@ -255,19 +263,25 @@ func getAttackTracksGetter(accountID string, downloadReleasedPolicy *getter.Down
if downloadReleasedPolicy == nil {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull attack tracks, fallback to cache
logger.L().Warning("failed to get attack tracks from github release, loading attack tracks from cache", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to get attack tracks from github release, loading attack tracks from cache", helpers.Error(err))
return getter.NewLoadPolicy([]string{getter.GetDefaultPath(cautils.LocalAttackTracksFilename)})
}
return downloadReleasedPolicy
}
// getUIPrinter returns a printer that will be used to print to the programs UI (terminal)
func getUIPrinter(verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes) printer.IPrinter {
p := printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType)
func getUIPrinter(ctx context.Context, verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes) printer.IPrinter {
var p printer.IPrinter
if helpers.ToLevel(logger.L().GetLevel()) >= helpers.WarningLevel {
p = &printerv2.SilentPrinter{}
} else {
p = printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType)
// Since the UI of the program is a CLI (Stdout), it means that it should always print to Stdout
p.SetWriter(os.Stdout.Name())
// Since the UI of the program is a CLI (Stdout), it means that it should always print to Stdout
p.SetWriter(ctx, os.Stdout.Name())
}
return p
}

View File

@@ -1,10 +1,14 @@
package core
import (
"context"
"reflect"
"testing"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/stretchr/testify/assert"
)
func Test_getUIPrinter(t *testing.T) {
@@ -13,27 +17,91 @@ func Test_getUIPrinter(t *testing.T) {
VerboseMode: true,
View: "control",
}
wantFormatVersion := scanInfo.FormatVersion
wantVerboseMode := scanInfo.VerboseMode
wantViewType := cautils.ViewTypes(scanInfo.View)
got := getUIPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
gotValue := reflect.ValueOf(got).Elem()
gotFormatVersion := gotValue.FieldByName("formatVersion").String()
gotVerboseMode := gotValue.FieldByName("verboseMode").Bool()
gotViewType := cautils.ViewTypes(gotValue.FieldByName("viewType").String())
if gotFormatVersion != wantFormatVersion {
t.Errorf("Got: %s, want: %s", gotFormatVersion, wantFormatVersion)
type args struct {
ctx context.Context
formatVersion string
viewType cautils.ViewTypes
verboseMode bool
printAttack bool
loggerLevel helpers.Level
}
type wantTypes struct {
structType string
formatVersion string
viewType cautils.ViewTypes
verboseMode bool
}
tests := []struct {
name string
args args
want wantTypes
testAllFields bool
}{
{
name: "Test getUIPrinter PrettyPrinter",
args: args{
ctx: context.TODO(),
verboseMode: scanInfo.VerboseMode,
formatVersion: scanInfo.FormatVersion,
printAttack: scanInfo.PrintAttackTree,
viewType: cautils.ViewTypes(scanInfo.View),
loggerLevel: helpers.InfoLevel,
},
want: wantTypes{
structType: "*printer.PrettyPrinter",
formatVersion: scanInfo.FormatVersion,
verboseMode: scanInfo.VerboseMode,
viewType: cautils.ViewTypes(scanInfo.View),
},
testAllFields: true,
},
{
name: "Test getUIPrinter SilentPrinter",
args: args{
ctx: context.TODO(),
verboseMode: scanInfo.VerboseMode,
formatVersion: scanInfo.FormatVersion,
printAttack: scanInfo.PrintAttackTree,
viewType: cautils.ViewTypes(scanInfo.View),
loggerLevel: helpers.WarningLevel,
},
want: wantTypes{
structType: "*printer.SilentPrinter",
formatVersion: "",
verboseMode: false,
viewType: cautils.ViewTypes(""),
},
testAllFields: false,
},
}
if gotVerboseMode != wantVerboseMode {
t.Errorf("Got: %t, want: %t", gotVerboseMode, wantVerboseMode)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger.L().SetLevel(tt.args.loggerLevel.String())
got := getUIPrinter(tt.args.ctx, tt.args.verboseMode, tt.args.formatVersion, tt.args.printAttack, tt.args.viewType)
if gotViewType != wantViewType {
t.Errorf("Got: %v, want: %v", gotViewType, wantViewType)
}
assert.Equal(t, tt.want.structType, reflect.TypeOf(got).String())
if !tt.testAllFields {
return
}
gotValue := reflect.ValueOf(got).Elem()
gotFormatVersion := gotValue.FieldByName("formatVersion").String()
gotVerboseMode := gotValue.FieldByName("verboseMode").Bool()
gotViewType := cautils.ViewTypes(gotValue.FieldByName("viewType").String())
if gotFormatVersion != tt.want.formatVersion {
t.Errorf("Got: %s, want: %s", gotFormatVersion, tt.want.formatVersion)
}
if gotVerboseMode != tt.want.verboseMode {
t.Errorf("Got: %t, want: %t", gotVerboseMode, tt.want.verboseMode)
}
if gotViewType != tt.want.viewType {
t.Errorf("Got: %v, want: %v", gotViewType, tt.want.viewType)
}
})
}
}

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"encoding/json"
"fmt"
"sort"
@@ -13,13 +14,13 @@ import (
"github.com/olekukonko/tablewriter"
)
var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
var listFunc = map[string]func(context.Context, *metav1.ListPolicies) ([]string, error){
"controls": listControls,
"frameworks": listFrameworks,
"exceptions": listExceptions,
}
var listFormatFunc = map[string]func(string, []string){
var listFormatFunc = map[string]func(context.Context, string, []string){
"pretty-print": prettyPrintListFormat,
"json": jsonListFormat,
}
@@ -31,16 +32,16 @@ func ListSupportActions() []string {
}
return commands
}
func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
func (ks *Kubescape) List(ctx context.Context, listPolicies *metav1.ListPolicies) error {
if policyListerFunc, ok := listFunc[listPolicies.Target]; ok {
policies, err := policyListerFunc(listPolicies)
policies, err := policyListerFunc(ctx, listPolicies)
if err != nil {
return err
}
sort.Strings(policies)
if listFormatFunction, ok := listFormatFunc[listPolicies.Format]; ok {
listFormatFunction(listPolicies.Target, policies)
listFormatFunction(ctx, listPolicies.Target, policies)
} else {
return fmt.Errorf("Invalid format \"%s\", Supported formats: 'pretty-print'/'json' ", listPolicies.Format)
}
@@ -50,26 +51,26 @@ func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
return fmt.Errorf("unknown command to download")
}
func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
func listFrameworks(ctx context.Context, listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
policyGetter := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), true, nil)
return listFrameworksNames(policyGetter), nil
}
func listControls(listPolicies *metav1.ListPolicies) ([]string, error) {
func listControls(ctx context.Context, listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
policyGetter := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), false, nil)
return policyGetter.ListControls()
}
func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
func listExceptions(ctx context.Context, listPolicies *metav1.ListPolicies) ([]string, error) {
// load tenant metav1
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi())
var exceptionsNames []string
ksCloudAPI := getExceptionsGetter("", tenant.GetAccountID(), nil)
ksCloudAPI := getExceptionsGetter(ctx, "", tenant.GetAccountID(), nil)
exceptions, err := ksCloudAPI.GetExceptions("")
if err != nil {
return exceptionsNames, err
@@ -80,15 +81,15 @@ func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
return exceptionsNames, nil
}
func prettyPrintListFormat(targetPolicy string, policies []string) {
func prettyPrintListFormat(ctx context.Context, targetPolicy string, policies []string) {
if targetPolicy == "controls" {
prettyPrintControls(policies)
prettyPrintControls(ctx, policies)
return
}
header := fmt.Sprintf("Supported %s", targetPolicy)
policyTable := tablewriter.NewWriter(printer.GetWriter(""))
policyTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
policyTable.SetAutoWrapText(true)
policyTable.SetHeader([]string{header})
policyTable.SetHeaderLine(true)
@@ -103,14 +104,14 @@ func prettyPrintListFormat(targetPolicy string, policies []string) {
policyTable.Render()
}
func jsonListFormat(targetPolicy string, policies []string) {
func jsonListFormat(_ context.Context, _ string, policies []string) {
j, _ := json.MarshalIndent(policies, "", " ")
fmt.Printf("%s\n", j)
}
func prettyPrintControls(policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(""))
func prettyPrintControls(ctx context.Context, policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
controlsTable.SetAutoWrapText(true)
controlsTable.SetHeader([]string{"Control ID", "Control Name", "Docs", "Frameworks"})
controlsTable.SetHeaderLine(true)

View File

@@ -1,14 +1,12 @@
package core
import (
"context"
"fmt"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/k8s-interface/k8sinterface"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/pkg/hostsensorutils"
@@ -19,6 +17,8 @@ import (
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"go.opentelemetry.io/otel"
"github.com/kubescape/opa-utils/resources"
)
@@ -32,20 +32,22 @@ type componentInterfaces struct {
hostSensorHandler hostsensorutils.IHostSensor
}
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInterfaces {
ctx, span := otel.Tracer("").Start(ctx, "getInterfaces")
defer span.End()
// ================== setup k8s interface object ======================================
var k8s *k8sinterface.KubernetesApi
if scanInfo.GetScanningContext() == cautils.ContextCluster {
k8s = getKubernetesApi()
if k8s == nil {
logger.L().Fatal("failed connecting to Kubernetes cluster")
logger.L().Ctx(ctx).Fatal("failed connecting to Kubernetes cluster")
}
}
// ================== setup tenant object ======================================
tenantConfig := getTenantConfig(&scanInfo.Credentials, scanInfo.KubeContext, scanInfo.CustomClusterName, k8s)
ctxTenant, spanTenant := otel.Tracer("").Start(ctx, "setup tenant")
tenantConfig := getTenantConfig(&scanInfo.Credentials, k8sinterface.GetContextName(), scanInfo.CustomClusterName, k8s)
// Set submit behavior AFTER loading tenant config
setSubmitBehavior(scanInfo, tenantConfig)
@@ -53,58 +55,56 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
if scanInfo.Submit {
// submit - Create tenant & Submit report
if err := tenantConfig.SetTenant(); err != nil {
logger.L().Error(err.Error())
logger.L().Ctx(ctxTenant).Error(err.Error())
}
if scanInfo.OmitRawResources {
logger.L().Warning("omit-raw-resources flag will be ignored in submit mode")
logger.L().Ctx(ctx).Warning("omit-raw-resources flag will be ignored in submit mode")
}
}
spanTenant.End()
// ================== version testing ======================================
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
v := cautils.NewIVersionCheckHandler(ctx)
v.CheckLatestVersion(ctx, cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
// ================== setup host scanner object ======================================
hostSensorHandler := getHostSensorHandler(scanInfo, k8s)
if err := hostSensorHandler.Init(); err != nil {
logger.L().Error("failed to init host scanner", helpers.Error(err))
ctxHostScanner, spanHostScanner := otel.Tracer("").Start(ctx, "setup host scanner")
hostSensorHandler := getHostSensorHandler(ctx, scanInfo, k8s)
if err := hostSensorHandler.Init(ctxHostScanner); err != nil {
logger.L().Ctx(ctxHostScanner).Error("failed to init host scanner", helpers.Error(err))
hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{}
}
// excluding hostsensor namespace
if len(scanInfo.IncludeNamespaces) == 0 && hostSensorHandler.GetNamespace() != "" {
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace())
}
spanHostScanner.End()
// ================== setup registry adaptors ======================================
registryAdaptors, err := resourcehandler.NewRegistryAdaptors()
if err != nil {
logger.L().Error("failed to initialize registry adaptors", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to initialize registry adaptors", helpers.Error(err))
}
// ================== setup resource collector object ======================================
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors)
resourceHandler := getResourceHandler(ctx, scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors)
// ================== setup reporter & printer objects ======================================
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
reportHandler := getReporter(ctx, tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
// setup printers
formats := scanInfo.Formats()
outputPrinters := make([]printer.IPrinter, 0)
for _, format := range formats {
printerHandler := resultshandling.NewPrinter(format, scanInfo.FormatVersion, scanInfo.PrintAttackTree, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(scanInfo.Output)
printerHandler := resultshandling.NewPrinter(ctx, format, scanInfo.FormatVersion, scanInfo.PrintAttackTree, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(ctx, scanInfo.Output)
outputPrinters = append(outputPrinters, printerHandler)
}
uiPrinter := getUIPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
uiPrinter := getUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
// ================== return interface ======================================
@@ -118,13 +118,16 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
}
}
func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
ctx, spanScan := otel.Tracer("").Start(ctx, "kubescape.Scan")
defer spanScan.End()
logger.L().Info("Kubescape scanner starting")
// ===================== Initialization =====================
scanInfo.Init() // initialize scan info
ctxInit, spanInit := otel.Tracer("").Start(ctx, "initialization")
scanInfo.Init(ctxInit) // initialize scan info
interfaces := getInterfaces(scanInfo)
interfaces := getInterfaces(ctxInit, scanInfo)
cautils.ClusterName = interfaces.tenantConfig.GetContextName() // TODO - Deprecated
cautils.CustomerGUID = interfaces.tenantConfig.GetAccountID() // TODO - Deprecated
@@ -134,10 +137,10 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetTenantEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.AttackTracksGetter = getAttackTracksGetter(interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.PolicyGetter = getPolicyGetter(ctx, scanInfo.UseFrom, interfaces.tenantConfig.GetTenantEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(ctx, scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(ctx, scanInfo.UseExceptions, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.AttackTracksGetter = getAttackTracksGetter(ctx, scanInfo.AttackTracks, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
// TODO - list supported frameworks/controls
if scanInfo.ScanAll {
@@ -147,34 +150,40 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
// remove host scanner components
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
logger.L().Error("failed to tear down host scanner", helpers.Error(err))
logger.L().Ctx(ctxInit).Error("failed to tear down host scanner", helpers.Error(err))
}
}()
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.outputPrinters, interfaces.uiPrinter)
spanInit.End()
// ===================== policies & resources =====================
ctxPolicies, spanPolicies := otel.Tracer("").Start(ctx, "policies & resources")
policyHandler := policyhandler.NewPolicyHandler(interfaces.resourceHandler)
scanData, err := policyHandler.CollectResources(scanInfo.PolicyIdentifier, scanInfo)
scanData, err := policyHandler.CollectResources(ctxPolicies, scanInfo.PolicyIdentifier, scanInfo)
if err != nil {
return resultsHandling, err
}
spanPolicies.End()
// ========================= opa testing =====================
ctxOpa, spanOpa := otel.Tracer("").Start(ctx, "opa testing")
deps := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), interfaces.tenantConfig.GetContextName())
reportResults := opaprocessor.NewOPAProcessor(scanData, deps)
if err := reportResults.ProcessRulesListenner(); err != nil {
if err := reportResults.ProcessRulesListenner(ctxOpa, cautils.NewProgressHandler("")); err != nil {
// TODO - do something
return resultsHandling, fmt.Errorf("%w", err)
}
spanOpa.End()
// ======================== prioritization ===================
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(scanInfo.Getters.AttackTracksGetter, scanInfo.PrintAttackTree); err != nil {
logger.L().Warning("failed to get attack tracks, this may affect the scanning results", helpers.Error(err))
_, spanPrioritization := otel.Tracer("").Start(ctx, "prioritization")
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(ctx, 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))
} else if err := priotizationHandler.PrioritizeResources(scanData); err != nil {
return resultsHandling, fmt.Errorf("%w", err)
}
spanPrioritization.End()
// ========================= results handling =====================
resultsHandling.SetData(scanData)

View File

@@ -1,6 +1,8 @@
package core
import (
"context"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/meta/cliinterfaces"
@@ -9,7 +11,7 @@ import (
"github.com/kubescape/go-logger/helpers"
)
func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
func (ks *Kubescape) Submit(ctx context.Context, submitInterfaces cliinterfaces.SubmitInterfaces) error {
// list resources
report, err := submitInterfaces.SubmitObjects.SetResourcesReport()
@@ -26,7 +28,7 @@ func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) err
AllResources: allresources,
Metadata: &report.Metadata,
}
if err := submitInterfaces.Reporter.Submit(o); err != nil {
if err := submitInterfaces.Reporter.Submit(ctx, o); err != nil {
return err
}
logger.L().Success("Data has been submitted successfully")
@@ -35,13 +37,13 @@ func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) err
return nil
}
func (ks *Kubescape) SubmitExceptions(credentials *cautils.Credentials, excPath string) error {
func (ks *Kubescape) SubmitExceptions(ctx context.Context, credentials *cautils.Credentials, excPath string) error {
logger.L().Info("submitting exceptions", helpers.String("path", excPath))
// load cached config
tenantConfig := getTenantConfig(credentials, "", "", getKubernetesApi())
if err := tenantConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed setting account ID", helpers.Error(err))
}
// load exceptions from file

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