mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-31 15:54:24 +00:00
Compare commits
664
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58c85438d3 | ||
|
|
2fc5b069c5 | ||
|
|
ef00efa018 | ||
|
|
3078f10596 | ||
|
|
566205085d | ||
|
|
9fa419afb6 | ||
|
|
af0d901038 | ||
|
|
926ea897e8 | ||
|
|
9bbd9dbf8b | ||
|
|
e1daaded67 | ||
|
|
66060ea68e | ||
|
|
5c97cfcc60 | ||
|
|
4b00aac812 | ||
|
|
e0f412521b | ||
|
|
48bbd7fc79 | ||
|
|
8a98a12eaa | ||
|
|
7b55e465b6 | ||
|
|
7d9ebbccd0 | ||
|
|
0f70295e37 | ||
|
|
b8956ebe37 | ||
|
|
55ef498b1a | ||
|
|
b206e011a9 | ||
|
|
287057e360 | ||
|
|
21c679fa8e | ||
|
|
6139196c34 | ||
|
|
ef03ffa4e1 | ||
|
|
e22aef93e6 | ||
|
|
974196bec8 | ||
|
|
0c8c85c526 | ||
|
|
3c588fed81 | ||
|
|
8a3c0fe609 | ||
|
|
40e988e109 | ||
|
|
9ce5e3ada7 | ||
|
|
8d08f22604 | ||
|
|
349903ab57 | ||
|
|
2f077de74a | ||
|
|
fd642e8d71 | ||
|
|
4620b07dac | ||
|
|
c88663b7ef | ||
|
|
6197e7b342 | ||
|
|
e4a596696b | ||
|
|
7dd436c971 | ||
|
|
c76fbaef3b | ||
|
|
6b329b1835 | ||
|
|
d111781387 | ||
|
|
6f68c5cacc | ||
|
|
a1cfc2d3df | ||
|
|
958ca6da65 | ||
|
|
a140007ecb | ||
|
|
75dec160bb | ||
|
|
df16872333 | ||
|
|
40a1e8abbd | ||
|
|
d623d26e64 | ||
|
|
ca2b6f3ffc | ||
|
|
c572c8a56d | ||
|
|
797abe634a | ||
|
|
a18b5ff49f | ||
|
|
0e1c6c2d3a | ||
|
|
fc5b484b27 | ||
|
|
2d225de838 | ||
|
|
a332c806bb | ||
|
|
e236bcecf5 | ||
|
|
5bf309117a | ||
|
|
6cfb68773f | ||
|
|
902d82485a | ||
|
|
5b51a4b2ad | ||
|
|
9a5aa8ec06 | ||
|
|
e2d5490700 | ||
|
|
8429c1c36d | ||
|
|
882cd3c25e | ||
|
|
0204d24f5e | ||
|
|
27fe201a48 | ||
|
|
a154fb46b8 | ||
|
|
530c627ca7 | ||
|
|
9861c22fe0 | ||
|
|
0a9ea21e11 | ||
|
|
a591beb1bb | ||
|
|
967969288f | ||
|
|
ddb72cff0f | ||
|
|
240f57278a | ||
|
|
b2e322fab7 | ||
|
|
6a38d22634 | ||
|
|
6525c2f17a | ||
|
|
e0698b28d1 | ||
|
|
49146ffddf | ||
|
|
b41e23415b | ||
|
|
55688eaab0 | ||
|
|
9c89b13c13 | ||
|
|
4db961bad3 | ||
|
|
e4bf049d06 | ||
|
|
184b4c3f70 | ||
|
|
e1620bcb0f | ||
|
|
a2f3fab936 | ||
|
|
0cd2ad5636 | ||
|
|
6fab9a89fd | ||
|
|
87f5d32bb3 | ||
|
|
6540facaea | ||
|
|
d11b5f8f12 | ||
|
|
8a982afe21 | ||
|
|
f3303974d3 | ||
|
|
c69f0a0085 | ||
|
|
9c9df486f0 | ||
|
|
e12aa389a3 | ||
|
|
e8db8525ab | ||
|
|
87e9b0f807 | ||
|
|
65b2a81226 | ||
|
|
ad63b6991e | ||
|
|
b536be1c56 | ||
|
|
6b078fd668 | ||
|
|
436521d932 | ||
|
|
ee09ccb4aa | ||
|
|
52406f6df2 | ||
|
|
7687148a27 | ||
|
|
44b7fc4624 | ||
|
|
68050d3df3 | ||
|
|
47fe1b77e2 | ||
|
|
04057e8911 | ||
|
|
401fdec1fb | ||
|
|
4e556c86ef | ||
|
|
72c8de3e67 | ||
|
|
3786cbcd5f | ||
|
|
1084b00ef7 | ||
|
|
7f5ddcc215 | ||
|
|
60b8f9d3b9 | ||
|
|
4c4f12ffda | ||
|
|
4c0729378d | ||
|
|
fda49bec18 | ||
|
|
9606cf21eb | ||
|
|
d3da9dd90e | ||
|
|
4fb010847f | ||
|
|
8eac17d826 | ||
|
|
8a1abb60c9 | ||
|
|
74776bed06 | ||
|
|
3373a8e6c8 | ||
|
|
7db74516b3 | ||
|
|
3ae2f9e0bf | ||
|
|
91a8a13dc6 | ||
|
|
700ed5ecec | ||
|
|
50aebcaf88 | ||
|
|
ad96963706 | ||
|
|
95832bdb12 | ||
|
|
d7bdc0aaef | ||
|
|
1c0c836286 | ||
|
|
822252d03b | ||
|
|
a20fdb30f9 | ||
|
|
2e339f964a | ||
|
|
bb4498a3d5 | ||
|
|
dae4392efc | ||
|
|
8b3a1608c2 | ||
|
|
148bdacebe | ||
|
|
b60edc1f6b | ||
|
|
d28462b642 | ||
|
|
ef9b37d5c0 | ||
|
|
764f260cea | ||
|
|
e771218bba | ||
|
|
c627a3e9aa | ||
|
|
1c58644118 | ||
|
|
398fa37a23 | ||
|
|
412d948585 | ||
|
|
6bc0432ed5 | ||
|
|
b2ebc8410a | ||
|
|
94b6fbda91 | ||
|
|
3ce785a7b4 | ||
|
|
e4ecc98aae | ||
|
|
53a4befb01 | ||
|
|
fee0bf3ea1 | ||
|
|
6811a22651 | ||
|
|
fe2dd69b08 | ||
|
|
26a9748700 | ||
|
|
cef0a54bc3 | ||
|
|
553a51ddc2 | ||
|
|
61dc09d15d | ||
|
|
9b91ba2b91 | ||
|
|
18d60c98d7 | ||
|
|
00054b5cd9 | ||
|
|
24fc86cbb3 | ||
|
|
861fb22257 | ||
|
|
7e14204be8 | ||
|
|
09ea6ba6c4 | ||
|
|
a83725fbed | ||
|
|
34210cfc06 | ||
|
|
2e20d52030 | ||
|
|
9b0b61ef02 | ||
|
|
0203aec9e0 | ||
|
|
862a4ad76c | ||
|
|
4b7883c464 | ||
|
|
2bf835d3d2 | ||
|
|
09733eb298 | ||
|
|
096749a455 | ||
|
|
b06e549d81 | ||
|
|
a5f5967bb2 | ||
|
|
7a4f5f34f7 | ||
|
|
e33b081dc6 | ||
|
|
c8fdaa3923 | ||
|
|
8a491bcf7d | ||
|
|
f5e71db5e0 | ||
|
|
456f79d80c | ||
|
|
9f728833a7 | ||
|
|
f01ce849dc | ||
|
|
572d5a1f2e | ||
|
|
c69571abcd | ||
|
|
8ddb9fbb84 | ||
|
|
193b79c221 | ||
|
|
a25a6148f2 | ||
|
|
0a63e707c2 | ||
|
|
f53a887291 | ||
|
|
ca35510d74 | ||
|
|
5d7c8d9cd2 | ||
|
|
0d01790b22 | ||
|
|
78b518e22b | ||
|
|
17af724995 | ||
|
|
c9898d6d01 | ||
|
|
259538d5e4 | ||
|
|
4785feae0e | ||
|
|
8be83fc632 | ||
|
|
005d251106 | ||
|
|
b6c8adfc64 | ||
|
|
7a711095cd | ||
|
|
b0bb348480 | ||
|
|
c1b050b8b9 | ||
|
|
28c7e803ac | ||
|
|
919acfd548 | ||
|
|
1586cdae5e | ||
|
|
cb74dae296 | ||
|
|
3608aa3536 | ||
|
|
c59b08c40b | ||
|
|
73361a1cea | ||
|
|
794268cec5 | ||
|
|
06b41cf8e6 | ||
|
|
a419b4b898 | ||
|
|
622bc48688 | ||
|
|
c0659f712a | ||
|
|
796983a530 | ||
|
|
8dc042e594 | ||
|
|
e881a0f274 | ||
|
|
5641160177 | ||
|
|
dca49b1972 | ||
|
|
b8b60e6bc5 | ||
|
|
9d65b49cb4 | ||
|
|
f1334190d8 | ||
|
|
c434181dfd | ||
|
|
f3cfacae9a | ||
|
|
77c7986797 | ||
|
|
2ac716d6db | ||
|
|
daee5fb4d2 | ||
|
|
d587d40451 | ||
|
|
f0cd88bd0e | ||
|
|
aca17904fa | ||
|
|
0157802ac1 | ||
|
|
10766d708d | ||
|
|
f231d8b080 | ||
|
|
590a7b2697 | ||
|
|
3c3421644f | ||
|
|
f1f68da25d | ||
|
|
48df7fdebf | ||
|
|
f2e8691bf4 | ||
|
|
344d54155a | ||
|
|
8ecffa3039 | ||
|
|
efbbfc1c68 | ||
|
|
dc68c1b955 | ||
|
|
5de13bdd8a | ||
|
|
5d0f498425 | ||
|
|
716558ffcb | ||
|
|
23929b3e68 | ||
|
|
a5612abc8c | ||
|
|
78dddc1e03 | ||
|
|
76020d4d47 | ||
|
|
b0af1390b5 | ||
|
|
bc3cd43126 | ||
|
|
087dae07d8 | ||
|
|
0baf4fb224 | ||
|
|
0f8ea48f2f | ||
|
|
ec207c50ce | ||
|
|
b59b40b822 | ||
|
|
aa51045329 | ||
|
|
1a9f854063 | ||
|
|
6bdcb509e1 | ||
|
|
ce1e9de104 | ||
|
|
2471bc569a | ||
|
|
d0ef75d8d9 | ||
|
|
aa79a289ce | ||
|
|
0340ab9570 | ||
|
|
a2929f2efb | ||
|
|
bf4db86dec | ||
|
|
a339dafcc6 | ||
|
|
f376516aad | ||
|
|
816b49fac5 | ||
|
|
6851350093 | ||
|
|
d5873c0437 | ||
|
|
a2dba30869 | ||
|
|
0662dff13f | ||
|
|
0ae26bddfc | ||
|
|
43efabef6c | ||
|
|
e73fc14f62 | ||
|
|
89fe8fa8e2 | ||
|
|
634ef2e599 | ||
|
|
4efb70a508 | ||
|
|
c3ae0aa873 | ||
|
|
a109cd2816 | ||
|
|
78fb540bbb | ||
|
|
5b543bf058 | ||
|
|
9802fc141a | ||
|
|
ea038085ba | ||
|
|
6ff1c436a0 | ||
|
|
1b50fdba28 | ||
|
|
230d2571f9 | ||
|
|
6c818cbcc3 | ||
|
|
694cee1afb | ||
|
|
bc89f4383e | ||
|
|
84d4e4a604 | ||
|
|
5fbf8ddfe9 | ||
|
|
ddcd06d9be | ||
|
|
5214a37d6d | ||
|
|
a1f4ae73cf | ||
|
|
d0bc37c281 | ||
|
|
85393e6f78 | ||
|
|
e3104ae5ee | ||
|
|
be523c11c8 | ||
|
|
797b627695 | ||
|
|
5ac670ed4f | ||
|
|
bf9111397b | ||
|
|
17dd9de6d8 | ||
|
|
e4400ecf10 | ||
|
|
cbba5acc31 | ||
|
|
046f1b2e5f | ||
|
|
9e8f88c889 | ||
|
|
2d73b9b8f4 | ||
|
|
9a7190c9c2 | ||
|
|
a2b6bdc461 | ||
|
|
056d482023 | ||
|
|
239b248935 | ||
|
|
5bd394dffe | ||
|
|
1195b75acc | ||
|
|
fee70bc9b4 | ||
|
|
01716d9020 | ||
|
|
b87e6d20d7 | ||
|
|
11592634f2 | ||
|
|
bc308de571 | ||
|
|
4bee4d482a | ||
|
|
82ec3e8779 | ||
|
|
85777546e8 | ||
|
|
ec69d8073a | ||
|
|
e6053ce218 | ||
|
|
f01910e4f2 | ||
|
|
8848cadc0a | ||
|
|
2c7d71a0d9 | ||
|
|
dcd1b1121a | ||
|
|
8a6e222f7a | ||
|
|
a4c39c25f1 | ||
|
|
628d50cf0d | ||
|
|
f70e3deade | ||
|
|
14f06d6497 | ||
|
|
3c6e06837c | ||
|
|
e778444d1d | ||
|
|
a4cca188ef | ||
|
|
76ee608ef8 | ||
|
|
7af5c82371 | ||
|
|
172530153c | ||
|
|
0114d0462f | ||
|
|
6502330512 | ||
|
|
9bf9ebe4fd | ||
|
|
406d5864ee | ||
|
|
0f9ebecbb7 | ||
|
|
0331af02ac | ||
|
|
64fb823276 | ||
|
|
33f2c80a78 | ||
|
|
84ce7a8b52 | ||
|
|
1a6b2eaa7d | ||
|
|
df373279e9 | ||
|
|
6a09171851 | ||
|
|
93d257941b | ||
|
|
28f8915f6f | ||
|
|
fef99fd5fb | ||
|
|
1e1c7cc1ce | ||
|
|
7e7d86f14a | ||
|
|
41cdc2bcc7 | ||
|
|
c41866db38 | ||
|
|
f36d23c9a7 | ||
|
|
8ac28fbcfd | ||
|
|
7f41ae7385 | ||
|
|
4c5f3a212c | ||
|
|
ffa29f2f6e | ||
|
|
2ef9e27ee3 | ||
|
|
d4b93d79b5 | ||
|
|
d00afbdc87 | ||
|
|
5b0868e26c | ||
|
|
415c319208 | ||
|
|
1aca7a754c | ||
|
|
147c3c455b | ||
|
|
d997ebb2cc | ||
|
|
50cb79ee2f | ||
|
|
2b34fd39f6 | ||
|
|
0c82137834 | ||
|
|
413b86e7cf | ||
|
|
23a20a582e | ||
|
|
8411fcb5fc | ||
|
|
41e585643b | ||
|
|
aca5824240 | ||
|
|
e65b346afd | ||
|
|
98cb954f74 | ||
|
|
778edd5fec | ||
|
|
06deda7e5f | ||
|
|
26a00a14df | ||
|
|
12abea371d | ||
|
|
a17cf1bbb6 | ||
|
|
5d51942768 | ||
|
|
3122d727a5 | ||
|
|
e5f89d5bc7 | ||
|
|
efc60d2bf4 | ||
|
|
f7fd355dc1 | ||
|
|
7bd402bf4e | ||
|
|
b69962efb6 | ||
|
|
2b8b223403 | ||
|
|
a024ab31a0 | ||
|
|
9969e271ed | ||
|
|
f1449b66d6 | ||
|
|
3c0f360244 | ||
|
|
6e3c008a89 | ||
|
|
9d97b1a7ee | ||
|
|
d07f1e982a | ||
|
|
402e0e3107 | ||
|
|
c5716bf9b6 | ||
|
|
bfdff563e6 | ||
|
|
4be83f240a | ||
|
|
45c32abcdf | ||
|
|
c0ac4c7c30 | ||
|
|
c90cb3712b | ||
|
|
23c3884ab7 | ||
|
|
a491e39a18 | ||
|
|
78d2fb9fd5 | ||
|
|
aac6038565 | ||
|
|
0449d6372c | ||
|
|
bc1e6c0626 | ||
|
|
c1d061ef70 | ||
|
|
9788fe4236 | ||
|
|
7fd0798b7c | ||
|
|
82ab439e9a | ||
|
|
54280ee2dc | ||
|
|
434460b978 | ||
|
|
808fa96407 | ||
|
|
2c0c1f7d09 | ||
|
|
037e40f8e4 | ||
|
|
e0ed891fc4 | ||
|
|
dfc8e3e38f | ||
|
|
aef4a68c46 | ||
|
|
3c929bd68f | ||
|
|
444d820f98 | ||
|
|
304bb27502 | ||
|
|
a6db526eec | ||
|
|
3ace44979a | ||
|
|
493d6a9210 | ||
|
|
3762d70ba3 | ||
|
|
03a26ec507 | ||
|
|
c3e3381c63 | ||
|
|
f8a8266c9d | ||
|
|
d9c2933dc5 | ||
|
|
cad99c5e0f | ||
|
|
9f2de7d2f9 | ||
|
|
4181ca56be | ||
|
|
d45750b042 | ||
|
|
16191a7b15 | ||
|
|
0c149461b3 | ||
|
|
3ee39cff2a | ||
|
|
41ba118cc4 | ||
|
|
e0587fe0cf | ||
|
|
50481665ce | ||
|
|
a49c744e08 | ||
|
|
aa32634105 | ||
|
|
b27898de1d | ||
|
|
b703357027 | ||
|
|
27cd9b22df | ||
|
|
5bf85366e0 | ||
|
|
30bc971f4b | ||
|
|
3950d7eba8 | ||
|
|
2f8a3d2ef8 | ||
|
|
3b64bbd3a8 | ||
|
|
09d099891a | ||
|
|
a6b10a8611 | ||
|
|
c239ede3f9 | ||
|
|
66f2754017 | ||
|
|
9138ecdce9 | ||
|
|
2b66368cf2 | ||
|
|
aa3425a7de | ||
|
|
a31b15c26c | ||
|
|
f2301d5ed6 | ||
|
|
df10253056 | ||
|
|
d5acdc766a | ||
|
|
e389e0136f | ||
|
|
8bb3bd0dcb | ||
|
|
4d4bf3fa11 | ||
|
|
e99c58405c | ||
|
|
2177704b4b | ||
|
|
2ffe7f3ef7 | ||
|
|
158263a8bf | ||
|
|
469986dd28 | ||
|
|
ff101087bf | ||
|
|
b2151e2e9c | ||
|
|
2c4244b1fb | ||
|
|
260cdf575a | ||
|
|
ab4190c215 | ||
|
|
7f97b0a57f | ||
|
|
2c2dd82d0c | ||
|
|
2511df1732 | ||
|
|
f955dd76d9 | ||
|
|
a08cc769c8 | ||
|
|
77ac5e3b91 | ||
|
|
2da8f2b1eb | ||
|
|
38e024216c | ||
|
|
8e4847ec89 | ||
|
|
c6d34e8089 | ||
|
|
880523076d | ||
|
|
3d2f1a3aa7 | ||
|
|
c9ff96144d | ||
|
|
234f8c2958 | ||
|
|
da87c0d81e | ||
|
|
7732ec7d34 | ||
|
|
a1b9b2171f | ||
|
|
30e3fd9e46 | ||
|
|
3db541a42a | ||
|
|
d5abe16180 | ||
|
|
564b18c388 | ||
|
|
13e40eb03e | ||
|
|
b402ced402 | ||
|
|
6bbb9d04a6 | ||
|
|
6616657c91 | ||
|
|
853b833cfb | ||
|
|
c047b29140 | ||
|
|
c4a39662ae | ||
|
|
66e804f212 | ||
|
|
9d4fa55c13 | ||
|
|
ff05ce4da1 | ||
|
|
0474c7995c | ||
|
|
1a679f371f | ||
|
|
05f7170add | ||
|
|
19acb873af | ||
|
|
0b566f9666 | ||
|
|
67bf89537a | ||
|
|
d0681a9e20 | ||
|
|
31bff99b3d | ||
|
|
48c7e65a39 | ||
|
|
1b407639f0 | ||
|
|
4d7d5718d5 | ||
|
|
7955048e79 | ||
|
|
8e0b715f12 | ||
|
|
1d81261d97 | ||
|
|
114a3088a4 | ||
|
|
bc8f3eba4d | ||
|
|
8e087196c9 | ||
|
|
744e7ff5ac | ||
|
|
90b84b57d3 | ||
|
|
0a2b7cf152 | ||
|
|
ebbccd04f1 | ||
|
|
2b431fc79f | ||
|
|
fe7c3e7548 | ||
|
|
0e5f929044 | ||
|
|
47a6e28d71 | ||
|
|
de5742433b | ||
|
|
3fcccd0bcd | ||
|
|
00938cadb1 | ||
|
|
9fb26643ba | ||
|
|
e4890f9d9d | ||
|
|
980b9b4770 | ||
|
|
348cea67c0 | ||
|
|
f4d89066d9 | ||
|
|
b26dc899be | ||
|
|
25327d618d | ||
|
|
3951295c0c | ||
|
|
ff9c3b52d6 | ||
|
|
af8c18eb4e | ||
|
|
6fbfcc7f5f | ||
|
|
7c7132f9c4 | ||
|
|
62e30f929c | ||
|
|
ddaafd5876 | ||
|
|
1f43e6eff9 | ||
|
|
aa118c05c5 | ||
|
|
cca17b9378 | ||
|
|
14ed19e3a8 | ||
|
|
8caf8f794c | ||
|
|
cba9ad61e4 | ||
|
|
e64a0eff0f | ||
|
|
23c65b8fde | ||
|
|
a7c93f3237 | ||
|
|
7b9402f3d0 | ||
|
|
4badcca4f8 | ||
|
|
c6daa60f26 | ||
|
|
f9aa2bb8be | ||
|
|
66ac395705 | ||
|
|
16a251254e | ||
|
|
751958907c | ||
|
|
60012ab19d | ||
|
|
65d7ba020b | ||
|
|
9456c6198a | ||
|
|
45ce1a0650 | ||
|
|
4c5db5295c | ||
|
|
a2ad0cdf30 | ||
|
|
0c70a64e84 | ||
|
|
73c96f8346 | ||
|
|
0974c5f333 | ||
|
|
7db0746416 | ||
|
|
8f0bf5e896 | ||
|
|
57abe1c839 | ||
|
|
43183962ad | ||
|
|
87948b458e | ||
|
|
ab5c3eb4f8 | ||
|
|
320a2a2c77 | ||
|
|
dbc8e140e3 | ||
|
|
21ac395d4c | ||
|
|
8a8c2b5097 | ||
|
|
3bea772c6b | ||
|
|
34679c98d6 | ||
|
|
2b41445d57 | ||
|
|
796c87bc93 | ||
|
|
a83e08aa9e | ||
|
|
ae794c7c32 | ||
|
|
edc78bfd6b | ||
|
|
9263adeb78 | ||
|
|
bfdc87723b | ||
|
|
8d23e81b1c | ||
|
|
f0cd924016 | ||
|
|
c425e8249b | ||
|
|
1ece8bbcd6 | ||
|
|
5fb2d7c3ce | ||
|
|
64aebe84fe | ||
|
|
de831b0abe | ||
|
|
68af4f6c73 | ||
|
|
52981b54b9 | ||
|
|
a366594714 | ||
|
|
1fb36f316b | ||
|
|
30ffa8f00b | ||
|
|
5855918ade | ||
|
|
f9005c875f | ||
|
|
91bf99ca45 | ||
|
|
8176063fef | ||
|
|
3373822240 | ||
|
|
7e16702b2f | ||
|
|
f54b64f1f8 | ||
|
|
2c337ab3f6 | ||
|
|
5279d937d7 | ||
|
|
48c31a1616 | ||
|
|
917a2ad0fe | ||
|
|
8cfc4c56cf | ||
|
|
99e9e42a17 | ||
|
|
13c95ba131 | ||
|
|
600a8c7804 | ||
|
|
64fb52fc5e | ||
|
|
92b6e7230d | ||
|
|
cc8bc781c1 | ||
|
|
edbe463d73 | ||
|
|
8ace8c01cf | ||
|
|
8f37252676 | ||
|
|
c0c59968bf | ||
|
|
9f5a909be3 | ||
|
|
90975bdadc | ||
|
|
7d1fad9eb7 | ||
|
|
983c79ad3b | ||
|
|
96e73fcb63 | ||
|
|
70a3736073 | ||
|
|
1e8e8ba65c | ||
|
|
359a1f2c8e | ||
|
|
2e4f8cbfc7 | ||
|
|
482aee0d9d | ||
|
|
0ae3374e81 | ||
|
|
ddc088859e | ||
|
|
5e3da2d687 |
No files matched your search
+43
-2
@@ -5,6 +5,7 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# v5
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
@@ -14,29 +15,69 @@ updates:
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "pip"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: master
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github_actions"
|
||||
|
||||
- package-ecosystem: "pip"
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: master
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "npm"
|
||||
|
||||
# v4.6
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v4.6
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "pip"
|
||||
- "v4"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v4.6
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github_actions"
|
||||
- "v4"
|
||||
|
||||
# v3
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v3
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "pip"
|
||||
- "v3"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v3
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github_actions"
|
||||
- "v3"
|
||||
@@ -79,3 +79,10 @@ output/csv:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "prowler/lib/outputs/csv/**"
|
||||
- any-glob-to-any-file: "tests/lib/outputs/csv/**"
|
||||
|
||||
compliance:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "prowler/lib/outputs/compliance/**"
|
||||
- any-glob-to-any-file: "tests/lib/outputs/compliance/**"
|
||||
- any-glob-to-any-file: "prowler/compliance/**"
|
||||
|
||||
@@ -3,7 +3,11 @@ name: build-lint-push-containers
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
# For `v3-latest`
|
||||
- "v3"
|
||||
# For `v4-latest`
|
||||
- "v4.6"
|
||||
# For `latest`
|
||||
- "master"
|
||||
paths-ignore:
|
||||
- ".github/**"
|
||||
@@ -58,7 +62,7 @@ jobs:
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
pipx install poetry
|
||||
pipx install poetry==1.8.5
|
||||
pipx inject poetry poetry-bumpversion
|
||||
|
||||
- name: Get Prowler version
|
||||
@@ -80,8 +84,8 @@ jobs:
|
||||
;;
|
||||
|
||||
4)
|
||||
echo "LATEST_TAG=latest" >> "${GITHUB_ENV}"
|
||||
echo "STABLE_TAG=stable" >> "${GITHUB_ENV}"
|
||||
echo "LATEST_TAG=v4-latest" >> "${GITHUB_ENV}"
|
||||
echo "STABLE_TAG=v4-stable" >> "${GITHUB_ENV}"
|
||||
;;
|
||||
|
||||
*)
|
||||
@@ -153,7 +157,7 @@ jobs:
|
||||
run: |
|
||||
curl https://api.github.com/repos/${{ secrets.DISPATCH_OWNER }}/${{ secrets.DISPATCH_REPO }}/dispatches \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.ACCESS_TOKEN }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.PROWLER_BOT_ACCESS_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
--data '{"event_type":"dispatch","client_payload":{"version":"v3-latest", "tag": "${{ env.LATEST_COMMIT_HASH }}"}}'
|
||||
|
||||
@@ -162,6 +166,6 @@ jobs:
|
||||
run: |
|
||||
curl https://api.github.com/repos/${{ secrets.DISPATCH_OWNER }}/${{ secrets.DISPATCH_REPO }}/dispatches \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.ACCESS_TOKEN }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.PROWLER_BOT_ACCESS_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
--data '{"event_type":"dispatch","client_payload":{"version":"release", "tag":"${{ needs.container-build-push.outputs.prowler_version }}"}}'
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: TruffleHog OSS
|
||||
uses: trufflesecurity/trufflehog@v3.81.10
|
||||
uses: trufflesecurity/trufflehog@v3.88.18
|
||||
with:
|
||||
path: ./
|
||||
base: ${{ github.event.repository.default_branch }}
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test if changes are in not ignored paths
|
||||
id: are-non-ignored-files-changed
|
||||
uses: tj-actions/changed-files@v45
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: ./**
|
||||
files_ignore: |
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pipx install poetry
|
||||
pipx install poetry==1.8.5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
@@ -91,6 +91,6 @@ jobs:
|
||||
poetry run pytest -n auto --cov=./prowler --cov-report=xml tests
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pipx install poetry
|
||||
pipx install poetry==1.8.5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
|
||||
@@ -85,7 +85,7 @@ repos:
|
||||
# For running trufflehog in docker, use the following entry instead:
|
||||
# entry: bash -c 'docker run -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --only-verified --fail'
|
||||
language: system
|
||||
stages: ["commit", "push"]
|
||||
stages: ["pre-commit", "pre-push"]
|
||||
|
||||
- id: bandit
|
||||
name: bandit
|
||||
|
||||
+7
-8
@@ -1,30 +1,29 @@
|
||||
FROM python:3.12-alpine
|
||||
FROM python:3.12.9-alpine3.20
|
||||
|
||||
LABEL maintainer="https://github.com/prowler-cloud/prowler"
|
||||
|
||||
# Update system dependencies
|
||||
# Update system dependencies and install essential tools
|
||||
#hadolint ignore=DL3018
|
||||
RUN apk --no-cache upgrade && apk --no-cache add curl
|
||||
RUN apk --no-cache upgrade && apk --no-cache add curl git
|
||||
|
||||
# Create nonroot user
|
||||
# Create non-root user
|
||||
RUN mkdir -p /home/prowler && \
|
||||
echo 'prowler:x:1000:1000:prowler:/home/prowler:' > /etc/passwd && \
|
||||
echo 'prowler:x:1000:' > /etc/group && \
|
||||
chown -R prowler:prowler /home/prowler
|
||||
USER prowler
|
||||
|
||||
# Copy necessary files
|
||||
# Copy necessary files
|
||||
WORKDIR /home/prowler
|
||||
COPY prowler/ /home/prowler/prowler/
|
||||
COPY dashboard/ /home/prowler/dashboard/
|
||||
COPY pyproject.toml /home/prowler
|
||||
COPY README.md /home/prowler
|
||||
|
||||
# Install dependencies
|
||||
# Install Python dependencies
|
||||
ENV HOME='/home/prowler'
|
||||
ENV PATH="$HOME/.local/bin:$PATH"
|
||||
#hadolint ignore=DL3013
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
|
||||
pip install --no-cache-dir .
|
||||
|
||||
# Remove deprecated dash dependencies
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog"><img width="30" height="30" alt="Prowler community on Slack" src="https://github.com/prowler-cloud/prowler/assets/38561120/3c8b4ec5-6849-41a5-b5e1-52bbb94af73a"></a>
|
||||
<a href="https://goto.prowler.com/slack"><img width="30" height="30" alt="Prowler community on Slack" src="https://github.com/prowler-cloud/prowler/assets/38561120/3c8b4ec5-6849-41a5-b5e1-52bbb94af73a"></a>
|
||||
<br>
|
||||
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-2oinmgmw6-cl7gOrljSEqo_aoripVPFA">Join our Prowler community!</a>
|
||||
<a href="https://goto.prowler.com/slack">Join our Prowler community!</a>
|
||||
</p>
|
||||
<hr>
|
||||
<p align="center">
|
||||
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog"><img alt="Slack Shield" src="https://img.shields.io/badge/slack-prowler-brightgreen.svg?logo=slack"></a>
|
||||
<a href="https://goto.prowler.com/slack"><img alt="Slack Shield" src="https://img.shields.io/badge/slack-prowler-brightgreen.svg?logo=slack"></a>
|
||||
<a href="https://pypi.org/project/prowler/"><img alt="Python Version" src="https://img.shields.io/pypi/v/prowler.svg"></a>
|
||||
<a href="https://pypi.python.org/pypi/prowler/"><img alt="Python Version" src="https://img.shields.io/pypi/pyversions/prowler.svg"></a>
|
||||
<a href="https://pypistats.org/packages/prowler"><img alt="PyPI Prowler Downloads" src="https://img.shields.io/pypi/dw/prowler.svg?label=prowler%20downloads"></a>
|
||||
@@ -63,9 +63,9 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe
|
||||
|
||||
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) |
|
||||
|---|---|---|---|---|
|
||||
| AWS | 415 | 67 -> `prowler aws --list-services` | 28 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` |
|
||||
| GCP | 77 | 13 -> `prowler gcp --list-services` | 1 -> `prowler gcp --list-compliance` | 2 -> `prowler gcp --list-categories`|
|
||||
| Azure | 135 | 16 -> `prowler azure --list-services` | 2 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` |
|
||||
| AWS | 553 | 77 -> `prowler aws --list-services` | 30 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` |
|
||||
| GCP | 77 | 13 -> `prowler gcp --list-services` | 3 -> `prowler gcp --list-compliance` | 2 -> `prowler gcp --list-categories`|
|
||||
| Azure | 138 | 17 -> `prowler azure --list-services` | 4 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` |
|
||||
| Kubernetes | 83 | 7 -> `prowler kubernetes --list-services` | 1 -> `prowler kubernetes --list-compliance` | 7 -> `prowler kubernetes --list-categories` |
|
||||
|
||||
# 💻 Installation
|
||||
|
||||
@@ -12,7 +12,11 @@ Originally based on [org-multi-account](https://github.com/prowler-cloud/prowler
|
||||
|
||||
## Architecture Explanation
|
||||
|
||||
The solution is designed to be very simple. Prowler is run via an ECS Task definition that launches a single Fargate container. This Task Definition is executed on a schedule using an EventBridge Rule.
|
||||
The solution is designed to be very simple. Prowler is run via an ECS Task definition that launches a single Fargate container. This Task Definition is executed on a schedule using an EventBridge Rule.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This solution assumes that you have a VPC architecture with two redundant subnets that can reach the AWS API endpoints (e.g. PrivateLink, NAT Gateway, etc.).
|
||||
|
||||
## CloudFormation Templates
|
||||
|
||||
@@ -59,9 +63,9 @@ The logs that are generated and sent to Cloudwatch are error logs, and assessmen
|
||||
|
||||
## Instructions
|
||||
1. Create a Private ECR Repository in the account that will host the Prowler container. The Audit account is recommended, but any account can be used.
|
||||
2. Configure the .awsvariables file. Note the ROLE name chosen as it will be the CrossAccountRole.
|
||||
3. Follow the steps from "View Push Commands" to build and upload the container image. You need to have Docker and AWS CLI installed, and use the cli to login to the account first. After upload note the Image URI, as it is required for the CF-Prowler-ECS template.
|
||||
4. Make sure SecurityHub is enabled in every account in AWS Organizations, and that the SecurityHub integration is enabled as explained in [Prowler - Security Hub Integration](https://github.com/prowler-cloud/prowler#security-hub-integration)
|
||||
2. Configure the .awsvariables file. Note the ROLE name chosen as it will be the CrossAccountRole.
|
||||
3. Follow the steps from "View Push Commands" to build and upload the container image. Substitute step 2 with the build command provided in the Dockerfile. You need to have Docker and AWS CLI installed, and use the cli to login to the account first. After upload note the Image URI, as it is required for the CF-Prowler-ECS template. Ensure that you pay attention to the architecture while performing the docker build command. A common mistake is not specifying the architecture and then building on Apple silicon. Your task will fail with *exec /home/prowler/.local/bin/prowler: exec format error*.
|
||||
4. Make sure SecurityHub is enabled in every account in AWS Organizations, and that the SecurityHub integration is enabled as explained in [Prowler - Security Hub Integration](https://github.com/prowler-cloud/prowler#security-hub-integration)
|
||||
5. Deploy **CF-Prowler-CrossAccountRole.yml** in the Master Account as a single stack. You will have to choose the CrossAccountRole name (ProwlerXA-Role by default) and the ProwlerTaskRoleName (ProwlerECSTask-Role by default)
|
||||
6. Deploy **CF-Prowler-CrossAccountRole.yml** in every Member Account as a StackSet. Choose the same CrossAccountName and ProwlerTaskRoleName as the previous step.
|
||||
7. Deploy **CF-Prowler-IAM.yml** in the account that will host the Prowler container (the same from step 1). The following template parameters must be provided:
|
||||
@@ -91,4 +95,4 @@ If you permission find errors in the CloudWatch logs, the culprit might be a [Se
|
||||
## Upgrading Prowler
|
||||
|
||||
Prowler version is controlled by the PROWLERVER argument in the Dockerfile, change it to the desired version and follow the ECR Push Commands to update the container image.
|
||||
Old images can be deleted from the ECR Repository after the new image is confirmed to work. They will show as "untagged" as only one image can hold the "latest" tag.
|
||||
Old images can be deleted from the ECR Repository after the new image is confirmed to work. They will show as "untagged" as only one image can hold the "latest" tag.
|
||||
@@ -68,7 +68,7 @@ for accountId in ${ACCOUNTS_IN_ORGS}; do
|
||||
# Run Prowler
|
||||
echo -e "Assessing AWS Account: ${accountId}, using Role: ${ROLE} on $(date)"
|
||||
# Pipe stdout to /dev/null to reduce unnecessary Cloudwatch logs
|
||||
prowler aws -R arn:"${PARTITION}":iam::"${accountId}":role/"${ROLE}" -q -S -f "${REGION}" > /dev/null
|
||||
prowler aws -R arn:"${PARTITION}":iam::"${accountId}":role/"${ROLE}" --security-hub --send-sh-only-fails -f "${REGION}" > /dev/null
|
||||
TOTAL_SEC=$((SECONDS - START_TIME))
|
||||
printf "Completed AWS Account: ${accountId} in %02dh:%02dm:%02ds" $((TOTAL_SEC / 3600)) $((TOTAL_SEC % 3600 / 60)) $((TOTAL_SEC % 60))
|
||||
echo ""
|
||||
|
||||
@@ -60,24 +60,42 @@ Resources:
|
||||
Effect: Allow
|
||||
Resource: "*"
|
||||
Action:
|
||||
- ds:ListAuthorizedApplications
|
||||
- account:Get*
|
||||
- appstream:Describe*
|
||||
- appstream:List*
|
||||
- backup:List*
|
||||
- cloudtrail:GetInsightSelectors
|
||||
- codeartifact:List*
|
||||
- codebuild:BatchGet*
|
||||
- cognito-idp:GetUserPoolMfaConfig
|
||||
- dlm:Get*
|
||||
- drs:Describe*
|
||||
- ds:Describe*
|
||||
- ds:Get*
|
||||
- ds:List*
|
||||
- dynamodb:GetResourcePolicy
|
||||
- ec2:GetEbsEncryptionByDefault
|
||||
- ec2:GetSnapshotBlockPublicAccessState
|
||||
- ec2:GetInstanceMetadataDefaults
|
||||
- ecr:Describe*
|
||||
- ecr:GetRegistryScanningConfiguration
|
||||
- elasticfilesystem:DescribeBackupPolicy
|
||||
- glue:GetConnections
|
||||
- glue:GetSecurityConfiguration
|
||||
- glue:GetSecurityConfiguration*
|
||||
- glue:SearchTables
|
||||
- lambda:GetFunction
|
||||
- lambda:GetFunction*
|
||||
- logs:FilterLogEvents
|
||||
- lightsail:GetRelationalDatabases
|
||||
- macie2:GetMacieSession
|
||||
- s3:GetAccountPublicAccessBlock
|
||||
- shield:DescribeProtection
|
||||
- shield:GetSubscriptionState
|
||||
- ssm:GetDocument
|
||||
- ssm-incidents:List*
|
||||
- support:Describe*
|
||||
- tag:GetTagKeys
|
||||
- PolicyName: Prowler-Security-Hub
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- wellarchitected:List*
|
||||
|
||||
- Sid: AllowProwlerSecurityHub
|
||||
Effect: Allow
|
||||
Resource: "*"
|
||||
|
||||
@@ -62,7 +62,7 @@ Resources:
|
||||
awslogs-stream-prefix: ecs
|
||||
Cpu: 1024
|
||||
ExecutionRoleArn: !Ref ECSExecutionRole
|
||||
Memory: 2048
|
||||
Memory: 8192
|
||||
NetworkMode: awsvpc
|
||||
TaskRoleArn: !Ref ProwlerTaskRole
|
||||
Family: SecurityHubProwlerTask
|
||||
|
||||
@@ -97,9 +97,15 @@ Outputs:
|
||||
ECSExecutionRoleARN:
|
||||
Description: ARN of the ECS Task Execution Role
|
||||
Value: !GetAtt ECSExecutionRole.Arn
|
||||
Export:
|
||||
Name: ECSExecutionRoleArn
|
||||
ProwlerTaskRoleARN:
|
||||
Description: ARN of the ECS Prowler Task Role
|
||||
Value: !GetAtt ProwlerTaskRole.Arn
|
||||
Export:
|
||||
Name: ProwlerTaskRoleArn
|
||||
ECSEventRoleARN:
|
||||
Description: ARN of the Eventbridge Task Role
|
||||
Value: !GetAtt ECSEventRole.Arn
|
||||
Export:
|
||||
Name: ECSEventRoleARN
|
||||
@@ -2223,3 +2223,232 @@ def get_section_containers_ens(data, section_1, section_2, section_3, section_4)
|
||||
section_containers.append(section_container)
|
||||
|
||||
return html.Div(section_containers, className="compliance-data-layout")
|
||||
|
||||
|
||||
# This function extracts and compares up to two numeric values, ensuring correct sorting for version-like strings.
|
||||
def extract_numeric_values(value):
|
||||
numbers = re.findall(r"\d+", str(value))
|
||||
if len(numbers) >= 2:
|
||||
return int(numbers[0]), int(numbers[1])
|
||||
elif len(numbers) == 1:
|
||||
return int(numbers[0]), 0
|
||||
return 0, 0
|
||||
|
||||
|
||||
def get_section_containers_kisa_ismsp(data, section_1, section_2):
|
||||
data["STATUS"] = data["STATUS"].apply(map_status_to_icon)
|
||||
data[section_1] = data[section_1].astype(str)
|
||||
data[section_2] = data[section_2].astype(str)
|
||||
data.sort_values(
|
||||
by=section_1,
|
||||
key=lambda x: x.map(extract_numeric_values),
|
||||
ascending=True,
|
||||
inplace=True,
|
||||
)
|
||||
|
||||
findings_counts_section = (
|
||||
data.groupby([section_2, "STATUS"]).size().unstack(fill_value=0)
|
||||
)
|
||||
findings_counts_name = (
|
||||
data.groupby([section_1, "STATUS"]).size().unstack(fill_value=0)
|
||||
)
|
||||
|
||||
section_containers = []
|
||||
|
||||
for name in data[section_1].unique():
|
||||
success_name = (
|
||||
findings_counts_name.loc[name, pass_emoji]
|
||||
if pass_emoji in findings_counts_name.columns
|
||||
else 0
|
||||
)
|
||||
failed_name = (
|
||||
findings_counts_name.loc[name, fail_emoji]
|
||||
if fail_emoji in findings_counts_name.columns
|
||||
else 0
|
||||
)
|
||||
|
||||
fig_name = go.Figure(
|
||||
data=[
|
||||
go.Bar(
|
||||
name="Failed",
|
||||
x=[failed_name],
|
||||
y=[""],
|
||||
orientation="h",
|
||||
marker=dict(color="#e77676"),
|
||||
width=[0.8],
|
||||
),
|
||||
go.Bar(
|
||||
name="Success",
|
||||
x=[success_name],
|
||||
y=[""],
|
||||
orientation="h",
|
||||
marker=dict(color="#45cc6e"),
|
||||
width=[0.8],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
fig_name.update_layout(
|
||||
barmode="stack",
|
||||
margin=dict(l=10, r=10, t=10, b=10),
|
||||
paper_bgcolor="rgba(0,0,0,0)",
|
||||
plot_bgcolor="rgba(0,0,0,0)",
|
||||
showlegend=False,
|
||||
width=350,
|
||||
height=30,
|
||||
xaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||
yaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||
annotations=[
|
||||
dict(
|
||||
x=success_name + failed_name,
|
||||
y=0,
|
||||
xref="x",
|
||||
yref="y",
|
||||
text=str(success_name),
|
||||
showarrow=False,
|
||||
font=dict(color="#45cc6e", size=14),
|
||||
xanchor="left",
|
||||
yanchor="middle",
|
||||
),
|
||||
dict(
|
||||
x=0,
|
||||
y=0,
|
||||
xref="x",
|
||||
yref="y",
|
||||
text=str(failed_name),
|
||||
showarrow=False,
|
||||
font=dict(color="#e77676", size=14),
|
||||
xanchor="right",
|
||||
yanchor="middle",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
graph_name = dcc.Graph(
|
||||
figure=fig_name, config={"staticPlot": True}, className="info-bar"
|
||||
)
|
||||
|
||||
graph_div = html.Div(graph_name, className="graph-section")
|
||||
|
||||
direct_internal_items = []
|
||||
|
||||
for section in data[data[section_1] == name][section_2].unique():
|
||||
specific_data = data[
|
||||
(data[section_1] == name) & (data[section_2] == section)
|
||||
]
|
||||
success_section = (
|
||||
findings_counts_section.loc[section, pass_emoji]
|
||||
if pass_emoji in findings_counts_section.columns
|
||||
else 0
|
||||
)
|
||||
failed_section = (
|
||||
findings_counts_section.loc[section, fail_emoji]
|
||||
if fail_emoji in findings_counts_section.columns
|
||||
else 0
|
||||
)
|
||||
|
||||
data_table = dash_table.DataTable(
|
||||
data=specific_data.to_dict("records"),
|
||||
columns=[
|
||||
{"name": i, "id": i}
|
||||
for i in ["CHECKID", "STATUS", "REGION", "ACCOUNTID", "RESOURCEID"]
|
||||
],
|
||||
style_table={"overflowX": "auto"},
|
||||
style_as_list_view=True,
|
||||
style_cell={"textAlign": "left", "padding": "5px"},
|
||||
)
|
||||
|
||||
fig_section = go.Figure(
|
||||
data=[
|
||||
go.Bar(
|
||||
name="Failed",
|
||||
x=[failed_section],
|
||||
y=[""],
|
||||
orientation="h",
|
||||
marker=dict(color="#e77676"),
|
||||
),
|
||||
go.Bar(
|
||||
name="Success",
|
||||
x=[success_section],
|
||||
y=[""],
|
||||
orientation="h",
|
||||
marker=dict(color="#45cc6e"),
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
fig_section.update_layout(
|
||||
barmode="stack",
|
||||
margin=dict(l=10, r=10, t=10, b=10),
|
||||
paper_bgcolor="rgba(0,0,0,0)",
|
||||
plot_bgcolor="rgba(0,0,0,0)",
|
||||
showlegend=False,
|
||||
width=350,
|
||||
height=30,
|
||||
xaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||
yaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||
annotations=[
|
||||
dict(
|
||||
x=success_section + failed_section,
|
||||
y=0,
|
||||
xref="x",
|
||||
yref="y",
|
||||
text=str(success_section),
|
||||
showarrow=False,
|
||||
font=dict(color="#45cc6e", size=14),
|
||||
xanchor="left",
|
||||
yanchor="middle",
|
||||
),
|
||||
dict(
|
||||
x=0,
|
||||
y=0,
|
||||
xref="x",
|
||||
yref="y",
|
||||
text=str(failed_section),
|
||||
showarrow=False,
|
||||
font=dict(color="#e77676", size=14),
|
||||
xanchor="right",
|
||||
yanchor="middle",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
graph_section = dcc.Graph(
|
||||
figure=fig_section,
|
||||
config={"staticPlot": True},
|
||||
className="info-bar-child",
|
||||
)
|
||||
|
||||
graph_div_section = html.Div(graph_section, className="graph-section-req")
|
||||
|
||||
internal_accordion_item = dbc.AccordionItem(
|
||||
title=section,
|
||||
children=[html.Div([data_table], className="inner-accordion-content")],
|
||||
)
|
||||
|
||||
internal_section_container = html.Div(
|
||||
[
|
||||
graph_div_section,
|
||||
dbc.Accordion(
|
||||
[internal_accordion_item], start_collapsed=True, flush=True
|
||||
),
|
||||
],
|
||||
className="accordion-inner--child",
|
||||
)
|
||||
|
||||
direct_internal_items.append(internal_section_container)
|
||||
|
||||
accordion_item = dbc.AccordionItem(
|
||||
title=f"{name}", children=direct_internal_items
|
||||
)
|
||||
section_container = html.Div(
|
||||
[
|
||||
graph_div,
|
||||
dbc.Accordion([accordion_item], start_collapsed=True, flush=True),
|
||||
],
|
||||
className="accordion-inner",
|
||||
)
|
||||
|
||||
section_containers.append(section_container)
|
||||
|
||||
return html.Div(section_containers, className="compliance-data-layout")
|
||||
@@ -0,0 +1,36 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_ens
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
# append the requirements_description to idgrupocontrol
|
||||
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"] = (
|
||||
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"]
|
||||
+ " - "
|
||||
+ data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ATTRIBUTES_MARCO",
|
||||
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
|
||||
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
|
||||
"REQUIREMENTS_ATTRIBUTES_TIPO",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
]
|
||||
|
||||
return get_section_containers_ens(
|
||||
aux,
|
||||
"REQUIREMENTS_ATTRIBUTES_MARCO",
|
||||
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
|
||||
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
|
||||
"REQUIREMENTS_ATTRIBUTES_TIPO",
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_ens
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
# append the requirements_description to idgrupocontrol
|
||||
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"] = (
|
||||
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"]
|
||||
+ " - "
|
||||
+ data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ATTRIBUTES_MARCO",
|
||||
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
|
||||
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
|
||||
"REQUIREMENTS_ATTRIBUTES_TIPO",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
]
|
||||
|
||||
return get_section_containers_ens(
|
||||
aux,
|
||||
"REQUIREMENTS_ATTRIBUTES_MARCO",
|
||||
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
|
||||
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
|
||||
"REQUIREMENTS_ATTRIBUTES_TIPO",
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SUBDOMAIN",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
# "REQUIREMENTS_DESCRIPTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SUBDOMAIN", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SUBDOMAIN",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
# "REQUIREMENTS_DESCRIPTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SUBDOMAIN", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
||||
)
|
||||
@@ -148,6 +148,7 @@ else:
|
||||
select_account_dropdown_list = ["All"]
|
||||
# Append to the list the unique values of the columns ACCOUNTID, PROJECTID and SUBSCRIPTIONID if they exist
|
||||
if "ACCOUNTID" in data.columns:
|
||||
data["ACCOUNTID"] = data["ACCOUNTID"].astype(str)
|
||||
select_account_dropdown_list = select_account_dropdown_list + list(
|
||||
data["ACCOUNTID"].unique()
|
||||
)
|
||||
@@ -246,9 +247,11 @@ def display_data(
|
||||
dfs = []
|
||||
for file in files:
|
||||
df = pd.read_csv(
|
||||
file, sep=";", on_bad_lines="skip", encoding=encoding_format
|
||||
file, sep=";", on_bad_lines="skip", encoding=encoding_format, dtype=str
|
||||
)
|
||||
dfs.append(df.astype(str))
|
||||
df = df.astype(str).fillna("nan")
|
||||
df.columns = df.columns.astype(str)
|
||||
dfs.append(df)
|
||||
return pd.concat(dfs, ignore_index=True)
|
||||
|
||||
data = load_csv_files(files)
|
||||
@@ -274,17 +277,24 @@ def display_data(
|
||||
data.rename(columns={"PROJECTID": "ACCOUNTID"}, inplace=True)
|
||||
data["REGION"] = "-"
|
||||
# Rename the column SUBSCRIPTIONID to ACCOUNTID for Azure
|
||||
if data.columns.str.contains("SUBSCRIPTIONID").any():
|
||||
if (
|
||||
data.columns.str.contains("SUBSCRIPTIONID").any()
|
||||
and not data.columns.str.contains("ACCOUNTID").any()
|
||||
):
|
||||
data.rename(columns={"SUBSCRIPTIONID": "ACCOUNTID"}, inplace=True)
|
||||
data["REGION"] = "-"
|
||||
# Handle v3 azure cis compliance
|
||||
if data.columns.str.contains("SUBSCRIPTION").any():
|
||||
if (
|
||||
data.columns.str.contains("SUBSCRIPTION").any()
|
||||
and not data.columns.str.contains("ACCOUNTID").any()
|
||||
):
|
||||
data.rename(columns={"SUBSCRIPTION": "ACCOUNTID"}, inplace=True)
|
||||
data["REGION"] = "-"
|
||||
|
||||
# Filter ACCOUNT
|
||||
if account_filter == ["All"]:
|
||||
updated_cloud_account_values = data["ACCOUNTID"].unique()
|
||||
|
||||
elif "All" in account_filter and len(account_filter) > 1:
|
||||
# Remove 'All' from the list
|
||||
account_filter.remove("All")
|
||||
@@ -299,9 +309,11 @@ def display_data(
|
||||
|
||||
account_filter_options = list(data["ACCOUNTID"].unique())
|
||||
account_filter_options = account_filter_options + ["All"]
|
||||
for item in account_filter_options:
|
||||
if "nan" in item or item.__class__.__name__ != "str" or item is None:
|
||||
account_filter_options.remove(item)
|
||||
account_filter_options = [
|
||||
item
|
||||
for item in account_filter_options
|
||||
if isinstance(item, str) and item.lower() != "nan"
|
||||
]
|
||||
|
||||
# Filter REGION
|
||||
if region_filter_analytics == ["All"]:
|
||||
@@ -520,8 +532,8 @@ def get_bar_graph(df, column_name):
|
||||
|
||||
# Cut the text if it is too long
|
||||
for i in range(len(colums)):
|
||||
if len(colums[i]) > 15:
|
||||
colums[i] = colums[i][:15] + "..."
|
||||
if len(colums[i]) > 43:
|
||||
colums[i] = colums[i][:43] + "..."
|
||||
|
||||
fig = px.bar(
|
||||
df,
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
For technical support or any type of inquiries, you are very welcome to:
|
||||
|
||||
- Reach out to community members on the [**Prowler Slack channel**](https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog)
|
||||
- Reach out to community members on the [**Prowler Slack channel**](https://goto.prowler.com/slack)
|
||||
|
||||
- Open an Issue or a Pull Request in our [**GitHub repository**](https://github.com/prowler-cloud/prowler).
|
||||
|
||||
|
||||
@@ -160,14 +160,20 @@ else:
|
||||
All the checks MUST fill the `report.resource_id` and `report.resource_arn` with the following criteria:
|
||||
|
||||
- AWS
|
||||
- Resource ID -- `report.resource_id`
|
||||
- AWS Account --> Account Number `123456789012`
|
||||
- AWS Resource --> Resource ID / Name
|
||||
- Root resource --> `<root_account>`
|
||||
- Resource ARN -- `report.resource_arn`
|
||||
- AWS Account --> Root ARN `arn:aws:iam::123456789012:root`
|
||||
- AWS Resource --> Resource ARN
|
||||
- Root resource --> Resource Type ARN `f"arn:{service_client.audited_partition}:<service_name>:{service_client.region}:{service_client.audited_account}:<resource_type>"`
|
||||
- Resouce ID and resource ARN:
|
||||
- If the resource audited is the AWS account:
|
||||
- `resource_id` -> AWS Account Number
|
||||
- `resource_arn` -> AWS Account Root ARN
|
||||
- If we can’t get the ARN from the resource audited, we create a valid ARN with the `resource_id` part as the resource audited. Examples:
|
||||
- Bedrock -> `arn:<partition>:bedrock:<region>:<account-id>:model-invocation-logging`
|
||||
- DirectConnect -> `arn:<partition>:directconnect:<region>:<account-id>:dxcon`
|
||||
- If there is no real resource to audit we do the following:
|
||||
- resource_id -> `resource_type/unknown`
|
||||
- resource_arn -> `arn:<partition>:<service>:<region>:<account-id>:<resource_type>/unknown`
|
||||
- Examples:
|
||||
- AWS Security Hub -> `arn:<partition>:security-hub:<region>:<account-id>:hub/unknown`
|
||||
- Access Analyzer -> `arn:<partition>:access-analyzer:<region>:<account-id>:analyzer/unknown`
|
||||
- GuardDuty -> `arn:<partition>:guardduty:<region>:<account-id>:detector/unknown`
|
||||
- GCP
|
||||
- Resource ID -- `report.resource_id`
|
||||
- GCP Resource --> Resource ID
|
||||
@@ -272,7 +278,7 @@ Each Prowler check has metadata associated which is stored at the same level of
|
||||
# Severity holds the check's severity, always in lowercase (critical, high, medium, low or informational)
|
||||
"Severity": "critical",
|
||||
# ResourceType only for AWS, holds the type from here
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
|
||||
# https://docs.aws.amazon.com/securityhub/latest/userguide/asff-resources.html
|
||||
"ResourceType": "Other",
|
||||
# Description holds the title of the check, for now is the same as CheckTitle
|
||||
"Description": "Ensure there are no EC2 AMIs set as Public.",
|
||||
|
||||
@@ -14,10 +14,8 @@ Once that is satisfied go ahead and clone your forked repo:
|
||||
git clone https://github.com/<your-github-user>/prowler
|
||||
cd prowler
|
||||
```
|
||||
For isolation and avoid conflicts with other environments, we recommend usage of `poetry`:
|
||||
```
|
||||
pip install poetry
|
||||
```
|
||||
For isolation and to avoid conflicts with other environments, we recommend using `poetry`, a Python dependency management tool. You can install it by following the instructions [here](https://python-poetry.org/docs/#installation).
|
||||
|
||||
Then install all dependencies including the ones for developers:
|
||||
```
|
||||
poetry install --with dev
|
||||
@@ -69,4 +67,4 @@ If you create or review a PR in https://github.com/prowler-cloud/prowler please
|
||||
|
||||
## Want some swag as appreciation for your contribution?
|
||||
|
||||
If you are like us and you love swag, we are happy to thank you for your contribution with some laptop stickers or whatever other swag we may have at that time. Please, tell us more details and your pull request link in our [Slack workspace here](https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog). You can also reach out to Toni de la Fuente on Twitter [here](https://twitter.com/ToniBlyx), his DMs are open.
|
||||
If you are like us and you love swag, we are happy to thank you for your contribution with some laptop stickers or whatever other swag we may have at that time. Please, tell us more details and your pull request link in our [Slack workspace here](https://goto.prowler.com/slack). You can also reach out to Toni de la Fuente on Twitter [here](https://twitter.com/ToniBlyx), his DMs are open.
|
||||
@@ -44,7 +44,6 @@ class Provider(ABC):
|
||||
Methods:
|
||||
print_credentials(): Displays the provider's credentials used for auditing in the command-line interface.
|
||||
setup_session(): Sets up the session for the provider.
|
||||
get_output_mapping(): Returns the output mapping between the provider and the generic model.
|
||||
validate_arguments(): Validates the arguments for the provider.
|
||||
get_checks_to_execute_by_audit_resources(): Returns a set of checks based on the input resources to scan.
|
||||
|
||||
@@ -131,15 +130,6 @@ class Provider(ABC):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def get_output_mapping(self) -> dict:
|
||||
"""
|
||||
get_output_mapping returns the output mapping between the provider and the generic model.
|
||||
|
||||
This method needs to be created in each provider.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def validate_arguments(self) -> None:
|
||||
"""
|
||||
validate_arguments validates the arguments for the provider.
|
||||
@@ -200,18 +190,18 @@ from prowler.providers.common.models import Audit_Metadata
|
||||
from prowler.providers.common.provider import Provider
|
||||
from prowler.providers.<new_provider_name>.models import (
|
||||
# All providers models needed
|
||||
ProvierSessionModel,
|
||||
ProvierIdentityModel,
|
||||
ProvierOutputOptionsModel
|
||||
ProviderSessionModel,
|
||||
ProviderIdentityModel,
|
||||
ProviderOutputOptionsModel
|
||||
)
|
||||
|
||||
class NewProvider(Provider):
|
||||
# All properties from the class, some of this are properties in the base class
|
||||
_type: str = "<provider_name>"
|
||||
_session: <ProvierSessionModel>
|
||||
_identity: <ProvierIdentityModel>
|
||||
_session: <ProviderSessionModel>
|
||||
_identity: <ProviderIdentityModel>
|
||||
_audit_config: dict
|
||||
_output_options: ProvierOutputOptionsModel
|
||||
_output_options: ProviderOutputOptionsModel
|
||||
_mutelist: dict
|
||||
audit_metadata: Audit_Metadata
|
||||
|
||||
@@ -222,13 +212,13 @@ class NewProvider(Provider):
|
||||
arguments (dict): A dictionary containing configuration arguments.
|
||||
"""
|
||||
logger.info("Setting <NewProviderName> provider ...")
|
||||
# First get from arguments the necesary from the cloud acount (subscriptions or projects or whatever the provider use for storing services)
|
||||
# First get from arguments the necessary from the cloud account (subscriptions or projects or whatever the provider use for storing services)
|
||||
|
||||
# Set the session with the method enforced by parent class
|
||||
self._session = self.setup_session(credentials_file)
|
||||
|
||||
# Set the Identity class normaly the provider class give by Python provider library
|
||||
self._identity = <ProvierIdentityModel>()
|
||||
self._identity = <ProviderIdentityModel>()
|
||||
|
||||
# Set the provider configuration
|
||||
self._audit_config = load_and_validate_config_file(
|
||||
@@ -264,7 +254,7 @@ class NewProvider(Provider):
|
||||
<all_needed_for_auth> Can include all necessary arguments to setup the session
|
||||
|
||||
Returns:
|
||||
Credentials necesary to communicate with the provider.
|
||||
Credentials necessary to communicate with the provider.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
+72
-37
@@ -19,14 +19,40 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe
|
||||
## Quick Start
|
||||
### Installation
|
||||
|
||||
Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/), thus can be installed using pip with `Python >= 3.9`:
|
||||
Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/), thus can be installed as Python package with `Python >= 3.9`:
|
||||
|
||||
=== "Generic"
|
||||
=== "pipx"
|
||||
|
||||
[pipx](https://pipx.pypa.io/stable/) is a tool to install Python applications in isolated environments. It is recommended to use `pipx` for a global installation.
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* `Python >= 3.9`
|
||||
* `Python pip >= 3.9`
|
||||
* `pipx` installed: [pipx installation](https://pipx.pypa.io/stable/installation/).
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
|
||||
_Commands_:
|
||||
|
||||
``` bash
|
||||
pipx install prowler
|
||||
prowler -v
|
||||
```
|
||||
|
||||
To upgrade Prowler to the latest version, run:
|
||||
|
||||
``` bash
|
||||
pipx upgrade prowler
|
||||
```
|
||||
|
||||
=== "pip"
|
||||
|
||||
???+ warning
|
||||
This method is not recommended because it will modify the environment which you choose to install. Consider using [pipx](https://docs.prowler.com/projects/prowler-open-source/en/latest/#__tabbed_1_1) for a global installation.
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* `Python >= 3.9`
|
||||
* `Python pip >= 21.0.0`
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
|
||||
_Commands_:
|
||||
@@ -36,13 +62,19 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
prowler -v
|
||||
```
|
||||
|
||||
To upgrade Prowler to the latest version, run:
|
||||
|
||||
``` bash
|
||||
pip install --upgrade prowler
|
||||
```
|
||||
|
||||
=== "Docker"
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* Have `docker` installed: https://docs.docker.com/get-docker/.
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
* In the command below, change `-v` to your local directory path in order to access the reports.
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
|
||||
_Commands_:
|
||||
|
||||
@@ -54,41 +86,21 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
--env AWS_SESSION_TOKEN toniblyx/prowler:latest
|
||||
```
|
||||
|
||||
=== "Ubuntu"
|
||||
|
||||
_Requirements for Ubuntu 20.04.3 LTS_:
|
||||
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
* Install python 3.9 with: `sudo apt-get install python3.9`
|
||||
* Remove python 3.8 to avoid conflicts if you can: `sudo apt-get remove python3.8`
|
||||
* Make sure you have the python3 distutils package installed: `sudo apt-get install python3-distutils`
|
||||
* To make sure you use pip for 3.9 get the get-pip script with: `curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py`
|
||||
* Execute it with the proper python version: `sudo python3.9 get-pip.py`
|
||||
* Now you should have pip for 3.9 ready: `pip3.9 --version`
|
||||
|
||||
_Commands_:
|
||||
|
||||
```
|
||||
pip3.9 install prowler
|
||||
export PATH=$PATH:/home/$HOME/.local/bin/
|
||||
prowler -v
|
||||
```
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
_Requirements for Developers_:
|
||||
|
||||
* `git`
|
||||
* `poetry` installed: [poetry installation](https://python-poetry.org/docs/#installation).
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
* `git`, `Python >= 3.9`, `pip` and `poetry` installed (`pip install poetry`)
|
||||
|
||||
_Commands_:
|
||||
|
||||
```
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
cd prowler
|
||||
poetry shell
|
||||
poetry install
|
||||
python prowler.py -v
|
||||
poetry run python prowler.py -v
|
||||
```
|
||||
???+ note
|
||||
If you want to clone Prowler from Windows, use `git config core.longpaths true` to allow long file paths.
|
||||
@@ -97,15 +109,33 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* `Python >= 3.9`
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
* Latest Amazon Linux 2 should come with Python 3.9 already installed however it may need pip. Install Python pip 3.9 with: `sudo yum install -y python3-pip`.
|
||||
* Make sure setuptools for python is already installed with: `pip3 install setuptools`
|
||||
|
||||
_Commands_:
|
||||
|
||||
```
|
||||
pip3.9 install prowler
|
||||
export PATH=$PATH:/home/$HOME/.local/bin/
|
||||
python3 -m pip install --user pipx
|
||||
python3 -m pipx ensurepath
|
||||
pipx install prowler
|
||||
prowler -v
|
||||
```
|
||||
|
||||
=== "Ubuntu"
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* `Ubuntu 23.04` or above, if you are using an older version of Ubuntu check [pipx installation](https://docs.prowler.com/projects/prowler-open-source/en/latest/#__tabbed_1_1) and ensure you have `Python >= 3.9`.
|
||||
* `Python >= 3.9`
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
|
||||
_Commands_:
|
||||
|
||||
``` bash
|
||||
sudo apt update
|
||||
sudo apt install pipx
|
||||
pipx ensurepath
|
||||
pipx install prowler
|
||||
prowler -v
|
||||
```
|
||||
|
||||
@@ -125,7 +155,7 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
|
||||
=== "AWS CloudShell"
|
||||
|
||||
After the migration of AWS CloudShell from Amazon Linux 2 to Amazon Linux 2023 [[1]](https://aws.amazon.com/about-aws/whats-new/2023/12/aws-cloudshell-migrated-al2023/) [2](https://docs.aws.amazon.com/cloudshell/latest/userguide/cloudshell-AL2023-migration.html), there is no longer a need to manually compile Python 3.9 as it's already included in AL2023. Prowler can thus be easily installed following the Generic method of installation via pip. Follow the steps below to successfully execute Prowler v4 in AWS CloudShell:
|
||||
After the migration of AWS CloudShell from Amazon Linux 2 to Amazon Linux 2023 [[1]](https://aws.amazon.com/about-aws/whats-new/2023/12/aws-cloudshell-migrated-al2023/) [[2]](https://docs.aws.amazon.com/cloudshell/latest/userguide/cloudshell-AL2023-migration.html), there is no longer a need to manually compile Python 3.9 as it's already included in AL2023. Prowler can thus be easily installed following the Generic method of installation via pip. Follow the steps below to successfully execute Prowler v4 in AWS CloudShell:
|
||||
|
||||
_Requirements_:
|
||||
|
||||
@@ -133,11 +163,13 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
|
||||
_Commands_:
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo bash
|
||||
adduser prowler
|
||||
su prowler
|
||||
pip install prowler
|
||||
python3 -m pip install --user pipx
|
||||
python3 -m pipx ensurepath
|
||||
pipx install prowler
|
||||
cd /tmp
|
||||
prowler aws
|
||||
```
|
||||
@@ -153,9 +185,12 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
|
||||
_Commands_:
|
||||
|
||||
```
|
||||
pip install prowler
|
||||
prowler -v
|
||||
```bash
|
||||
python3 -m pip install --user pipx
|
||||
python3 -m pipx ensurepath
|
||||
pipx install prowler
|
||||
cd /tmp
|
||||
prowler azure --az-cli-auth
|
||||
```
|
||||
|
||||
## Prowler container versions
|
||||
|
||||
@@ -4,21 +4,25 @@ Prowler allows you to do threat detection in AWS based on the CloudTrail log rec
|
||||
```
|
||||
prowler aws --category threat-detection
|
||||
```
|
||||
This comand will run these checks:
|
||||
This command will run these checks:
|
||||
|
||||
* `cloudtrail_threat_detection_privilege_escalation`
|
||||
* `cloudtrail_threat_detection_enumeration`
|
||||
* `cloudtrail_threat_detection_privilege_escalation` -> Detects privilege escalation attacks.
|
||||
* `cloudtrail_threat_detection_enumeration` -> Detects enumeration attacks.
|
||||
* `cloudtrail_threat_detection_llm_jacking` -> Detects LLM Jacking attacks.
|
||||
|
||||
???+ note
|
||||
Threat Detection checks will be only executed using `--category threat-detection` flag due to preformance.
|
||||
Threat Detection checks will be only executed using `--category threat-detection` flag due to performance.
|
||||
|
||||
## Config File
|
||||
|
||||
If you want to manage the behavior of the Threat Detection checks you can edit `config.yaml` file from `/prowler/config`. In this file you can edit the following attributes related with Threat Detection:
|
||||
|
||||
* `threat_detection_privilege_escalation_threshold`: determines the percentage of actions found to decide if it is an privilege_scalation attack event, by default is 0.1 (10%)
|
||||
* `threat_detection_privilege_escalation_threshold`: determines the percentage of actions found to decide if it is an privilege_scalation attack event, by default is 0.2 (20%)
|
||||
* `threat_detection_privilege_escalation_minutes`: it is the past minutes to search from now for privilege_escalation attacks, by default is 1440 minutes (24 hours)
|
||||
* `threat_detection_privilege_escalation_actions`: these are the default actions related with priviledge scalation.
|
||||
* `threat_detection_enumeration_threshold`: determines the percentage of actions found to decide if it is an enumeration attack event, by default is 0.1 (10%)
|
||||
* `threat_detection_privilege_escalation_actions`: these are the default actions related with privilege escalation.
|
||||
* `threat_detection_enumeration_threshold`: determines the percentage of actions found to decide if it is an enumeration attack event, by default is 0.3 (30%)
|
||||
* `threat_detection_enumeration_minutes`: it is the past minutes to search from now for enumeration attacks, by default is 1440 minutes (24 hours)
|
||||
* `threat_detection_enumeration_actions`: these are the default actions related with enumeration attacks.
|
||||
* `threat_detection_llm_jacking_threshold`: determines the percentage of actions found to decide if it is an LLM Jacking attack event, by default is 0.4 (40%)
|
||||
* `threat_detection_llm_jacking_minutes`: it is the past minutes to search from now for LLM Jacking attacks, by default is 1440 minutes (24 hours)
|
||||
* `threat_detection_llm_jacking_actions`: these are the default actions related with LLM Jacking attacks.
|
||||
@@ -7,7 +7,6 @@ At the time of writing this documentation the available Azure Clouds from differ
|
||||
- AzureCloud
|
||||
- AzureChinaCloud
|
||||
- AzureUSGovernment
|
||||
- AzureGermanCloud
|
||||
|
||||
If you want to change the default one you must include the flag `--azure-region`, i.e.:
|
||||
|
||||
|
||||
@@ -13,52 +13,56 @@ The following list includes all the AWS checks with configurable variables that
|
||||
|
||||
| Check Name | Value | Type |
|
||||
|---------------------------------------------------------------|--------------------------------------------------|-----------------|
|
||||
| `iam_user_accesskey_unused` | `max_unused_access_keys_days` | Integer |
|
||||
| `iam_user_console_access_unused` | `max_console_access_days` | Integer |
|
||||
| `ec2_elastic_ip_shodan` | `shodan_api_key` | String |
|
||||
| `ec2_securitygroup_with_many_ingress_egress_rules` | `max_security_group_rules` | Integer |
|
||||
| `ec2_instance_older_than_specific_days` | `max_ec2_instance_age_in_days` | Integer |
|
||||
| `ec2_securitygroup_allow_ingress_from_internet_to_high_risk_tcp_ports`| `ec2_sg_high_risk_ports` | List of Integer |
|
||||
| `vpc_endpoint_connections_trust_boundaries` | `trusted_account_ids` | List of Strings |
|
||||
| `vpc_endpoint_services_allowed_principals_trust_boundaries` | `trusted_account_ids` | List of Strings |
|
||||
| `cloudwatch_log_group_retention_policy_specific_days_enabled` | `log_group_retention_days` | Integer |
|
||||
| `appstream_fleet_session_idle_disconnect_timeout` | `max_idle_disconnect_timeout_in_seconds` | Integer |
|
||||
| `appstream_fleet_session_disconnect_timeout` | `max_disconnect_timeout_in_seconds` | Integer |
|
||||
| `appstream_fleet_maximum_session_duration` | `max_session_duration_seconds` | Integer |
|
||||
| `awslambda_function_using_supported_runtimes` | `obsolete_lambda_runtimes` | Integer |
|
||||
| `organizations_scp_check_deny_regions` | `organizations_enabled_regions` | List of Strings |
|
||||
| `organizations_delegated_administrators` | `organizations_trusted_delegated_administrators` | List of Strings |
|
||||
| `ecr_repositories_scan_vulnerabilities_in_latest_image` | `ecr_repository_vulnerability_minimum_severity` | String |
|
||||
| `trustedadvisor_premium_support_plan_subscribed` | `verify_premium_support_plans` | Boolean |
|
||||
| `config_recorder_all_regions_enabled` | `mute_non_default_regions` | Boolean |
|
||||
| `drs_job_exist` | `mute_non_default_regions` | Boolean |
|
||||
| `guardduty_is_enabled` | `mute_non_default_regions` | Boolean |
|
||||
| `securityhub_enabled` | `mute_non_default_regions` | Boolean |
|
||||
| `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_entropy` | Integer |
|
||||
| `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_minutes` | Integer |
|
||||
| `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_actions` | List of Strings |
|
||||
| `cloudtrail_threat_detection_enumeration` | `threat_detection_enumeration_entropy` | Integer |
|
||||
| `cloudtrail_threat_detection_enumeration` | `threat_detection_enumeration_minutes` | Integer |
|
||||
| `cloudtrail_threat_detection_enumeration` | `threat_detection_enumeration_actions` | List of Strings |
|
||||
| `codebuild_project_no_secrets_in_variables` | `excluded_sensitive_environment_variables` | List of Strings |
|
||||
| `rds_instance_backup_enabled` | `check_rds_instance_replicas` | Boolean |
|
||||
| `ec2_securitygroup_allow_ingress_from_internet_to_any_port` | `ec2_allowed_interface_types` | List of Strings |
|
||||
| `ec2_securitygroup_allow_ingress_from_internet_to_any_port` | `ec2_allowed_instance_owners` | List of Strings |
|
||||
| `acm_certificates_expiration_check` | `days_to_expire_threshold` | Integer |
|
||||
| `eks_control_plane_logging_all_types_enabled` | `eks_required_log_types` | List of Strings |
|
||||
| `eks_cluster_uses_a_supported_version` | `eks_cluster_oldest_version_supported` | String |
|
||||
| `elbv2_is_in_multiple_az` | `elbv2_min_azs` | Integer |
|
||||
| `elb_is_in_multiple_az` | `elb_min_azs` | Integer |
|
||||
| `appstream_fleet_maximum_session_duration` | `max_session_duration_seconds` | Integer |
|
||||
| `appstream_fleet_session_disconnect_timeout` | `max_disconnect_timeout_in_seconds` | Integer |
|
||||
| `appstream_fleet_session_idle_disconnect_timeout` | `max_idle_disconnect_timeout_in_seconds` | Integer |
|
||||
| `autoscaling_find_secrets_ec2_launch_configuration` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `awslambda_function_no_secrets_in_code` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `awslambda_function_no_secrets_in_variables` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `awslambda_function_using_supported_runtimes` | `obsolete_lambda_runtimes` | Integer |
|
||||
| `awslambda_function_vpc_is_in_multi_azs` | `lambda_min_azs` | Integer |
|
||||
| `cloudformation_stack_outputs_find_secrets` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `cloudtrail_threat_detection_enumeration` | `threat_detection_enumeration_actions` | List of Strings |
|
||||
| `cloudtrail_threat_detection_enumeration` | `threat_detection_enumeration_entropy` | Integer |
|
||||
| `cloudtrail_threat_detection_enumeration` | `threat_detection_enumeration_minutes` | Integer |
|
||||
| `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_actions` | List of Strings |
|
||||
| `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_entropy` | Integer |
|
||||
| `cloudtrail_threat_detection_privilege_escalation` | `threat_detection_privilege_escalation_minutes` | Integer |
|
||||
| `cloudwatch_log_group_no_secrets_in_logs` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `cloudwatch_log_group_retention_policy_specific_days_enabled` | `log_group_retention_days` | Integer |
|
||||
| `codebuild_project_no_secrets_in_variables` | `excluded_sensitive_environment_variables` | List of Strings |
|
||||
| `codebuild_project_no_secrets_in_variables` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `config_recorder_all_regions_enabled` | `mute_non_default_regions` | Boolean |
|
||||
| `drs_job_exist` | `mute_non_default_regions` | Boolean |
|
||||
| `ec2_elastic_ip_shodan` | `shodan_api_key` | String |
|
||||
| `ec2_instance_older_than_specific_days` | `max_ec2_instance_age_in_days` | Integer |
|
||||
| `ec2_instance_secrets_user_data` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `ec2_launch_template_no_secrets` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `ec2_securitygroup_allow_ingress_from_internet_to_any_port` | `ec2_allowed_instance_owners` | List of Strings |
|
||||
| `ec2_securitygroup_allow_ingress_from_internet_to_any_port` | `ec2_allowed_interface_types` | List of Strings |
|
||||
| `ec2_securitygroup_allow_ingress_from_internet_to_high_risk_tcp_ports`| `ec2_sg_high_risk_ports` | List of Integer |
|
||||
| `ec2_securitygroup_with_many_ingress_egress_rules` | `max_security_group_rules` | Integer |
|
||||
| `ecs_task_definitions_no_environment_secrets` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `ecr_repositories_scan_vulnerabilities_in_latest_image` | `ecr_repository_vulnerability_minimum_severity` | String |
|
||||
| `eks_cluster_uses_a_supported_version` | `eks_cluster_oldest_version_supported` | String |
|
||||
| `eks_control_plane_logging_all_types_enabled` | `eks_required_log_types` | List of Strings |
|
||||
| `elasticache_redis_cluster_backup_enabled` | `minimum_snapshot_retention_period` | Integer |
|
||||
| `elb_is_in_multiple_az` | `elb_min_azs` | Integer |
|
||||
| `elbv2_is_in_multiple_az` | `elbv2_min_azs` | Integer |
|
||||
| `guardduty_is_enabled` | `mute_non_default_regions` | Boolean |
|
||||
| `iam_user_accesskey_unused` | `max_unused_access_keys_days` | Integer |
|
||||
| `iam_user_console_access_unused` | `max_console_access_days` | Integer |
|
||||
| `organizations_delegated_administrators` | `organizations_trusted_delegated_administrators` | List of Strings |
|
||||
| `organizations_scp_check_deny_regions` | `organizations_enabled_regions` | List of Strings |
|
||||
| `rds_instance_backup_enabled` | `check_rds_instance_replicas` | Boolean |
|
||||
| `securityhub_enabled` | `mute_non_default_regions` | Boolean |
|
||||
| `secretsmanager_secret_unused` | `max_days_secret_unused` | Integer |
|
||||
| `secretsmanager_secret_rotated_periodically` | `max_days_secret_unrotated` | Integer |
|
||||
| `ssm_document_secrets` | `secrets_ignore_patterns` | List of Strings |
|
||||
| `trustedadvisor_premium_support_plan_subscribed` | `verify_premium_support_plans` | Boolean |
|
||||
| `vpc_endpoint_connections_trust_boundaries` | `trusted_account_ids` | List of Strings |
|
||||
| `vpc_endpoint_services_allowed_principals_trust_boundaries` | `trusted_account_ids` | List of Strings |
|
||||
|
||||
|
||||
## Azure
|
||||
@@ -157,6 +161,7 @@ aws:
|
||||
]
|
||||
|
||||
# AWS VPC Configuration (vpc_endpoint_connections_trust_boundaries, vpc_endpoint_services_allowed_principals_trust_boundaries)
|
||||
# AWS SSM Configuration (aws.ssm_documents_set_as_public)
|
||||
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
|
||||
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
|
||||
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]
|
||||
@@ -224,7 +229,7 @@ aws:
|
||||
|
||||
# AWS CloudTrail Configuration
|
||||
# aws.cloudtrail_threat_detection_privilege_escalation
|
||||
threat_detection_privilege_escalation_threshold: 0.1 # Percentage of actions found to decide if it is an privilege_escalation attack event, by default is 0.1 (10%)
|
||||
threat_detection_privilege_escalation_threshold: 0.2 # Percentage of actions found to decide if it is an privilege_escalation attack event, by default is 0.2 (20%)
|
||||
threat_detection_privilege_escalation_minutes: 1440 # Past minutes to search from now for privilege_escalation attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_privilege_escalation_actions:
|
||||
[
|
||||
@@ -281,7 +286,7 @@ aws:
|
||||
"UpdateLoginProfile",
|
||||
]
|
||||
# aws.cloudtrail_threat_detection_enumeration
|
||||
threat_detection_enumeration_threshold: 0.1 # Percentage of actions found to decide if it is an enumeration attack event, by default is 0.1 (10%)
|
||||
threat_detection_enumeration_threshold: 0.3 # Percentage of actions found to decide if it is an enumeration attack event, by default is 0.3 (30%)
|
||||
threat_detection_enumeration_minutes: 1440 # Past minutes to search from now for enumeration attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_enumeration_actions:
|
||||
[
|
||||
@@ -376,6 +381,24 @@ aws:
|
||||
"LookupEvents",
|
||||
"Search",
|
||||
]
|
||||
# aws.cloudtrail_threat_detection_llm_jacking
|
||||
threat_detection_llm_jacking_threshold: 0.4 # Percentage of actions found to decide if it is an LLM Jacking attack event, by default is 0.4 (40%)
|
||||
threat_detection_llm_jacking_minutes: 1440 # Past minutes to search from now for LLM Jacking attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_llm_jacking_actions:
|
||||
[
|
||||
"PutUseCaseForModelAccess", # Submits a use case for model access, providing justification (Write).
|
||||
"PutFoundationModelEntitlement", # Grants entitlement for accessing a foundation model (Write).
|
||||
"PutModelInvocationLoggingConfiguration", # Configures logging for model invocations (Write).
|
||||
"CreateFoundationModelAgreement", # Creates a new agreement to use a foundation model (Write).
|
||||
"InvokeModel", # Invokes a specified Bedrock model for inference using provided prompt and parameters (Read).
|
||||
"InvokeModelWithResponseStream", # Invokes a Bedrock model for inference with real-time token streaming (Read).
|
||||
"GetUseCaseForModelAccess", # Retrieves an existing use case for model access (Read).
|
||||
"GetModelInvocationLoggingConfiguration", # Fetches the logging configuration for model invocations (Read).
|
||||
"GetFoundationModelAvailability", # Checks the availability of a foundation model for use (Read).
|
||||
"ListFoundationModelAgreementOffers", # Lists available agreement offers for accessing foundation models (List).
|
||||
"ListFoundationModels", # Lists the available foundation models in Bedrock (List).
|
||||
"ListProvisionedModelThroughputs", # Lists the provisioned throughput for previously created models (List).
|
||||
]
|
||||
|
||||
# AWS RDS Configuration
|
||||
# aws.rds_instance_backup_enabled
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# GCP Organization
|
||||
|
||||
By default, Prowler scans all Google Cloud projects accessible to the authenticated user.
|
||||
|
||||
To limit the scan to projects within a specific Google Cloud organization, use the `--organization-id` option with the GCP organization ID:
|
||||
|
||||
```console
|
||||
prowler gcp --organization-id organization-id
|
||||
```
|
||||
|
||||
???+ warning
|
||||
Make sure that the used credentials have the role Cloud Asset Viewer (`roles/cloudasset.viewer`) or Cloud Asset Owner (`roles/cloudasset.owner`) on the organization level.
|
||||
|
||||
???+ note
|
||||
With this option, Prowler retrieves all projects within the specified organization, including those organized in folders and nested subfolders. This ensures that every project under the organization’s hierarchy is scanned, providing full visibility across the entire organization.
|
||||
|
||||
???+ note
|
||||
To find the organization ID, use the following command:
|
||||
|
||||
```console
|
||||
gcloud organizations list
|
||||
```
|
||||
@@ -24,6 +24,11 @@ Prowler can run without showing its banner:
|
||||
```console
|
||||
prowler <provider> -b/--no-banner
|
||||
```
|
||||
## Disable Colors
|
||||
Prowler can run without showing colors:
|
||||
```console
|
||||
prowler <provider> --no-color
|
||||
```
|
||||
## Checks
|
||||
Prowler has checks per provider, there are options related with them:
|
||||
|
||||
@@ -120,5 +125,5 @@ prowler <provider> --list-categories
|
||||
```
|
||||
- Execute specific category(s):
|
||||
```console
|
||||
prowler <provider> --categories
|
||||
prowler <provider> --categories secrets
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
# Prowler Check Kreator
|
||||
|
||||
???+ note
|
||||
Currently, this tool is only available for creating checks for the AWS provider.
|
||||
|
||||
**Prowler Check Kreator** is a utility designed to streamline the creation of new checks for Prowler. This tool generates all necessary files required to add a new check to the Prowler repository. Specifically, it creates:
|
||||
|
||||
- A dedicated folder for the check.
|
||||
- The main check script.
|
||||
- A metadata file with essential details.
|
||||
- A folder and file structure for testing the check.
|
||||
|
||||
## Usage
|
||||
|
||||
To use the tool, execute the main script with the following command:
|
||||
|
||||
```bash
|
||||
python util/prowler_check_kreator/prowler_check_kreator.py <prowler_provider> <check_name>
|
||||
```
|
||||
Parameters:
|
||||
|
||||
- `<prowler_provider>`: Currently only AWS is supported.
|
||||
- `<check_name>`: The name you wish to assign to the new check.
|
||||
|
||||
## AI integration
|
||||
|
||||
This tool optionally integrates AI to assist in generating the check code and metadata file content. When AI assistance is chosen, the tool uses [Gemini](https://gemini.google.com/) to produce preliminary code and metadata.
|
||||
|
||||
???+ note
|
||||
For this feature to work, you must have the library `google-generativeai` installed in your Python environment.
|
||||
|
||||
???+ warning
|
||||
AI-generated code and metadata might contain errors or require adjustments to align with specific Prowler requirements. Carefully review all AI-generated content before committing.
|
||||
|
||||
To enable AI assistance, simply confirm when prompted by the tool. Additionally, ensure that the `GEMINI_API_KEY` environment variable is set with a valid Gemini API key. For instructions on obtaining your API key, refer to the [Gemini documentation](https://ai.google.dev/gemini-api/docs/api-key).
|
||||
@@ -142,7 +142,8 @@ The JSON-OCSF output format implements the [Detection Finding](https://schema.oc
|
||||
"desc": "Ensure CloudTrail is enabled in all regions",
|
||||
"product_uid": "prowler",
|
||||
"title": "Ensure CloudTrail is enabled in all regions",
|
||||
"uid": "prowler-aws-cloudtrail_multi_region_enabled-123456789012-ap-northeast-1-123456789012"
|
||||
"uid": "prowler-aws-cloudtrail_multi_region_enabled-123456789012-ap-northeast-1-123456789012",
|
||||
"types": ["Software and Configuration Checks","Industry and Regulatory Standards","CIS AWS Foundations Benchmark"],
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
@@ -189,11 +190,10 @@ The JSON-OCSF output format implements the [Detection Finding](https://schema.oc
|
||||
"type_uid": 200401,
|
||||
"type_name": "Create",
|
||||
"unmapped": {
|
||||
"check_type": "Software and Configuration Checks,Industry and Regulatory Standards,CIS AWS Foundations Benchmark",
|
||||
"related_url": "",
|
||||
"categories": "forensics-ready",
|
||||
"depends_on": "",
|
||||
"related_to": "",
|
||||
"categories": ["forensics-ready"],
|
||||
"depends_on": [],
|
||||
"related_to": [],
|
||||
"notes": "",
|
||||
"compliance": {
|
||||
"CISA": [
|
||||
@@ -336,7 +336,7 @@ The following is the mapping between the native JSON and the Detection Finding f
|
||||
| Provider | cloud.provider |
|
||||
| CheckID | metadata.event_code |
|
||||
| CheckTitle | finding_info.title |
|
||||
| CheckType | unmapped.check_type |
|
||||
| CheckType | finding_info.types |
|
||||
| ServiceName | resources.group.name |
|
||||
| SubServiceName | _Not mapped yet_ |
|
||||
| Status | status_code |
|
||||
|
||||
@@ -65,6 +65,7 @@ nav:
|
||||
- Pentesting: tutorials/pentesting.md
|
||||
- Parallel Execution: tutorials/parallel-execution.md
|
||||
- Developer Guide: developer-guide/introduction.md
|
||||
- Prowler Check Kreator: tutorials/prowler-check-kreator.md
|
||||
- AWS:
|
||||
- Authentication: tutorials/aws/authentication.md
|
||||
- Assume Role: tutorials/aws/role-assumption.md
|
||||
@@ -87,6 +88,7 @@ nav:
|
||||
- Google Cloud:
|
||||
- Authentication: tutorials/gcp/authentication.md
|
||||
- Projects: tutorials/gcp/projects.md
|
||||
- Organization: tutorials/gcp/organization.md
|
||||
- Kubernetes:
|
||||
- In-Cluster Execution: tutorials/kubernetes/in-cluster.md
|
||||
- Non In-Cluster Execution: tutorials/kubernetes/outside-cluster.md
|
||||
|
||||
@@ -59,9 +59,12 @@ Resources:
|
||||
- 'appstream:Describe*'
|
||||
- 'appstream:List*'
|
||||
- 'backup:List*'
|
||||
- 'bedrock:List*'
|
||||
- 'bedrock:Get*'
|
||||
- 'cloudtrail:GetInsightSelectors'
|
||||
- 'codeartifact:List*'
|
||||
- 'codebuild:BatchGet*'
|
||||
- 'codebuild:ListReportGroups'
|
||||
- 'cognito-idp:GetUserPoolMfaConfig'
|
||||
- 'dlm:Get*'
|
||||
- 'drs:Describe*'
|
||||
@@ -82,11 +85,14 @@ Resources:
|
||||
- 'logs:FilterLogEvents'
|
||||
- 'lightsail:GetRelationalDatabases'
|
||||
- 'macie2:GetMacieSession'
|
||||
- 'macie2:GetAutomatedDiscoveryConfiguration'
|
||||
- 's3:GetAccountPublicAccessBlock'
|
||||
- 'shield:DescribeProtection'
|
||||
- 'shield:GetSubscriptionState'
|
||||
- 'securityhub:BatchImportFindings'
|
||||
- 'securityhub:GetFindings'
|
||||
- 'servicecatalog:Describe*'
|
||||
- 'servicecatalog:List*'
|
||||
- 'ssm:GetDocument'
|
||||
- 'ssm-incidents:List*'
|
||||
- 'support:Describe*'
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
"appstream:Describe*",
|
||||
"appstream:List*",
|
||||
"backup:List*",
|
||||
"bedrock:List*",
|
||||
"bedrock:Get*",
|
||||
"cloudtrail:GetInsightSelectors",
|
||||
"codeartifact:List*",
|
||||
"codebuild:BatchGet*",
|
||||
"codebuild:ListReportGroups",
|
||||
"cognito-idp:GetUserPoolMfaConfig",
|
||||
"dlm:Get*",
|
||||
"drs:Describe*",
|
||||
@@ -30,11 +33,14 @@
|
||||
"logs:FilterLogEvents",
|
||||
"lightsail:GetRelationalDatabases",
|
||||
"macie2:GetMacieSession",
|
||||
"macie2:GetAutomatedDiscoveryConfiguration",
|
||||
"s3:GetAccountPublicAccessBlock",
|
||||
"shield:DescribeProtection",
|
||||
"shield:GetSubscriptionState",
|
||||
"securityhub:BatchImportFindings",
|
||||
"securityhub:GetFindings",
|
||||
"servicecatalog:Describe*",
|
||||
"servicecatalog:List*",
|
||||
"ssm:GetDocument",
|
||||
"ssm-incidents:List*",
|
||||
"support:Describe*",
|
||||
|
||||
Generated
+2359
-1954
File diff suppressed because it is too large.
Load diff
+126
-55
@@ -5,6 +5,7 @@ import sys
|
||||
from os import environ
|
||||
|
||||
from colorama import Fore, Style
|
||||
from colorama import init as colorama_init
|
||||
|
||||
from prowler.config.config import (
|
||||
csv_file_suffix,
|
||||
@@ -52,8 +53,11 @@ from prowler.lib.outputs.compliance.cis.cis_gcp import GCPCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_kubernetes import KubernetesCIS
|
||||
from prowler.lib.outputs.compliance.compliance import display_compliance_table
|
||||
from prowler.lib.outputs.compliance.ens.ens_aws import AWSENS
|
||||
from prowler.lib.outputs.compliance.ens.ens_azure import AzureENS
|
||||
from prowler.lib.outputs.compliance.ens.ens_gcp import GCPENS
|
||||
from prowler.lib.outputs.compliance.generic.generic import GenericCompliance
|
||||
from prowler.lib.outputs.compliance.iso27001.iso27001_aws import AWSISO27001
|
||||
from prowler.lib.outputs.compliance.kisa_ismsp.kisa_ismsp_aws import AWSKISAISMSP
|
||||
from prowler.lib.outputs.compliance.mitre_attack.mitre_attack_aws import AWSMitreAttack
|
||||
from prowler.lib.outputs.compliance.mitre_attack.mitre_attack_azure import (
|
||||
AzureMitreAttack,
|
||||
@@ -68,8 +72,12 @@ from prowler.lib.outputs.slack.slack import Slack
|
||||
from prowler.lib.outputs.summary_table import display_summary_table
|
||||
from prowler.providers.aws.lib.s3.s3 import S3
|
||||
from prowler.providers.aws.lib.security_hub.security_hub import SecurityHub
|
||||
from prowler.providers.aws.models import AWSOutputOptions
|
||||
from prowler.providers.azure.models import AzureOutputOptions
|
||||
from prowler.providers.common.provider import Provider
|
||||
from prowler.providers.common.quick_inventory import run_provider_quick_inventory
|
||||
from prowler.providers.gcp.models import GCPOutputOptions
|
||||
from prowler.providers.kubernetes.models import KubernetesOutputOptions
|
||||
|
||||
|
||||
def prowler():
|
||||
@@ -107,6 +115,9 @@ def prowler():
|
||||
and not checks_folder
|
||||
)
|
||||
|
||||
if args.no_color:
|
||||
colorama_init(strip=True)
|
||||
|
||||
if not args.no_banner:
|
||||
legend = args.verbose or getattr(args, "fixer", None)
|
||||
print_banner(legend)
|
||||
@@ -168,15 +179,15 @@ def prowler():
|
||||
|
||||
# Load checks to execute
|
||||
checks_to_execute = load_checks_to_execute(
|
||||
bulk_checks_metadata,
|
||||
bulk_compliance_frameworks,
|
||||
checks_file,
|
||||
checks,
|
||||
services,
|
||||
severities,
|
||||
compliance_framework,
|
||||
categories,
|
||||
provider,
|
||||
bulk_checks_metadata=bulk_checks_metadata,
|
||||
bulk_compliance_frameworks=bulk_compliance_frameworks,
|
||||
checks_file=checks_file,
|
||||
check_list=checks,
|
||||
service_list=services,
|
||||
severities=severities,
|
||||
compliance_frameworks=compliance_framework,
|
||||
categories=categories,
|
||||
provider=provider,
|
||||
)
|
||||
|
||||
# if --list-checks-json, dump a json file and exit
|
||||
@@ -190,7 +201,7 @@ def prowler():
|
||||
sys.exit()
|
||||
|
||||
# Provider to scan
|
||||
Provider.set_global_provider(args)
|
||||
Provider.init_global_provider(args)
|
||||
global_provider = Provider.get_global_provider()
|
||||
|
||||
# Print Provider Credentials
|
||||
@@ -231,11 +242,23 @@ def prowler():
|
||||
# Sort final check list
|
||||
checks_to_execute = sorted(checks_to_execute)
|
||||
|
||||
# Setup Mutelist
|
||||
global_provider.mutelist = args.mutelist_file
|
||||
|
||||
# Setup Output Options
|
||||
global_provider.output_options = (args, bulk_checks_metadata)
|
||||
if provider == "aws":
|
||||
output_options = AWSOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "azure":
|
||||
output_options = AzureOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "gcp":
|
||||
output_options = GCPOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "kubernetes":
|
||||
output_options = KubernetesOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
|
||||
# Run the quick inventory for the provider if available
|
||||
if hasattr(args, "quick_inventory") and args.quick_inventory:
|
||||
@@ -251,6 +274,7 @@ def prowler():
|
||||
global_provider,
|
||||
custom_checks_metadata,
|
||||
args.config_file,
|
||||
output_options,
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
@@ -258,7 +282,7 @@ def prowler():
|
||||
)
|
||||
|
||||
# Prowler Fixer
|
||||
if global_provider.output_options.fixer:
|
||||
if output_options.fixer:
|
||||
print(f"{Style.BRIGHT}\nRunning Prowler Fixer, please wait...{Style.RESET_ALL}")
|
||||
# Check if there are any FAIL findings
|
||||
if any("FAIL" in finding.status for finding in findings):
|
||||
@@ -304,7 +328,8 @@ def prowler():
|
||||
# TODO: this part is needed since the checks generates a Check_Report_XXX and the output uses Finding
|
||||
# This will be refactored for the outputs generate directly the Finding
|
||||
finding_outputs = [
|
||||
Finding.generate_output(global_provider, finding) for finding in findings
|
||||
Finding.generate_output(global_provider, finding, output_options)
|
||||
for finding in findings
|
||||
]
|
||||
|
||||
generated_outputs = {"regular": [], "compliance": []}
|
||||
@@ -312,8 +337,8 @@ def prowler():
|
||||
if args.output_formats:
|
||||
for mode in args.output_formats:
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/"
|
||||
f"{global_provider.output_options.output_filename}"
|
||||
f"{output_options.output_directory}/"
|
||||
f"{output_options.output_filename}"
|
||||
)
|
||||
if mode == "csv":
|
||||
csv_output = CSV(
|
||||
@@ -355,16 +380,16 @@ def prowler():
|
||||
)
|
||||
|
||||
# Compliance Frameworks
|
||||
input_compliance_frameworks = set(
|
||||
global_provider.output_options.output_modes
|
||||
).intersection(get_available_compliance_frameworks(provider))
|
||||
input_compliance_frameworks = set(output_options.output_modes).intersection(
|
||||
get_available_compliance_frameworks(provider)
|
||||
)
|
||||
if provider == "aws":
|
||||
for compliance_name in input_compliance_frameworks:
|
||||
if compliance_name.startswith("cis_"):
|
||||
# Generate CIS Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
cis = AWSCIS(
|
||||
findings=finding_outputs,
|
||||
@@ -377,8 +402,8 @@ def prowler():
|
||||
elif compliance_name == "mitre_attack_aws":
|
||||
# Generate MITRE ATT&CK Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
mitre_attack = AWSMitreAttack(
|
||||
findings=finding_outputs,
|
||||
@@ -391,8 +416,8 @@ def prowler():
|
||||
elif compliance_name.startswith("ens_"):
|
||||
# Generate ENS Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
ens = AWSENS(
|
||||
findings=finding_outputs,
|
||||
@@ -405,8 +430,8 @@ def prowler():
|
||||
elif compliance_name.startswith("aws_well_architected_framework"):
|
||||
# Generate AWS Well-Architected Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
aws_well_architected = AWSWellArchitected(
|
||||
findings=finding_outputs,
|
||||
@@ -419,8 +444,8 @@ def prowler():
|
||||
elif compliance_name.startswith("iso27001_"):
|
||||
# Generate ISO27001 Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
iso27001 = AWSISO27001(
|
||||
findings=finding_outputs,
|
||||
@@ -430,10 +455,24 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(iso27001)
|
||||
iso27001.batch_write_data_to_file()
|
||||
elif compliance_name.startswith("kisa"):
|
||||
# Generate KISA-ISMS-P Finding Object
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
kisa_ismsp = AWSKISAISMSP(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
create_file_descriptor=True,
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(kisa_ismsp)
|
||||
kisa_ismsp.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
generic_compliance = GenericCompliance(
|
||||
findings=finding_outputs,
|
||||
@@ -449,8 +488,8 @@ def prowler():
|
||||
if compliance_name.startswith("cis_"):
|
||||
# Generate CIS Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
cis = AzureCIS(
|
||||
findings=finding_outputs,
|
||||
@@ -463,8 +502,8 @@ def prowler():
|
||||
elif compliance_name == "mitre_attack_azure":
|
||||
# Generate MITRE ATT&CK Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
mitre_attack = AzureMitreAttack(
|
||||
findings=finding_outputs,
|
||||
@@ -474,10 +513,24 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(mitre_attack)
|
||||
mitre_attack.batch_write_data_to_file()
|
||||
elif compliance_name.startswith("ens_"):
|
||||
# Generate ENS Finding Object
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
ens = AzureENS(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
create_file_descriptor=True,
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(ens)
|
||||
ens.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
generic_compliance = GenericCompliance(
|
||||
findings=finding_outputs,
|
||||
@@ -493,8 +546,8 @@ def prowler():
|
||||
if compliance_name.startswith("cis_"):
|
||||
# Generate CIS Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
cis = GCPCIS(
|
||||
findings=finding_outputs,
|
||||
@@ -507,8 +560,8 @@ def prowler():
|
||||
elif compliance_name == "mitre_attack_gcp":
|
||||
# Generate MITRE ATT&CK Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
mitre_attack = GCPMitreAttack(
|
||||
findings=finding_outputs,
|
||||
@@ -518,10 +571,24 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(mitre_attack)
|
||||
mitre_attack.batch_write_data_to_file()
|
||||
elif compliance_name.startswith("ens_"):
|
||||
# Generate ENS Finding Object
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
ens = GCPENS(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
create_file_descriptor=True,
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(ens)
|
||||
ens.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
generic_compliance = GenericCompliance(
|
||||
findings=finding_outputs,
|
||||
@@ -537,8 +604,8 @@ def prowler():
|
||||
if compliance_name.startswith("cis_"):
|
||||
# Generate CIS Finding Object
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
cis = KubernetesCIS(
|
||||
findings=finding_outputs,
|
||||
@@ -550,8 +617,8 @@ def prowler():
|
||||
cis.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{global_provider.output_options.output_directory}/compliance/"
|
||||
f"{global_provider.output_options.output_filename}_{compliance_name}.csv"
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
generic_compliance = GenericCompliance(
|
||||
findings=finding_outputs,
|
||||
@@ -584,7 +651,11 @@ def prowler():
|
||||
)
|
||||
|
||||
security_hub_regions = (
|
||||
global_provider.get_available_aws_service_regions("securityhub")
|
||||
global_provider.get_available_aws_service_regions(
|
||||
"securityhub",
|
||||
global_provider.identity.partition,
|
||||
global_provider.identity.audited_regions,
|
||||
)
|
||||
if not global_provider.identity.audited_regions
|
||||
else global_provider.identity.audited_regions
|
||||
)
|
||||
@@ -594,7 +665,7 @@ def prowler():
|
||||
aws_partition=global_provider.identity.partition,
|
||||
aws_session=global_provider.session.current_session,
|
||||
findings=asff_output.data,
|
||||
send_only_fails=global_provider.output_options.send_sh_only_fails,
|
||||
send_only_fails=output_options.send_sh_only_fails,
|
||||
aws_security_hub_available_regions=security_hub_regions,
|
||||
)
|
||||
# Send the findings to Security Hub
|
||||
@@ -620,7 +691,7 @@ def prowler():
|
||||
display_summary_table(
|
||||
findings,
|
||||
global_provider,
|
||||
global_provider.output_options,
|
||||
output_options,
|
||||
)
|
||||
# Only display compliance table if there are findings (not all MANUAL) and it is a default execution
|
||||
if (
|
||||
@@ -639,13 +710,13 @@ def prowler():
|
||||
findings,
|
||||
bulk_checks_metadata,
|
||||
compliance,
|
||||
global_provider.output_options.output_filename,
|
||||
global_provider.output_options.output_directory,
|
||||
output_options.output_filename,
|
||||
output_options.output_directory,
|
||||
compliance_overview,
|
||||
)
|
||||
if compliance_overview:
|
||||
print(
|
||||
f"\nDetailed compliance results are in {Fore.YELLOW}{global_provider.output_options.output_directory}/compliance/{Style.RESET_ALL}\n"
|
||||
f"\nDetailed compliance results are in {Fore.YELLOW}{output_options.output_directory}/compliance/{Style.RESET_ALL}\n"
|
||||
)
|
||||
|
||||
# If custom checks were passed, remove the modules
|
||||
|
||||
@@ -557,7 +557,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"inspector2_findings_exist"
|
||||
"inspector2_is_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -587,7 +587,8 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"inspector2_findings_exist",
|
||||
"inspector2_active_findings_exist",
|
||||
"inspector2_is_enabled",
|
||||
"ecr_registry_scan_images_on_push_enabled",
|
||||
"ecr_repositories_scan_vulnerabilities_in_latest_image",
|
||||
"ecr_repositories_scan_images_on_push_enabled"
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
"Service": "ebs"
|
||||
}
|
||||
],
|
||||
"Checks": []
|
||||
"Checks": [
|
||||
"ec2_ebs_volume_snapshots_exists"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "1.0.3",
|
||||
@@ -42,7 +44,8 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"ec2_ebs_default_encryption"
|
||||
"ec2_ebs_default_encryption",
|
||||
"ec2_ebs_volume_encryption"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -87,7 +90,9 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"iam_user_mfa_enabled_console_access"
|
||||
"iam_user_mfa_enabled_console_access",
|
||||
"iam_user_hardware_mfa_enabled",
|
||||
"iam_root_mfa_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -102,7 +107,9 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"iam_user_mfa_enabled_console_access"
|
||||
"iam_user_mfa_enabled_console_access",
|
||||
"iam_user_hardware_mfa_enabled",
|
||||
"iam_root_mfa_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -117,7 +124,9 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"iam_root_mfa_enabled"
|
||||
"iam_root_mfa_enabled",
|
||||
"iam_root_hardware_mfa_enabled",
|
||||
"iam_user_mfa_enabled_console_access"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -162,7 +171,10 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"rds_instance_no_public_access"
|
||||
"rds_instance_no_public_access",
|
||||
"s3_bucket_public_access",
|
||||
"s3_bucket_public_list_acl",
|
||||
"s3_account_level_public_access_blocks"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -192,7 +204,8 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"rds_instance_storage_encrypted"
|
||||
"rds_instance_storage_encrypted",
|
||||
"rds_instance_transport_encrypted"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -485,7 +485,7 @@
|
||||
"codeartifact_packages_external_public_publishing_disabled",
|
||||
"ecr_repositories_not_publicly_accessible",
|
||||
"efs_not_publicly_accessible",
|
||||
"eks_endpoints_not_publicly_accessible",
|
||||
"eks_cluster_not_publicly_accessible",
|
||||
"elb_internet_facing",
|
||||
"elbv2_internet_facing",
|
||||
"s3_account_level_public_access_blocks",
|
||||
@@ -664,7 +664,7 @@
|
||||
"awslambda_function_not_publicly_accessible",
|
||||
"apigateway_restapi_waf_acl_attached",
|
||||
"cloudfront_distributions_using_waf",
|
||||
"eks_control_plane_endpoint_access_restricted",
|
||||
"eks_cluster_not_publicly_accessible",
|
||||
"sagemaker_models_network_isolation_enabled",
|
||||
"sagemaker_models_vpc_settings_configured",
|
||||
"sagemaker_notebook_instance_vpc_settings_configured",
|
||||
|
||||
@@ -455,7 +455,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon S3 provides a variety of no, or low, cost encryption options to protect data at rest.",
|
||||
@@ -476,7 +477,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "At the Amazon S3 bucket level, you can configure permissions through a bucket policy making the objects accessible only through HTTPS.",
|
||||
@@ -497,7 +499,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.",
|
||||
@@ -518,7 +521,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Amazon S3 buckets can contain sensitive data, that for security purposes should be discovered, monitored, classified and protected. Macie along with other 3rd party tools can automatically provide an inventory of Amazon S3 buckets.",
|
||||
@@ -540,7 +544,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon S3 provides `Block public access (bucket settings)` and `Block public access (account settings)` to help you manage public access to Amazon S3 resources. By default, S3 buckets and objects are created with public access disabled. However, an IAM principal with sufficient S3 permissions can enable public access at the bucket and/or object level. While enabled, `Block public access (bucket settings)` prevents an individual bucket, and its contained objects, from becoming publicly accessible. Similarly, `Block public access (account settings)` prevents all buckets, and contained objects, from becoming publicly accessible across the entire account.",
|
||||
@@ -561,7 +566,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Elastic Compute Cloud (EC2) supports encryption at rest when using the Elastic Block Store (EBS) service. While disabled by default, forcing encryption at EBS volume creation is supported.",
|
||||
@@ -578,11 +584,13 @@
|
||||
"Id": "2.3.1",
|
||||
"Description": "Ensure that encryption is enabled for RDS Instances",
|
||||
"Checks": [
|
||||
"rds_instance_storage_encrypted"
|
||||
"rds_instance_storage_encrypted",
|
||||
"rds_instance_transport_encrypted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon RDS encrypted DB instances use the industry standard AES-256 encryption algorithm to encrypt your data on the server that hosts your Amazon RDS DB instances. After your data is encrypted, Amazon RDS handles authentication of access and decryption of your data transparently with a minimal impact on performance.",
|
||||
|
||||
@@ -455,7 +455,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon S3 provides a variety of no, or low, cost encryption options to protect data at rest.",
|
||||
@@ -476,7 +477,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "At the Amazon S3 bucket level, you can configure permissions through a bucket policy making the objects accessible only through HTTPS.",
|
||||
@@ -497,7 +499,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.",
|
||||
@@ -518,7 +521,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Amazon S3 buckets can contain sensitive data, that for security purposes should be discovered, monitored, classified and protected. Macie along with other 3rd party tools can automatically provide an inventory of Amazon S3 buckets.",
|
||||
@@ -540,7 +544,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon S3 provides `Block public access (bucket settings)` and `Block public access (account settings)` to help you manage public access to Amazon S3 resources. By default, S3 buckets and objects are created with public access disabled. However, an IAM principal with sufficient S3 permissions can enable public access at the bucket and/or object level. While enabled, `Block public access (bucket settings)` prevents an individual bucket, and its contained objects, from becoming publicly accessible. Similarly, `Block public access (account settings)` prevents all buckets, and contained objects, from becoming publicly accessible across the entire account.",
|
||||
@@ -561,7 +566,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Elastic Compute Cloud (EC2) supports encryption at rest when using the Elastic Block Store (EBS) service. While disabled by default, forcing encryption at EBS volume creation is supported.",
|
||||
@@ -578,11 +584,13 @@
|
||||
"Id": "2.3.1",
|
||||
"Description": "Ensure that encryption is enabled for RDS Instances",
|
||||
"Checks": [
|
||||
"rds_instance_storage_encrypted"
|
||||
"rds_instance_storage_encrypted",
|
||||
"rds_instance_transport_encrypted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon RDS encrypted DB instances use the industry standard AES-256 encryption algorithm to encrypt your data on the server that hosts your Amazon RDS DB instances. After your data is encrypted, Amazon RDS handles authentication of access and decryption of your data transparently with a minimal impact on performance.",
|
||||
@@ -603,7 +611,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure that RDS database instances have the Auto Minor Version Upgrade flag enabled in order to receive automatically minor engine upgrades during the specified maintenance window. So, RDS instances can get the new features, bug fixes, and security patches for their database engines.",
|
||||
@@ -624,7 +633,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure and verify that RDS database instances provisioned in your AWS account do restrict unauthorized access in order to minimize security risks. To restrict access to any publicly accessible RDS database instance, you must disable the database Publicly Accessible flag and update the VPC security group associated with the instance.",
|
||||
@@ -645,7 +655,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.4 Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.4 Elastic File System (EFS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "EFS data should be encrypted at rest using AWS KMS (Key Management Service).",
|
||||
|
||||
@@ -303,7 +303,9 @@
|
||||
{
|
||||
"Id": "1.22",
|
||||
"Description": "Ensure access to AWSCloudShellFullAccess is restricted",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"iam_policy_cloudshell_admin_not_attached"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1. Identity and Access Management",
|
||||
@@ -474,7 +476,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "At the Amazon S3 bucket level, you can configure permissions through a bucket policy making the objects accessible only through HTTPS.",
|
||||
@@ -491,11 +494,13 @@
|
||||
"Id": "2.1.2",
|
||||
"Description": "Ensure MFA Delete is enabled on S3 buckets",
|
||||
"Checks": [
|
||||
"s3_bucket_no_mfa_delete"
|
||||
"s3_bucket_no_mfa_delete",
|
||||
"cloudtrail_bucket_requires_mfa_delete"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.",
|
||||
@@ -516,7 +521,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Amazon S3 buckets can contain sensitive data, that for security purposes should be discovered, monitored, classified and protected. Macie along with other 3rd party tools can automatically provide an inventory of Amazon S3 buckets.",
|
||||
@@ -538,7 +544,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon S3 provides `Block public access (bucket settings)` and `Block public access (account settings)` to help you manage public access to Amazon S3 resources. By default, S3 buckets and objects are created with public access disabled. However, an IAM principal with sufficient S3 permissions can enable public access at the bucket and/or object level. While enabled, `Block public access (bucket settings)` prevents an individual bucket, and its contained objects, from becoming publicly accessible. Similarly, `Block public access (account settings)` prevents all buckets, and contained objects, from becoming publicly accessible across the entire account.",
|
||||
@@ -559,7 +566,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Elastic Compute Cloud (EC2) supports encryption at rest when using the Elastic Block Store (EBS) service. While disabled by default, forcing encryption at EBS volume creation is supported.",
|
||||
@@ -576,11 +584,13 @@
|
||||
"Id": "2.3.1",
|
||||
"Description": "Ensure that encryption is enabled for RDS Instances",
|
||||
"Checks": [
|
||||
"rds_instance_storage_encrypted"
|
||||
"rds_instance_storage_encrypted",
|
||||
"rds_instance_transport_encrypted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon RDS encrypted DB instances use the industry standard AES-256 encryption algorithm to encrypt your data on the server that hosts your Amazon RDS DB instances. After your data is encrypted, Amazon RDS handles authentication of access and decryption of your data transparently with a minimal impact on performance.",
|
||||
@@ -601,7 +611,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure that RDS database instances have the Auto Minor Version Upgrade flag enabled in order to receive automatically minor engine upgrades during the specified maintenance window. So, RDS instances can get the new features, bug fixes, and security patches for their database engines.",
|
||||
@@ -622,7 +633,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure and verify that RDS database instances provisioned in your AWS account do restrict unauthorized access in order to minimize security risks. To restrict access to any publicly accessible RDS database instance, you must disable the database Publicly Accessible flag and update the VPC security group associated with the instance.",
|
||||
@@ -643,7 +655,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.4 Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.4 Elastic File System (EFS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "EFS data should be encrypted at rest using AWS KMS (Key Management Service).",
|
||||
@@ -1338,7 +1351,8 @@
|
||||
"Id": "5.6",
|
||||
"Description": "Ensure that EC2 Metadata Service only allows IMDSv2",
|
||||
"Checks": [
|
||||
"ec2_instance_imdsv2_enabled"
|
||||
"ec2_instance_imdsv2_enabled",
|
||||
"ec2_instance_account_imdsv2_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -474,7 +474,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "At the Amazon S3 bucket level, you can configure permissions through a bucket policy making the objects accessible only through HTTPS.",
|
||||
@@ -495,7 +496,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.",
|
||||
@@ -516,7 +518,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Amazon S3 buckets can contain sensitive data, that for security purposes should be discovered, monitored, classified and protected. Macie along with other 3rd party tools can automatically provide an inventory of Amazon S3 buckets.",
|
||||
@@ -538,7 +541,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1. Simple Storage Service (S3)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.1. Simple Storage Service (S3)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon S3 provides `Block public access (bucket settings)` and `Block public access (account settings)` to help you manage public access to Amazon S3 resources. By default, S3 buckets and objects are created with public access disabled. However, an IAM principal with sufficient S3 permissions can enable public access at the bucket and/or object level. While enabled, `Block public access (bucket settings)` prevents an individual bucket, and its contained objects, from becoming publicly accessible. Similarly, `Block public access (account settings)` prevents all buckets, and contained objects, from becoming publicly accessible across the entire account.",
|
||||
@@ -559,7 +563,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.2. Elastic Compute Cloud (EC2)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Elastic Compute Cloud (EC2) supports encryption at rest when using the Elastic Block Store (EBS) service. While disabled by default, forcing encryption at EBS volume creation is supported.",
|
||||
@@ -580,7 +585,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Amazon RDS encrypted DB instances use the industry standard AES-256 encryption algorithm to encrypt your data on the server that hosts your Amazon RDS DB instances. After your data is encrypted, Amazon RDS handles authentication of access and decryption of your data transparently with a minimal impact on performance.",
|
||||
@@ -601,7 +607,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure that RDS database instances have the Auto Minor Version Upgrade flag enabled in order to receive automatically minor engine upgrades during the specified maintenance window. So, RDS instances can get the new features, bug fixes, and security patches for their database engines.",
|
||||
@@ -622,7 +629,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.3. Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.3. Relational Database Service (RDS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure and verify that RDS database instances provisioned in your AWS account do restrict unauthorized access in order to minimize security risks. To restrict access to anypublicly accessible RDS database instance, you must disable the database PubliclyAccessible flag and update the VPC security group associated with the instance",
|
||||
@@ -643,7 +651,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.4 Relational Database Service (RDS)",
|
||||
"Section": "2. Storage",
|
||||
"SubSection": "2.4 Elastic File System (EFS)",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "EFS data should be encrypted at rest using AWS KMS (Key Management Service).",
|
||||
|
||||
@@ -2932,7 +2932,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "op.pl.2.aws.warch.1",
|
||||
"Id": "op.pl.2.r1.aws.warch.1",
|
||||
"Description": "Sistema de gestión",
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -2956,7 +2956,7 @@
|
||||
"Checks": []
|
||||
},
|
||||
{
|
||||
"Id": "op.pl.2.aws.warch.1",
|
||||
"Id": "op.pl.2.r2.aws.warch.1",
|
||||
"Description": "Sistema de gestión de la seguridad con mejora continua",
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -2980,7 +2980,7 @@
|
||||
"Checks": []
|
||||
},
|
||||
{
|
||||
"Id": "op.pl.2.aws.warch.1",
|
||||
"Id": "op.pl.2.r3.aws.warch.1",
|
||||
"Description": "Validación de datos",
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -4304,32 +4304,6 @@
|
||||
],
|
||||
"Checks": []
|
||||
},
|
||||
{
|
||||
"Id": "op.mon.3.aws.cwl.1",
|
||||
"Description": "Vigilancia",
|
||||
"Attributes": [
|
||||
{
|
||||
"IdGrupoControl": "op.mon.3",
|
||||
"Marco": "operacional",
|
||||
"Categoria": "monitorización del sistema",
|
||||
"DescripcionControl": "Deberá asegurarse que todos los servicios que se utilicen en la arquitectura de la aplicación desplegada en AWS estén generando logs",
|
||||
"Nivel": "alto",
|
||||
"Tipo": "requisito",
|
||||
"Dimensiones": [
|
||||
"confidencialidad",
|
||||
"integridad",
|
||||
"trazabilidad",
|
||||
"autenticidad",
|
||||
"disponibilidad"
|
||||
],
|
||||
"ModoEjecucion": "automatico",
|
||||
"Dependencias": []
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_cloudwatch_logging_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "mp.com.2.aws.vpn.1",
|
||||
"Description": "Protección de la confidencialidad",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -19,7 +19,7 @@
|
||||
"ec2_ebs_public_snapshot",
|
||||
"ec2_instance_profile_attached",
|
||||
"ec2_instance_public_ip",
|
||||
"eks_endpoints_not_publicly_accessible",
|
||||
"eks_cluster_not_publicly_accessible",
|
||||
"emr_cluster_master_nodes_no_public_ip",
|
||||
"iam_aws_attached_policy_no_administrative_privileges",
|
||||
"iam_customer_attached_policy_no_administrative_privileges",
|
||||
@@ -61,7 +61,7 @@
|
||||
"ec2_ebs_public_snapshot",
|
||||
"ec2_instance_profile_attached",
|
||||
"ec2_instance_public_ip",
|
||||
"eks_endpoints_not_publicly_accessible",
|
||||
"eks_cluster_not_publicly_accessible",
|
||||
"emr_cluster_master_nodes_no_public_ip",
|
||||
"iam_aws_attached_policy_no_administrative_privileges",
|
||||
"iam_customer_attached_policy_no_administrative_privileges",
|
||||
@@ -102,7 +102,7 @@
|
||||
"Checks": [
|
||||
"ec2_ebs_public_snapshot",
|
||||
"ec2_instance_public_ip",
|
||||
"eks_endpoints_not_publicly_accessible",
|
||||
"eks_cluster_not_publicly_accessible",
|
||||
"emr_cluster_master_nodes_no_public_ip",
|
||||
"awslambda_function_not_publicly_accessible",
|
||||
"awslambda_function_url_public",
|
||||
|
||||
@@ -971,7 +971,7 @@
|
||||
"Checks": [
|
||||
"ec2_ebs_public_snapshot",
|
||||
"ec2_instance_public_ip",
|
||||
"eks_endpoints_not_publicly_accessible",
|
||||
"eks_cluster_not_publicly_accessible",
|
||||
"emr_cluster_master_nodes_no_public_ip",
|
||||
"awslambda_function_url_public",
|
||||
"rds_instance_no_public_access",
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Security defaults in Azure Active Directory (Azure AD) make it easier to be secure and help protect your organization. Security defaults contain preconfigured security settings for common attacks. Security defaults is available to everyone. The goal is to ensure that all organizations have a basic level of security",
|
||||
@@ -34,7 +35,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable multi-factor authentication for all roles, groups, and users that have write access or permissions to Azure resources. These include custom created objects or built-in roles such as; • Service Co-Administrators • Subscription Owners • Contributors",
|
||||
@@ -56,7 +58,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable multi-factor authentication for all non-privileged users.",
|
||||
@@ -76,7 +79,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Do not allow users to remember multi-factor authentication on devices.",
|
||||
@@ -98,7 +102,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Azure Active Directory Conditional Access allows an organization to configure Named locations and configure whether those locations are trusted or untrusted. These settings provide organizations the means to specify Geographical locations for use in conditional access policies, or define actual IP addresses and IP ranges and whether or not those IP addresses and/or ranges are trusted by the organization.",
|
||||
@@ -118,7 +123,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "CAUTION: If these policies are created without first auditing and testing the result, misconfiguration can potentially lock out administrators or create undesired access issues. Conditional Access Policies can be used to block access from geographic locations that are deemed out-of-scope for your organization or application. The scope and variables for this policy should be carefully examined and defined.",
|
||||
@@ -138,7 +144,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on login.",
|
||||
@@ -158,7 +165,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on logins.",
|
||||
@@ -178,7 +186,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on login.",
|
||||
@@ -198,7 +207,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on logins.",
|
||||
@@ -220,7 +230,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Require administrators or appropriately delegated users to create new tenants.",
|
||||
@@ -240,7 +250,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "This recommendation extends guest access review by utilizing the Azure AD Privileged Identity Management feature provided in Azure AD Premium P2. Azure AD is extended to include Azure AD B2B collaboration, allowing you to invite people from outside your organization to be guest users in your cloud account and sign in with their own work, school, or social identities. Guest users allow you to share your company's applications and services with users from any other organization, while maintaining control over your own corporate data. Work with external partners, large or small, even if they don't have Azure AD or an IT department. A simple invitation and redemption process lets partners use their own credentials to access your company's resources a a guest user.",
|
||||
@@ -260,7 +270,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Azure AD is extended to include Azure AD B2B collaboration, allowing you to invite people from outside your organization to be guest users in your cloud account and sign in with their own work, school, or social identities. Guest users allow you to share your company's applications and services with users from any other organization, while maintaining control over your own corporate data. Work with external partners, large or small, even if they don't have Azure AD or an IT department. A simple invitation and redemption process lets partners use their own credentials to access your company's resources as a guest user. Guest users in every subscription should be review on a regular basis to ensure that inactive and unneeded accounts are removed.",
|
||||
@@ -280,7 +290,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensures that two alternate forms of identification are provided before allowing a password reset.",
|
||||
@@ -300,7 +310,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Azure provides a Global Banned Password policy that applies to Azure administrative and normal user accounts. This is not applied to user accounts that are synced from an on-premise Active Directory unless Azure AD Connect is used and you enable EnforceCloudPasswordPolicyForPasswordSyncedUsers. Please see the list in default values on the specifics of this policy. To further password security, it is recommended to further define a custom banned password policy.",
|
||||
@@ -320,7 +330,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensure that the number of days before users are asked to re-confirm their authentication information is not set to 0.",
|
||||
@@ -340,7 +350,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensure that users are notified on their primary and secondary emails on password resets.",
|
||||
@@ -360,7 +370,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensure that all Global Administrators are notified if any other administrator resets their password.",
|
||||
@@ -382,7 +392,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Require administrators to provide consent for applications before use.",
|
||||
@@ -404,7 +414,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Allow users to provide consent for selected permissions when a request is coming from a verified publisher.",
|
||||
@@ -424,7 +434,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Require administrators to provide consent for the apps before use.",
|
||||
@@ -446,7 +456,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Require administrators or appropriately delegated users to register third-party applications.",
|
||||
@@ -468,7 +478,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Limit guest user permissions.",
|
||||
@@ -490,7 +500,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Restrict invitations to users with specific administrative roles only.",
|
||||
@@ -510,7 +520,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Restrict access to the Azure AD administration portal to administrators only. NOTE: This only affects access to the Azure AD administrator's web portal. This setting does not prohibit privileged users from using other methods such as Rest API or Powershell to obtain sensitive information from Azure AD.",
|
||||
@@ -530,7 +540,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Restricts group creation to administrators with permissions only.",
|
||||
@@ -552,7 +562,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Restrict security group creation to administrators only.",
|
||||
@@ -572,7 +582,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Restrict security group management to administrators only.",
|
||||
@@ -594,7 +604,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Restrict Microsoft 365 group creation to administrators only.",
|
||||
@@ -614,7 +624,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Joining or registering devices to the active directory should require Multi-factor authentication.",
|
||||
@@ -636,7 +646,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The principle of least privilege should be followed and only necessary privileges should be assigned instead of allowing full administrative access.",
|
||||
@@ -658,7 +668,7 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Resource locking is a powerful protection mechanism that can prevent inadvertent modification/deletion of resources within Azure subscriptions/Resource Groups and is a recommended NIST configuration.",
|
||||
@@ -678,7 +688,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1. Identity and Access Management",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Users who are set as subscription owners are able to make administrative changes to the subscriptions and move them into and out of Azure Active Directories.",
|
||||
@@ -700,7 +710,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Servers enables threat detection for Servers, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -722,7 +733,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for App Service enables threat detection for App Service, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -744,7 +756,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Databases enables threat detection for the instances running your database software. This provides threat intelligence, anomaly detection, and behavior analytics in the Azure Microsoft Defender for Cloud. Instead of being enabled on services like Platform as a Service (PaaS), this implementation will run within your instances as Infrastructure as a Service (IaaS) on the Operating Systems hosting your databases.",
|
||||
@@ -766,7 +779,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Azure SQL Databases enables threat detection for Azure SQL database servers, providing threat intelligence, anomaly detection, andbehavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -788,7 +802,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for SQL servers on machines enables threat detection for SQL servers on machines, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -810,7 +825,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Open-source relational databases enables threat detection for Open-source relational databases, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -832,7 +848,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Storage enables threat detection for Storage, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -854,7 +871,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Containers enables threat detection for Container Registries including Kubernetes, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -876,7 +894,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Defender for Azure Cosmos DB scans all incoming network requests for threats to your Azure Cosmos DB resources.",
|
||||
@@ -898,7 +917,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Turning on Microsoft Defender for Key Vault enables threat detection for Key Vault, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -920,7 +940,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Defender for DNS scans all network traffic exiting from within a subscription.",
|
||||
@@ -942,7 +963,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Defender for Resource Manager scans incoming administrative requests to change your infrastructure from both CLI and the Azure portal.",
|
||||
@@ -964,7 +986,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensure that the latest OS patches for all virtual machines are applied.",
|
||||
@@ -986,7 +1009,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "None of the settings offered by ASC Default policy should be set to effect Disabled.",
|
||||
@@ -1008,7 +1032,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable automatic provisioning of the monitoring agent to collect security data.",
|
||||
@@ -1030,7 +1055,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable automatic provisioning of vulnerability assessment for machines on both Azure and hybrid (Arc enabled) machines.",
|
||||
@@ -1050,7 +1076,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable automatic provisioning of the Microsoft Defender for Containers components.",
|
||||
@@ -1072,7 +1099,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable security alert emails to subscription owners.",
|
||||
@@ -1094,7 +1122,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Microsoft Defender for Cloud emails the subscription owners whenever a high-severity alert is triggered for their subscription. You should provide a security contact email address as an additional email address.",
|
||||
@@ -1116,7 +1145,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enables emailing security alerts to the subscription owner or other designated security contact.",
|
||||
@@ -1138,7 +1168,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "This integration setting enables Microsoft Defender for Cloud Apps (formerly 'Microsoft Cloud App Security' or 'MCAS' - see additional info) to communicate with Microsoft Defender for Cloud.",
|
||||
@@ -1160,7 +1191,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "This integration setting enables Microsoft Defender for Endpoint (formerly 'Advanced Threat Protection' or 'ATP' or 'WDATP' - see additional info) to communicate with Microsoft Defender for Cloud. IMPORTANT: When enabling integration between DfE & DfC it needs to be taken into account that this will have some side effects that may be undesirable. 1. For server 2019 & above if defender is installed (default for these server SKU's) this will trigger a deployment of the new unified agent and link to any of the extended configuration in the Defender portal. 2. If the new unified agent is required for server SKU's of Win 2016 or Linux and lower there is additional integration that needs to be switched on and agents need to be aligned.",
|
||||
@@ -1182,7 +1214,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.2 Microsoft Defender for IoT",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.2 Microsoft Defender for IoT",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Defender for IoT acts as a central security hub for IoT devices within your organization.",
|
||||
@@ -1524,7 +1557,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable auditing on SQL Servers.",
|
||||
@@ -1546,7 +1580,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure that no SQL Databases allow ingress from 0.0.0.0/0 (ANY IP).",
|
||||
@@ -1568,7 +1603,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Transparent Data Encryption (TDE) with Customer-managed key support provides increased transparency and control over the TDE Protector, increased security with an HSM-backed external service, and promotion of separation of duties. With TDE, data is encrypted at rest with a symmetric key (called the database encryption key) stored in the database or data warehouse distribution. To protect this data encryption key (DEK) in the past, only a certificate that the Azure SQL Service managed could be used. Now, with Customer-managed key support for TDE, the DEK can be protected with an asymmetric key that is stored in the Azure Key Vault. The Azure Key Vault is a highly available and scalable cloud-based key store which offers central key management, leverages FIPS 140-2 Level 2 validated hardware security modules (HSMs), and allows separation of management of keys and data for additional security. Based on business needs or criticality of data/databases hosted on a SQL server, it is recommended that the TDE protector is encrypted by a key that is managed by the data owner (Customer-managed key).",
|
||||
@@ -1590,7 +1626,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Use Azure Active Directory Authentication for authentication with SQL Database to manage credentials in a single place.",
|
||||
@@ -1612,7 +1649,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable Transparent Data Encryption on every SQL server.",
|
||||
@@ -1634,7 +1672,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "SQL Server Audit Retention should be configured to be greater than 90 days.",
|
||||
@@ -1656,7 +1695,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable 'Microsoft Defender for SQL' on critical SQL Servers.",
|
||||
@@ -1678,7 +1718,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable Vulnerability Assessment (VA) service scans for critical SQL servers and corresponding SQL databases.",
|
||||
@@ -1700,7 +1741,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable Vulnerability Assessment (VA) service scans for critical SQL servers and corresponding SQL databases.",
|
||||
@@ -1722,7 +1764,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Configure 'Send scan reports to' with email addresses of concerned data owners/stakeholders for a critical SQL servers",
|
||||
@@ -1744,7 +1787,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.2 SQL Server - Microsoft Defender for SQL",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable Vulnerability Assessment (VA) setting 'Also send email notifications to admins and subscription owners'.",
|
||||
@@ -1766,7 +1810,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable SSL connection on PostgreSQL Servers.",
|
||||
@@ -1788,7 +1833,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable log_checkpoints on PostgreSQL Servers.",
|
||||
@@ -1810,7 +1856,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable log_connections on PostgreSQL Servers.",
|
||||
@@ -1832,7 +1879,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable log_disconnections on PostgreSQL Servers.",
|
||||
@@ -1854,7 +1902,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable connection_throttling on PostgreSQL Servers.",
|
||||
@@ -1876,7 +1925,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure log_retention_days on PostgreSQL Servers is set to an appropriate value.",
|
||||
@@ -1898,7 +1948,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Disable access from Azure services to PostgreSQL Database Server.",
|
||||
@@ -1918,7 +1969,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Azure Database for PostgreSQL servers should be created with 'infrastructure double encryption' enabled.",
|
||||
@@ -1940,7 +1992,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable SSL connection on MYSQL Servers.",
|
||||
@@ -1962,7 +2015,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure TLS version on MySQL flexible servers is set to the default value.",
|
||||
@@ -1984,7 +2038,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable audit_log_enabled on MySQL Servers.",
|
||||
@@ -2006,7 +2061,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Set audit_log_enabled to include CONNECTION on MySQL Servers.",
|
||||
@@ -2028,7 +2084,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.5 Cosmos DB",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.5 Cosmos DB",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Limiting your Cosmos DB to only communicate on whitelisted networks lowers its attack footprint.",
|
||||
@@ -2050,7 +2107,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.5 Cosmos DB",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.5 Cosmos DB",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Private endpoints limit network traffic to approved sources.",
|
||||
@@ -2072,7 +2130,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.5 Cosmos DB",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.5 Cosmos DB",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Cosmos DB can use tokens or AAD for client authentication which in turn will use Azure RBAC for authorization. Using AAD is significantly more secure because AAD handles the credentials and allows for MFA and centralized management, and the Azure RBAC better integrated with the rest of Azure.",
|
||||
@@ -2094,7 +2153,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable Diagnostic settings for exporting activity logs. Diagnos tic settings are available for each individual resource within a subscription. Settings should be configured for allappropriate resources for your environment.",
|
||||
@@ -2116,7 +2176,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Prerequisite: A Diagnostic Setting must exist. If a Diagnostic Setting does not exist, the navigation and options within this recommendation will not be available. Please review the recommendation at the beginning of this subsection titled: 'Ensure that a 'Diagnostic Setting' exists.' The diagnostic setting should be configured to log the appropriate activities from the control/management plane.",
|
||||
@@ -2138,7 +2199,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The storage account container containing the activity log export should not be publicly accessible.",
|
||||
@@ -2160,7 +2222,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Storage accounts with the activity log exports can be configured to use Customer Managed Keys (CMK).",
|
||||
@@ -2182,7 +2245,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable AuditEvent logging for key vault instances to ensure interactions with key vaults are logged and available.",
|
||||
@@ -2204,7 +2268,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensure that network flow logs are captured and fed into a central log analytics workspace.",
|
||||
@@ -2226,7 +2291,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable AppServiceHTTPLogs diagnostic log category for Azure App Service instances to ensure all http requests are captured and centrally logged.",
|
||||
@@ -2248,7 +2314,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create Policy Assignment event.",
|
||||
@@ -2270,7 +2337,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Policy Assignment event.",
|
||||
@@ -2292,7 +2360,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an Activity Log Alert for the Create or Update Network Security Group event.",
|
||||
@@ -2314,7 +2383,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Network Security Group event.",
|
||||
@@ -2336,7 +2406,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create or Update Security Solution event.",
|
||||
@@ -2358,7 +2429,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Security Solution event.",
|
||||
@@ -2380,7 +2452,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create or Update SQL Server Firewall Rule event.",
|
||||
@@ -2402,7 +2475,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the 'Delete SQL Server Firewall Rule.'",
|
||||
@@ -2424,7 +2498,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create or Update Public IP Addresses rule.",
|
||||
@@ -2446,7 +2521,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Public IP Address rule.",
|
||||
@@ -2466,7 +2542,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.3 Configuring Application Insights",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Resource Logs capture activity to the data access plane while the Activity log is a subscription-level log for the control plane. Resource-level diagnostic logs provide insight into operations that were performed within that resource itself; for example, reading or updating a secret from a Key Vault. Currently, 95 Azure resources support Azure Monitoring (See the more information section for a complete list), including Network Security Groups, Load Balancers, Key Vault, AD, Logic Apps, and CosmosDB. The content of these logs varies by resource type. A number of back-end services were not configured to log and store Resource Logs for certain activities or for a sufficient length. It is crucial that monitoring is correctly configured to log all relevant activities and retain those logs for a sufficient length of time. Given that the mean time to detection in an enterprise is 240 days, a minimum retention period of two years is recommended.",
|
||||
@@ -2486,7 +2562,7 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.3 Configuring Application Insights",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The use of Basic or Free SKUs in Azure whilst cost effective have significant limitations in terms of what can be monitored and what support can be realized from Microsoft. Typically, these SKU’s do not have a service SLA and Microsoft will usually refuse to provide support for them. Consequently Basic/Free SKUs should never be used for production workloads.",
|
||||
@@ -2508,7 +2584,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.3 Configuring Application Insights",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.3 Configuring Application Insights",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Application Insights within Azure act as an Application Performance Monitoring solution providing valuable data into how well an application performs and additional information when performing incident response. The types of log data collected include application metrics, telemetry data, and application trace logging data providing organizations with detailed information about application activity and application transactions. Both data sets help organizations adopt a proactive and retroactive means to handle security and performance related metrics within their modern applications.",
|
||||
|
||||
@@ -494,7 +494,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults Security Defaults",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults Security Defaults",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Security defaults in Microsoft Entra ID make it easier to be secure and help protect your organization. Security defaults contain preconfigured security settings for common attacks. Security defaults is available to everyone. The goal is to ensure that all organizations have a basic level of security enabled at no extra cost. You may turn on security defaults in the Azure portal.",
|
||||
@@ -516,7 +517,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults Security Defaults",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults Security Defaults",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable multi-factor authentication for all roles, groups, and users that have write access or permissions to Azure resources. These include custom created objects or built-in roles such as; - Service Co-Administrators - Subscription Owners - Contributors",
|
||||
@@ -538,7 +540,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults Security Defaults",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable multi-factor authentication for all non-privileged users.",
|
||||
@@ -558,7 +561,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.1 Security Defaults",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.1 Security Defaults Security Defaults",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Do not allow users to remember multi-factor authentication on devices.",
|
||||
@@ -580,7 +584,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Entra ID Conditional Access allows an organization to configure `Named locations` and configure whether those locations are trusted or untrusted. These settings provide organizations the means to specify Geographical locations for use in conditional access policies, or define actual IP addresses and IP ranges and whether or not those IP addresses and/or ranges are trusted by the organization.",
|
||||
@@ -600,7 +605,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "**CAUTION**: If these policies are created without first auditing and testing the result, misconfiguration can potentially lock out administrators or create undesired access issues. Conditional Access Policies can be used to block access from geographic locations that are deemed out-of-scope for your organization or application. The scope and variables for this policy should be carefully examined and defined.",
|
||||
@@ -620,7 +626,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on login.",
|
||||
@@ -640,7 +647,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on logins.",
|
||||
@@ -660,7 +668,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "For designated users, they will be prompted to use their multi-factor authentication (MFA) process on login.",
|
||||
@@ -682,7 +691,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "This recommendation ensures that users accessing the Windows Azure Service Management API (i.e. Azure Powershell, Azure CLI, Azure Resource Manager API, etc.) are required to use multifactor authentication (MFA) credentials when accessing resources through the Windows Azure Service Management API.",
|
||||
@@ -702,7 +712,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "1.2 Conditional Access",
|
||||
"Section": "1.Identity and Access Management",
|
||||
"SubSection": "1.2 Conditional Access",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "This recommendation ensures that users accessing Microsoft Admin Portals (i.e. Microsoft 365 Admin, Microsoft 365 Defender, Exchange Admin Center, Azure Portal, etc.) are required to use multifactor authentication (MFA) credentials when logging into an Admin Portal.",
|
||||
@@ -724,7 +735,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for Servers enables threat detection for Servers, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -746,7 +758,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for App Service enables threat detection for App Service, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -768,7 +781,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for Azure SQL Databases enables threat detection for Managed Instance Azure SQL databases, providing threat intelligence, anomaly detection, and behavior analytics in Microsoft Defender for Cloud.",
|
||||
@@ -790,7 +804,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for SQL servers on machines enables threat detection for SQL servers on machines, providing threat intelligence, anomaly detection, and behavior analytics in Microsoft Defender for Cloud.",
|
||||
@@ -812,7 +827,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for Open-source relational databases enables threat detection for Open-source relational databases, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -834,7 +850,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Microsoft Defender for Azure Cosmos DB scans all incoming network requests for threats to your Azure Cosmos DB resources.",
|
||||
@@ -856,7 +873,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for Storage enables threat detection for Storage, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -878,7 +896,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for Containers enables threat detection for Container Registries including Kubernetes, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud. The following services will be enabled for container instances: - Defender agent in Azure - Azure Policy for Kubernetes - Agentless discovery for Kubernetes - Agentless container vulnerability assessment",
|
||||
@@ -900,7 +919,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Turning on Microsoft Defender for Key Vault enables threat detection for Key Vault, providing threat intelligence, anomaly detection, and behavior analytics in the Microsoft Defender for Cloud.",
|
||||
@@ -922,7 +942,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "[**NOTE:** As of August 1, customers with an existing subscription to Defender for DNS can continue to use the service, but new subscribers will receive alerts about suspicious DNS activity as part of Defender for Servers P2.] Microsoft Defender for DNS scans all network traffic exiting from within a subscription.",
|
||||
@@ -944,7 +965,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Microsoft Defender for Resource Manager scans incoming administrative requests to change your infrastructure from both CLI and the Azure portal.",
|
||||
@@ -966,7 +988,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure that the latest OS patches for all virtual machines are applied.",
|
||||
@@ -988,7 +1011,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "The Microsoft Cloud Security Benchmark (or MCSB) is an Azure Policy Initiative containing many security policies to evaluate resource configuration against best practice recommendations. If a policy in the MCSB is set with effect type `Disabled`, it is not evaluated and may prevent administrators from being informed of valuable security recommendations.",
|
||||
@@ -1010,7 +1034,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable automatic provisioning of the monitoring agent to collect security data.",
|
||||
@@ -1032,7 +1057,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable automatic provisioning of vulnerability assessment for machines on both Azure and hybrid (Arc enabled) machines.",
|
||||
@@ -1052,7 +1078,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable automatic provisioning of the Microsoft Defender for Containers components.",
|
||||
@@ -1074,7 +1101,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable security alert emails to subscription owners.",
|
||||
@@ -1096,7 +1124,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Microsoft Defender for Cloud emails the subscription owners whenever a high-severity alert is triggered for their subscription. You should provide a security contact email address as an additional email address.",
|
||||
@@ -1118,7 +1147,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enables emailing security alerts to the subscription owner or other designated security contact.",
|
||||
@@ -1140,7 +1170,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "This integration setting enables Microsoft Defender for Cloud Apps (formerly 'Microsoft Cloud App Security' or 'MCAS' - see additional info) to communicate with Microsoft Defender for Cloud.",
|
||||
@@ -1162,7 +1193,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "This integration setting enables Microsoft Defender for Endpoint (formerly 'Advanced Threat Protection' or 'ATP' or 'WDATP' - see additional info) to communicate with Microsoft Defender for Cloud. **IMPORTANT:** When enabling integration between DfE & DfC it needs to be taken into account that this will have some side effects that may be undesirable. 1. For server 2019 & above if defender is installed (default for these server SKU's) this will trigger a deployment of the new unified agent and link to any of the extended configuration in the Defender portal. 1. If the new unified agent is required for server SKU's of Win 2016 or Linux and lower there is additional integration that needs to be switched on and agents need to be aligned.",
|
||||
@@ -1182,7 +1214,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.1 Microsoft Defender for Cloud",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.1 Microsoft Defender for Cloud",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "An organization's attack surface is the collection of assets with a public network identifier or URI that an external threat actor can see or access from outside your cloud. It is the set of points on the boundary of a system, a system element, system component, or an environment where an attacker can try to enter, cause an effect on, or extract data from, that system, system element, system component, or environment. The larger the attack surface, the harder it is to protect. This tool can be configured to scan your organization's online infrastructure such as specified domains, hosts, CIDR blocks, and SSL certificates, and store them in an Inventory. Inventory items can be added, reviewed, approved, and removed, and may contain enrichments (insights) and additional information collected from the tool's different scan engines and open-source intelligence sources. A Defender EASM workspace will generate an Inventory of publicly exposed assets by crawling and scanning the internet using _Seeds_ you provide when setting up the tool. Seeds can be FQDNs, IP CIDR blocks, and WHOIS records. Defender EASM will generate Insights within 24-48 hours after Seeds are provided, and these insights include vulnerability data (CVEs), ports and protocols, and weak or expired SSL certificates that could be used by an attacker for reconnaisance or exploitation. Results are classified High/Medium/Low and some of them include proposed mitigations.",
|
||||
@@ -1204,7 +1237,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2.2 Microsoft Defender for IoT",
|
||||
"Section": "2. Microsoft Defender",
|
||||
"SubSection": "2.2 Microsoft Defender for IoT",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Microsoft Defender for IoT acts as a central security hub for IoT devices within your organization.",
|
||||
@@ -1586,7 +1620,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable auditing on SQL Servers.",
|
||||
@@ -1608,7 +1643,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure that no SQL Databases allow ingress from 0.0.0.0/0 (ANY IP).",
|
||||
@@ -1630,7 +1666,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Transparent Data Encryption (TDE) with Customer-managed key support provides increased transparency and control over the TDE Protector, increased security with an HSM-backed external service, and promotion of separation of duties. With TDE, data is encrypted at rest with a symmetric key (called the database encryption key) stored in the database or data warehouse distribution. To protect this data encryption key (DEK) in the past, only a certificate that the Azure SQL Service managed could be used. Now, with Customer-managed key support for TDE, the DEK can be protected with an asymmetric key that is stored in the Azure Key Vault. The Azure Key Vault is a highly available and scalable cloud-based key store which offers central key management, leverages FIPS 140-2 Level 2 validated hardware security modules (HSMs), and allows separation of management of keys and data for additional security. Based on business needs or criticality of data/databases hosted on a SQL server, it is recommended that the TDE protector is encrypted by a key that is managed by the data owner (Customer-managed key).",
|
||||
@@ -1652,7 +1689,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Use Microsoft Entra authentication for authentication with SQL Database to manage credentials in a single place.",
|
||||
@@ -1674,7 +1712,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable Transparent Data Encryption on every SQL server.",
|
||||
@@ -1696,7 +1735,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.1 SQL Server - Auditing",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.1 SQL Server - Auditing",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "SQL Server Audit Retention should be configured to be greater than 90 days.",
|
||||
@@ -1718,7 +1758,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable `SSL connection` on `PostgreSQL` Servers.",
|
||||
@@ -1740,7 +1781,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable `log_checkpoints` on `PostgreSQL Servers`.",
|
||||
@@ -1762,7 +1804,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable `log_connections` on `PostgreSQL Servers`.",
|
||||
@@ -1784,7 +1827,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable `log_disconnections` on `PostgreSQL Servers`.",
|
||||
@@ -1806,7 +1850,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable `connection_throttling` on `PostgreSQL Servers`.",
|
||||
@@ -1828,7 +1873,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure `log_retention_days` on `PostgreSQL Servers` is set to an appropriate value.",
|
||||
@@ -1850,7 +1896,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Disable access from Azure services to PostgreSQL Database Server.",
|
||||
@@ -1870,7 +1917,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.3 PostgreSQL Database Server. Storage Accounts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Azure Database for PostgreSQL servers should be created with 'infrastructure double encryption' enabled.",
|
||||
@@ -1892,7 +1940,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable `SSL connection` on `MYSQL` Servers.",
|
||||
@@ -1914,7 +1963,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure `TLS version` on `MySQL flexible` servers is set to use TLS version 1.2 or higher.",
|
||||
@@ -1936,7 +1986,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable audit_log_enabled on MySQL Servers.",
|
||||
@@ -1958,7 +2009,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.4 MySQL Database",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.4 MySQL Database",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Set `audit_log_enabled` to include CONNECTION on MySQL Servers.",
|
||||
@@ -1980,7 +2032,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.5 Cosmos DB",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.5 Cosmos DB",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Limiting your Cosmos DB to only communicate on whitelisted networks lowers its attack footprint.",
|
||||
@@ -2002,7 +2055,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.5 Cosmos DB",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.5 Cosmos DB",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Private endpoints limit network traffic to approved sources.",
|
||||
@@ -2024,7 +2078,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4.5 Cosmos DB",
|
||||
"Section": "4. Database Services",
|
||||
"SubSection": "4.5 Cosmos DB",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Cosmos DB can use tokens or Entra ID for client authentication which in turn will use Azure RBAC for authorization. Using Entra ID is significantly more secure because Entra ID handles the credentials and allows for MFA and centralized management, and the Azure RBAC better integrated with the rest of Azure.",
|
||||
@@ -2086,7 +2141,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable Diagnostic settings for exporting activity logs. Diagnostic settings are available for each individual resource within a subscription. Settings should be configured for all appropriate resources for your environment.",
|
||||
@@ -2108,7 +2164,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "**Prerequisite**: A Diagnostic Setting must exist. If a Diagnostic Setting does not exist, the navigation and options within this recommendation will not be available. Please review the recommendation at the beginning of this subsection titled: Ensure that a 'Diagnostic Setting' exists. The diagnostic setting should be configured to log the appropriate activities from the control/management plane.",
|
||||
@@ -2130,7 +2187,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Storage accounts with the activity log exports can be configured to use Customer Managed Keys (CMK).",
|
||||
@@ -2152,7 +2210,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enable AuditEvent logging for key vault instances to ensure interactions with key vaults are logged and available.",
|
||||
@@ -2174,7 +2233,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Ensure that network flow logs are captured and fed into a central log analytics workspace.",
|
||||
@@ -2196,7 +2256,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.1 Configuring Diagnostic Settings",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.1 Configuring Diagnostic Settings",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "Enable AppServiceHTTPLogs diagnostic log category for Azure App Service instances to ensure all http requests are captured and centrally logged.",
|
||||
@@ -2218,7 +2279,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create Policy Assignment event.",
|
||||
@@ -2240,7 +2302,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Policy Assignment event.",
|
||||
@@ -2262,7 +2325,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an Activity Log Alert for the Create or Update Network Security Group event.",
|
||||
@@ -2284,7 +2348,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Network Security Group event.",
|
||||
@@ -2306,7 +2371,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create or Update Security Solution event.",
|
||||
@@ -2328,7 +2394,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Security Solution event.",
|
||||
@@ -2350,7 +2417,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create or Update SQL Server Firewall Rule event.",
|
||||
@@ -2372,7 +2440,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete SQL Server Firewall Rule.",
|
||||
@@ -2394,7 +2463,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Create or Update Public IP Addresses rule.",
|
||||
@@ -2416,7 +2486,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.2 Monitoring using Activity Log Alerts",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Create an activity log alert for the Delete Public IP Address rule.",
|
||||
@@ -2438,7 +2509,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5.3 Configuring Application Insights. Storage Accounts",
|
||||
"Section": "5. Logging and Monitoring",
|
||||
"SubSection": "5.3 Configuring Application Insights. Storage Accounts",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Application Insights within Azure act as an Application Performance Monitoring solution providing valuable data into how well an application performs and additional information when performing incident response. The types of log data collected include application metrics, telemetry data, and application trace logging data providing organizations with detailed information about application activity and application transactions. Both data sets help organizations adopt a proactive and retroactive means to handle security and performance related metrics within their modern applications.",
|
||||
@@ -3044,7 +3116,7 @@
|
||||
"Id": "9.4",
|
||||
"Description": "Ensure that Register with Entra ID is enabled on App Service",
|
||||
"Checks": [
|
||||
""
|
||||
"app_register_with_identity"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -3175,9 +3247,7 @@
|
||||
{
|
||||
"Id": "9.10",
|
||||
"Description": "Ensure Azure Key Vaults are Used to Store Secrets",
|
||||
"Checks": [
|
||||
""
|
||||
],
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "9. AppService",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1292,7 +1292,8 @@
|
||||
"Checks": [],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.1. MySQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.1. MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Manual",
|
||||
"Description": "It is recommended to set a password for the administrative user (`root` by default) to prevent unauthorized access to the SQL database instances. This recommendation is applicable only for MySQL Instances. PostgreSQL does not offer any setting for No Password from the cloud console.",
|
||||
@@ -1313,7 +1314,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.1. MySQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.1. MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set `skip_show_database` database flag for Cloud SQL Mysql instance to `on`",
|
||||
@@ -1334,7 +1336,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.1. MySQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.1. MySQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set the `local_infile` database flag for a Cloud SQL MySQL instance to `off`.",
|
||||
@@ -1355,7 +1358,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The `log_error_verbosity` flag controls the verbosity/details of messages logged. Valid values are: - `TERSE` - `DEFAULT` - `VERBOSE` `TERSE` excludes the logging of `DETAIL`, `HINT`, `QUERY`, and `CONTEXT` error information. `VERBOSE` output includes the `SQLSTATE` error code, source code file name, function name, and line number that generated the error. Ensure an appropriate value is set to 'DEFAULT' or stricter.",
|
||||
@@ -1376,7 +1380,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The `log_min_error_statement` flag defines the minimum message severity level that are considered as an error statement. Messages for error statements are logged with the SQL statement. Valid values include `DEBUG5`, `DEBUG4`, `DEBUG3`, `DEBUG2`, `DEBUG1`, `INFO`, `NOTICE`, `WARNING`, `ERROR`, `LOG`, `FATAL`, and `PANIC`. Each severity level includes the subsequent levels mentioned above. Ensure a value of `ERROR` or stricter is set.",
|
||||
@@ -1397,7 +1402,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 2",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The value of `log_statement` flag determined the SQL statements that are logged. Valid values are: - `none` - `ddl` - `mod` - `all` The value `ddl` logs all data definition statements. The value `mod` logs all ddl statements, plus data-modifying statements. The statements are logged after a basic parsing is done and statement type is determined, thus this does not logs statements with errors. When using extended query protocol, logging occurs after an Execute message is received and values of the Bind parameters are included. A value of 'ddl' is recommended unless otherwise directed by your organization's logging policy.",
|
||||
@@ -1418,7 +1424,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Instance addresses can be public IP or private IP. Public IP means that the instance is accessible through the public internet. In contrast, instances using only private IP are not accessible through the public internet, but are accessible through a Virtual Private Cloud (VPC). Limiting network access to your database will limit potential attacks.",
|
||||
@@ -1439,7 +1446,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Ensure `cloudsql.enable_pgaudit` database flag for Cloud SQL PostgreSQL instance is set to `on` to allow for centralized logging.",
|
||||
@@ -1460,7 +1468,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enabling the `log_connections` setting causes each attempted connection to the server to be logged, along with successful completion of client authentication. This parameter cannot be changed after the session starts.",
|
||||
@@ -1481,7 +1490,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "Enabling the `log_disconnections` setting logs the end of each session, including the session duration.",
|
||||
@@ -1502,7 +1512,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The `log_min_duration_statement` flag defines the minimum amount of execution time of a statement in milliseconds where the total duration of the statement is logged. Ensure that `log_min_duration_statement` is disabled, i.e., a value of `-1` is set.",
|
||||
@@ -1523,7 +1534,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.2. PostgreSQL Database",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.2. PostgreSQL Database",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "The `log_min_messages` flag defines the minimum message severity level that is considered as an error statement. Messages for error statements are logged with the SQL statement. Valid values include `DEBUG5`, `DEBUG4`, `DEBUG3`, `DEBUG2`, `DEBUG1`, `INFO`, `NOTICE`, `WARNING`, `ERROR`, `LOG`, `FATAL`, and `PANIC`. Each severity level includes the subsequent levels mentioned above. ERROR is considered the best practice setting. Changes should only be made in accordance with the organization's logging policy.",
|
||||
@@ -1544,7 +1556,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set `3625 (trace flag)` database flag for Cloud SQL SQL Server instance to `on`.",
|
||||
@@ -1565,7 +1578,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set `external scripts enabled` database flag for Cloud SQL SQL Server instance to `off`",
|
||||
@@ -1586,7 +1600,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set `remote access` database flag for Cloud SQL SQL Server instance to `off`.",
|
||||
@@ -1607,7 +1622,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to check the `user connections` for a Cloud SQL SQL Server instance to ensure that it is not artificially limiting connections.",
|
||||
@@ -1628,7 +1644,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended that, `user options` database flag for Cloud SQL SQL Server instance should not be configured.",
|
||||
@@ -1649,7 +1666,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set `contained database authentication` database flag for Cloud SQL on the SQL Server instance to `off`.",
|
||||
@@ -1670,7 +1688,8 @@
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6.3. SQL Server",
|
||||
"Section": "6. Cloud SQL Database Services",
|
||||
"SubSection": "6.3. SQL Server",
|
||||
"Profile": "Level 1",
|
||||
"AssessmentStatus": "Automated",
|
||||
"Description": "It is recommended to set `cross db ownership chaining` database flag for Cloud SQL SQL Server instance to `off`.",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import pathlib
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
from os import getcwd
|
||||
|
||||
import requests
|
||||
@@ -11,7 +12,7 @@ from prowler.lib.logger import logger
|
||||
|
||||
timestamp = datetime.today()
|
||||
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
|
||||
prowler_version = "4.4.0"
|
||||
prowler_version = "4.6.3"
|
||||
html_logo_url = "https://github.com/prowler-cloud/prowler/"
|
||||
square_logo_img = "https://prowler.com/wp-content/uploads/logo-html.png"
|
||||
aws_logo = "https://user-images.githubusercontent.com/38561120/235953920-3e3fba08-0795-41dc-b480-9bea57db9f2e.png"
|
||||
@@ -21,8 +22,13 @@ gcp_logo = "https://user-images.githubusercontent.com/38561120/235928332-eb4accd
|
||||
orange_color = "\033[38;5;208m"
|
||||
banner_color = "\033[1;92m"
|
||||
|
||||
finding_statuses = ["PASS", "FAIL", "MANUAL"]
|
||||
valid_severities = ["critical", "high", "medium", "low", "informational"]
|
||||
|
||||
class Provider(str, Enum):
|
||||
AWS = "aws"
|
||||
GCP = "gcp"
|
||||
AZURE = "azure"
|
||||
KUBERNETES = "kubernetes"
|
||||
|
||||
|
||||
# Compliance
|
||||
actual_directory = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
|
||||
@@ -30,7 +36,7 @@ actual_directory = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
def get_available_compliance_frameworks(provider=None):
|
||||
available_compliance_frameworks = []
|
||||
providers = ["aws", "gcp", "azure", "kubernetes"]
|
||||
providers = [p.value for p in Provider]
|
||||
if provider:
|
||||
providers = [provider]
|
||||
for provider in providers:
|
||||
|
||||
@@ -57,7 +57,13 @@ aws:
|
||||
8088,
|
||||
]
|
||||
|
||||
# AWS ECS Configuration
|
||||
# aws.ecs_service_fargate_latest_platform_version
|
||||
fargate_linux_latest_version: "1.4.0"
|
||||
fargate_windows_latest_version: "1.0.0"
|
||||
|
||||
# AWS VPC Configuration (vpc_endpoint_connections_trust_boundaries, vpc_endpoint_services_allowed_principals_trust_boundaries)
|
||||
# AWS SSM Configuration (aws.ssm_documents_set_as_public)
|
||||
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
|
||||
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
|
||||
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]
|
||||
@@ -85,6 +91,7 @@ aws:
|
||||
"python3.6",
|
||||
"python2.7",
|
||||
"python3.7",
|
||||
"python3.8",
|
||||
"nodejs4.3",
|
||||
"nodejs4.3-edge",
|
||||
"nodejs6.10",
|
||||
@@ -93,7 +100,10 @@ aws:
|
||||
"nodejs10.x",
|
||||
"nodejs12.x",
|
||||
"nodejs14.x",
|
||||
"nodejs16.x",
|
||||
"dotnet5.0",
|
||||
"dotnet6",
|
||||
"dotnet7",
|
||||
"dotnetcore1.0",
|
||||
"dotnetcore2.0",
|
||||
"dotnetcore2.1",
|
||||
@@ -101,6 +111,8 @@ aws:
|
||||
"ruby2.5",
|
||||
"ruby2.7",
|
||||
]
|
||||
# aws.awslambda_function_vpc_is_in_multi_azs
|
||||
lambda_min_azs: 2
|
||||
|
||||
# AWS Organizations
|
||||
# aws.organizations_scp_check_deny_regions
|
||||
@@ -125,7 +137,7 @@ aws:
|
||||
|
||||
# AWS CloudTrail Configuration
|
||||
# aws.cloudtrail_threat_detection_privilege_escalation
|
||||
threat_detection_privilege_escalation_threshold: 0.1 # Percentage of actions found to decide if it is an privilege_escalation attack event, by default is 0.1 (10%)
|
||||
threat_detection_privilege_escalation_threshold: 0.2 # Percentage of actions found to decide if it is an privilege_escalation attack event, by default is 0.2 (20%)
|
||||
threat_detection_privilege_escalation_minutes: 1440 # Past minutes to search from now for privilege_escalation attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_privilege_escalation_actions:
|
||||
[
|
||||
@@ -182,7 +194,7 @@ aws:
|
||||
"UpdateLoginProfile",
|
||||
]
|
||||
# aws.cloudtrail_threat_detection_enumeration
|
||||
threat_detection_enumeration_threshold: 0.1 # Percentage of actions found to decide if it is an enumeration attack event, by default is 0.1 (10%)
|
||||
threat_detection_enumeration_threshold: 0.3 # Percentage of actions found to decide if it is an enumeration attack event, by default is 0.3 (30%)
|
||||
threat_detection_enumeration_minutes: 1440 # Past minutes to search from now for enumeration attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_enumeration_actions:
|
||||
[
|
||||
@@ -277,6 +289,24 @@ aws:
|
||||
"LookupEvents",
|
||||
"Search",
|
||||
]
|
||||
# aws.cloudtrail_threat_detection_llm_jacking
|
||||
threat_detection_llm_jacking_threshold: 0.4 # Percentage of actions found to decide if it is an LLM Jacking attack event, by default is 0.4 (40%)
|
||||
threat_detection_llm_jacking_minutes: 1440 # Past minutes to search from now for LLM Jacking attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_llm_jacking_actions:
|
||||
[
|
||||
"PutUseCaseForModelAccess", # Submits a use case for model access, providing justification (Write).
|
||||
"PutFoundationModelEntitlement", # Grants entitlement for accessing a foundation model (Write).
|
||||
"PutModelInvocationLoggingConfiguration", # Configures logging for model invocations (Write).
|
||||
"CreateFoundationModelAgreement", # Creates a new agreement to use a foundation model (Write).
|
||||
"InvokeModel", # Invokes a specified Bedrock model for inference using provided prompt and parameters (Read).
|
||||
"InvokeModelWithResponseStream", # Invokes a Bedrock model for inference with real-time token streaming (Read).
|
||||
"GetUseCaseForModelAccess", # Retrieves an existing use case for model access (Read).
|
||||
"GetModelInvocationLoggingConfiguration", # Fetches the logging configuration for model invocations (Read).
|
||||
"GetFoundationModelAvailability", # Checks the availability of a foundation model for use (Read).
|
||||
"ListFoundationModelAgreementOffers", # Lists available agreement offers for accessing foundation models (List).
|
||||
"ListFoundationModels", # Lists the available foundation models in Bedrock (List).
|
||||
"ListProvisionedModelThroughputs", # Lists the provisioned throughput for previously created models (List).
|
||||
]
|
||||
|
||||
# AWS RDS Configuration
|
||||
# aws.rds_instance_backup_enabled
|
||||
@@ -326,11 +356,30 @@ aws:
|
||||
# Minimum number of Availability Zones that an ELBv2 must be in
|
||||
elbv2_min_azs: 2
|
||||
|
||||
# AWS Elasticache Configuration
|
||||
# aws.elasticache_redis_cluster_backup_enabled
|
||||
# Minimum number of days that a Redis cluster must have backups retention period
|
||||
minimum_snapshot_retention_period: 7
|
||||
|
||||
# Known secrets to ignore on detection
|
||||
# this will include a list of regex patterns to ignore on detection
|
||||
|
||||
# AWS Secrets Configuration
|
||||
# Patterns to ignore in the secrets checks
|
||||
secrets_ignore_patterns: []
|
||||
|
||||
# AWS Secrets Manager Configuration
|
||||
# aws.secretsmanager_secret_unused
|
||||
# Maximum number of days a secret can be unused
|
||||
max_days_secret_unused: 90
|
||||
|
||||
# aws.secretsmanager_secret_rotated_periodically
|
||||
# Maximum number of days a secret should be rotated
|
||||
max_days_secret_unrotated: 90
|
||||
|
||||
# AWS Kinesis Configuration
|
||||
# Minimum retention period in hours for Kinesis streams
|
||||
min_kinesis_stream_retention_hours: 168 # 7 days
|
||||
|
||||
|
||||
# Azure Configuration
|
||||
azure:
|
||||
# Azure Network Configuration
|
||||
|
||||
@@ -9,14 +9,14 @@ class ProwlerException(Exception):
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self, code, provider=None, file=None, original_exception=None, error_info=None
|
||||
self, code, source=None, file=None, original_exception=None, error_info=None
|
||||
):
|
||||
"""
|
||||
Initialize the ProwlerException class.
|
||||
|
||||
Args:
|
||||
code (int): The error code.
|
||||
provider (str): The provider name.
|
||||
source (str): The source name. This can be the provider name, module name, service name, etc.
|
||||
file (str): The file name.
|
||||
original_exception (Exception): The original exception.
|
||||
error_info (dict): The error information.
|
||||
@@ -28,7 +28,7 @@ class ProwlerException(Exception):
|
||||
>>> [1901] Unexpected error occurred. - Exception: Error occurred.
|
||||
"""
|
||||
self.code = code
|
||||
self.provider = provider
|
||||
self.source = source
|
||||
self.file = file
|
||||
if error_info is None:
|
||||
error_info = self.ERROR_CODES.get((code, self.__class__.__name__))
|
||||
@@ -45,9 +45,12 @@ class ProwlerException(Exception):
|
||||
|
||||
def __str__(self):
|
||||
"""Overriding the __str__ method"""
|
||||
return f"{self.__class__.__name__}[{self.code}]: {self.message} - {self.original_exception}"
|
||||
default_str = f"{self.__class__.__name__}[{self.code}]: {self.message}"
|
||||
if self.original_exception:
|
||||
default_str += f" - {self.original_exception}"
|
||||
return default_str
|
||||
|
||||
|
||||
class UnexpectedError(ProwlerException):
|
||||
def __init__(self, provider, file, original_exception=None):
|
||||
super().__init__(1901, provider, file, original_exception)
|
||||
def __init__(self, source, file, original_exception=None):
|
||||
super().__init__(1901, source, file, original_exception)
|
||||
+103
-105
@@ -1,4 +1,3 @@
|
||||
import functools
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
@@ -274,7 +273,7 @@ def print_checks(
|
||||
for check in check_list:
|
||||
try:
|
||||
print(
|
||||
f"[{bulk_checks_metadata[check].CheckID}] {bulk_checks_metadata[check].CheckTitle} - {Fore.MAGENTA}{bulk_checks_metadata[check].ServiceName} {Fore.YELLOW}[{bulk_checks_metadata[check].Severity}]{Style.RESET_ALL}"
|
||||
f"[{bulk_checks_metadata[check].CheckID}] {bulk_checks_metadata[check].CheckTitle} - {Fore.MAGENTA}{bulk_checks_metadata[check].ServiceName} {Fore.YELLOW}[{bulk_checks_metadata[check].Severity.value}]{Style.RESET_ALL}"
|
||||
)
|
||||
except KeyError as error:
|
||||
logger.error(
|
||||
@@ -293,67 +292,12 @@ def print_checks(
|
||||
print(message)
|
||||
|
||||
|
||||
# Parse checks from compliance frameworks specification
|
||||
def parse_checks_from_compliance_framework(
|
||||
compliance_frameworks: list, bulk_compliance_frameworks: dict
|
||||
) -> list:
|
||||
"""parse_checks_from_compliance_framework returns a set of checks from the given compliance_frameworks"""
|
||||
checks_to_execute = set()
|
||||
try:
|
||||
for framework in compliance_frameworks:
|
||||
# compliance_framework_json["Requirements"][*]["Checks"]
|
||||
compliance_framework_checks_list = [
|
||||
requirement.Checks
|
||||
for requirement in bulk_compliance_frameworks[framework].Requirements
|
||||
]
|
||||
# Reduce nested list into a list
|
||||
# Pythonic functional magic
|
||||
compliance_framework_checks = functools.reduce(
|
||||
lambda x, y: x + y, compliance_framework_checks_list
|
||||
)
|
||||
# Then union this list of checks with the initial one
|
||||
checks_to_execute = checks_to_execute.union(compliance_framework_checks)
|
||||
except Exception as e:
|
||||
logger.error(f"{e.__class__.__name__}[{e.__traceback__.tb_lineno}] -- {e}")
|
||||
|
||||
return checks_to_execute
|
||||
|
||||
|
||||
# Import an input check using its path
|
||||
def import_check(check_path: str) -> ModuleType:
|
||||
lib = importlib.import_module(f"{check_path}")
|
||||
return lib
|
||||
|
||||
|
||||
def run_check(check: Check, verbose: bool = False, only_logs: bool = False) -> list:
|
||||
"""
|
||||
Run the check and return the findings
|
||||
Args:
|
||||
check (Check): check class
|
||||
output_options (Any): output options
|
||||
Returns:
|
||||
list: list of findings
|
||||
"""
|
||||
findings = []
|
||||
if verbose:
|
||||
print(
|
||||
f"\nCheck ID: {check.CheckID} - {Fore.MAGENTA}{check.ServiceName}{Fore.YELLOW} [{check.Severity}]{Style.RESET_ALL}"
|
||||
)
|
||||
logger.debug(f"Executing check: {check.CheckID}")
|
||||
try:
|
||||
findings = check.execute()
|
||||
except Exception as error:
|
||||
if not only_logs:
|
||||
print(
|
||||
f"Something went wrong in {check.CheckID}, please use --log-level ERROR"
|
||||
)
|
||||
logger.error(
|
||||
f"{check.CheckID} -- {error.__class__.__name__}[{traceback.extract_tb(error.__traceback__)[-1].lineno}]: {error}"
|
||||
)
|
||||
finally:
|
||||
return findings
|
||||
|
||||
|
||||
def run_fixer(check_findings: list) -> int:
|
||||
"""
|
||||
Run the fixer for the check if it exists and there are any FAIL findings
|
||||
@@ -435,6 +379,7 @@ def execute_checks(
|
||||
global_provider: Any,
|
||||
custom_checks_metadata: Any,
|
||||
config_file: str,
|
||||
output_options: Any,
|
||||
) -> list:
|
||||
# List to store all the check's findings
|
||||
all_findings = []
|
||||
@@ -470,18 +415,42 @@ def execute_checks(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
# Set verbose flag
|
||||
verbose = False
|
||||
if hasattr(output_options, "verbose"):
|
||||
verbose = output_options.verbose
|
||||
elif hasattr(output_options, "fixer"):
|
||||
verbose = output_options.fixer
|
||||
|
||||
# Execution with the --only-logs flag
|
||||
if global_provider.output_options.only_logs:
|
||||
if output_options.only_logs:
|
||||
for check_name in checks_to_execute:
|
||||
# Recover service from check name
|
||||
service = check_name.split("_")[0]
|
||||
try:
|
||||
try:
|
||||
# Import check module
|
||||
check_module_path = f"prowler.providers.{global_provider.type}.services.{service}.{check_name}.{check_name}"
|
||||
lib = import_check(check_module_path)
|
||||
# Recover functions from check
|
||||
check_to_execute = getattr(lib, check_name)
|
||||
check = check_to_execute()
|
||||
except ModuleNotFoundError:
|
||||
logger.error(
|
||||
f"Check '{check_name}' was not found for the {global_provider.type.upper()} provider"
|
||||
)
|
||||
continue
|
||||
if verbose:
|
||||
print(
|
||||
f"\nCheck ID: {check.CheckID} - {Fore.MAGENTA}{check.ServiceName}{Fore.YELLOW} [{check.Severity.value}]{Style.RESET_ALL}"
|
||||
)
|
||||
check_findings = execute(
|
||||
service,
|
||||
check_name,
|
||||
check,
|
||||
global_provider,
|
||||
custom_checks_metadata,
|
||||
output_options,
|
||||
)
|
||||
report(check_findings, global_provider, output_options)
|
||||
all_findings.extend(check_findings)
|
||||
|
||||
# Update Audit Status
|
||||
@@ -539,12 +508,31 @@ def execute_checks(
|
||||
f"-> Scanning {orange_color}{service}{Style.RESET_ALL} service"
|
||||
)
|
||||
try:
|
||||
try:
|
||||
# Import check module
|
||||
check_module_path = f"prowler.providers.{global_provider.type}.services.{service}.{check_name}.{check_name}"
|
||||
lib = import_check(check_module_path)
|
||||
# Recover functions from check
|
||||
check_to_execute = getattr(lib, check_name)
|
||||
check = check_to_execute()
|
||||
except ModuleNotFoundError:
|
||||
logger.error(
|
||||
f"Check '{check_name}' was not found for the {global_provider.type.upper()} provider"
|
||||
)
|
||||
continue
|
||||
if verbose:
|
||||
print(
|
||||
f"\nCheck ID: {check.CheckID} - {Fore.MAGENTA}{check.ServiceName}{Fore.YELLOW} [{check.Severity.value}]{Style.RESET_ALL}"
|
||||
)
|
||||
check_findings = execute(
|
||||
service,
|
||||
check_name,
|
||||
check,
|
||||
global_provider,
|
||||
custom_checks_metadata,
|
||||
output_options,
|
||||
)
|
||||
|
||||
report(check_findings, global_provider, output_options)
|
||||
|
||||
all_findings.extend(check_findings)
|
||||
services_executed.add(service)
|
||||
checks_executed.add(check_name)
|
||||
@@ -567,51 +555,79 @@ def execute_checks(
|
||||
)
|
||||
bar()
|
||||
bar.title = f"-> {Fore.GREEN}Scan completed!{Style.RESET_ALL}"
|
||||
|
||||
# Custom report interface
|
||||
if os.environ.get("PROWLER_REPORT_LIB_PATH"):
|
||||
try:
|
||||
logger.info("Using custom report interface ...")
|
||||
lib = os.environ["PROWLER_REPORT_LIB_PATH"]
|
||||
outputs_module = importlib.import_module(lib)
|
||||
custom_report_interface = getattr(outputs_module, "report")
|
||||
|
||||
# TODO: review this call and see if we can remove the global_provider.output_options since it is contained in the global_provider
|
||||
custom_report_interface(check_findings, output_options, global_provider)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
return all_findings
|
||||
|
||||
|
||||
def execute(
|
||||
service: str,
|
||||
check_name: str,
|
||||
check: Check,
|
||||
global_provider: Any,
|
||||
custom_checks_metadata: Any,
|
||||
output_options: Any = None,
|
||||
):
|
||||
try:
|
||||
# Import check module
|
||||
check_module_path = f"prowler.providers.{global_provider.type}.services.{service}.{check_name}.{check_name}"
|
||||
lib = import_check(check_module_path)
|
||||
# Recover functions from check
|
||||
check_to_execute = getattr(lib, check_name)
|
||||
check_class = check_to_execute()
|
||||
"""
|
||||
Execute the check and report the findings
|
||||
|
||||
Args:
|
||||
service (str): service name
|
||||
check_name (str): check name
|
||||
global_provider (Any): provider object
|
||||
custom_checks_metadata (Any): custom checks metadata
|
||||
output_options (Any): output options, depending on the provider
|
||||
|
||||
Returns:
|
||||
list: list of findings
|
||||
"""
|
||||
try:
|
||||
# Update check metadata to reflect that in the outputs
|
||||
if custom_checks_metadata and custom_checks_metadata["Checks"].get(
|
||||
check_class.CheckID
|
||||
check.CheckID
|
||||
):
|
||||
check_class = update_check_metadata(
|
||||
check_class, custom_checks_metadata["Checks"][check_class.CheckID]
|
||||
check = update_check_metadata(
|
||||
check, custom_checks_metadata["Checks"][check.CheckID]
|
||||
)
|
||||
|
||||
# Run check
|
||||
verbose = (
|
||||
global_provider.output_options.verbose
|
||||
or global_provider.output_options.fixer
|
||||
)
|
||||
check_findings = run_check(
|
||||
check_class, verbose, global_provider.output_options.only_logs
|
||||
)
|
||||
only_logs = False
|
||||
if hasattr(output_options, "only_logs"):
|
||||
only_logs = output_options.only_logs
|
||||
|
||||
# Execute the check
|
||||
check_findings = []
|
||||
logger.debug(f"Executing check: {check.CheckID}")
|
||||
try:
|
||||
check_findings = check.execute()
|
||||
except Exception as error:
|
||||
if not only_logs:
|
||||
print(
|
||||
f"Something went wrong in {check.CheckID}, please use --log-level ERROR"
|
||||
)
|
||||
logger.error(
|
||||
f"{check.CheckID} -- {error.__class__.__name__}[{traceback.extract_tb(error.__traceback__)[-1].lineno}]: {error}"
|
||||
)
|
||||
|
||||
# Exclude findings per status
|
||||
if global_provider.output_options.status:
|
||||
if hasattr(output_options, "status") and output_options.status:
|
||||
check_findings = [
|
||||
finding
|
||||
for finding in check_findings
|
||||
if finding.status in global_provider.output_options.status
|
||||
if finding.status in output_options.status
|
||||
]
|
||||
|
||||
# Mutelist findings
|
||||
# Before returning the findings, we need to apply the mute list logic
|
||||
if hasattr(global_provider, "mutelist") and global_provider.mutelist.mutelist:
|
||||
# TODO: make this prettier
|
||||
is_finding_muted_args = {}
|
||||
if global_provider.type == "aws":
|
||||
is_finding_muted_args["aws_account_id"] = (
|
||||
@@ -626,27 +642,9 @@ def execute(
|
||||
**is_finding_muted_args
|
||||
)
|
||||
|
||||
# Refactor(Outputs)
|
||||
# Report the check's findings
|
||||
report(check_findings, global_provider)
|
||||
|
||||
# Refactor(Outputs)
|
||||
if os.environ.get("PROWLER_REPORT_LIB_PATH"):
|
||||
try:
|
||||
logger.info("Using custom report interface ...")
|
||||
lib = os.environ["PROWLER_REPORT_LIB_PATH"]
|
||||
outputs_module = importlib.import_module(lib)
|
||||
custom_report_interface = getattr(outputs_module, "report")
|
||||
|
||||
# TODO: review this call and see if we can remove the global_provider.output_options since it is contained in the global_provider
|
||||
custom_report_interface(
|
||||
check_findings, global_provider.output_options, global_provider
|
||||
)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
except ModuleNotFoundError:
|
||||
logger.error(
|
||||
f"Check '{check_name}' was not found for the {global_provider.type.upper()} provider"
|
||||
f"Check '{check.CheckID}' was not found for the {global_provider.type.upper()} provider"
|
||||
)
|
||||
check_findings = []
|
||||
except Exception as error:
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
from colorama import Fore, Style
|
||||
|
||||
from prowler.config.config import valid_severities
|
||||
from prowler.lib.check.check import (
|
||||
parse_checks_from_compliance_framework,
|
||||
parse_checks_from_file,
|
||||
)
|
||||
from prowler.lib.check.utils import (
|
||||
recover_checks_from_provider,
|
||||
recover_checks_from_service,
|
||||
)
|
||||
from prowler.lib.check.check import parse_checks_from_file
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.check.models import CheckMetadata, Severity
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
# Generate the list of checks to execute
|
||||
def load_checks_to_execute(
|
||||
bulk_checks_metadata: dict,
|
||||
bulk_compliance_frameworks: dict,
|
||||
checks_file: str,
|
||||
check_list: list,
|
||||
service_list: list,
|
||||
severities: list,
|
||||
compliance_frameworks: list,
|
||||
categories: set,
|
||||
provider: str,
|
||||
bulk_checks_metadata: dict = None,
|
||||
bulk_compliance_frameworks: dict = None,
|
||||
checks_file: str = None,
|
||||
check_list: list = None,
|
||||
service_list: list = None,
|
||||
severities: list = None,
|
||||
compliance_frameworks: list = None,
|
||||
categories: set = None,
|
||||
) -> set:
|
||||
"""Generate the list of checks to execute based on the cloud provider and the input arguments given"""
|
||||
try:
|
||||
# Local subsets
|
||||
checks_to_execute = set()
|
||||
check_aliases = {}
|
||||
check_severities = {key: [] for key in valid_severities}
|
||||
check_categories = {}
|
||||
check_severities = {severity.value: [] for severity in Severity}
|
||||
|
||||
if not bulk_checks_metadata:
|
||||
bulk_checks_metadata = CheckMetadata.get_bulk(provider=provider)
|
||||
# First, loop over the bulk_checks_metadata to extract the needed subsets
|
||||
for check, metadata in bulk_checks_metadata.items():
|
||||
try:
|
||||
@@ -66,24 +62,41 @@ def load_checks_to_execute(
|
||||
checks_to_execute.update(check_severities[severity])
|
||||
|
||||
if service_list:
|
||||
checks_to_execute = (
|
||||
recover_checks_from_service(service_list, provider)
|
||||
& checks_to_execute
|
||||
)
|
||||
|
||||
for service in service_list:
|
||||
checks_to_execute = (
|
||||
set(
|
||||
CheckMetadata.list(
|
||||
bulk_checks_metadata=bulk_checks_metadata,
|
||||
service=service,
|
||||
)
|
||||
)
|
||||
& checks_to_execute
|
||||
)
|
||||
# Handle if there are checks passed using -C/--checks-file
|
||||
elif checks_file:
|
||||
checks_to_execute = parse_checks_from_file(checks_file, provider)
|
||||
|
||||
# Handle if there are services passed using -s/--services
|
||||
elif service_list:
|
||||
checks_to_execute = recover_checks_from_service(service_list, provider)
|
||||
for service in service_list:
|
||||
checks_to_execute.update(
|
||||
CheckMetadata.list(
|
||||
bulk_checks_metadata=bulk_checks_metadata,
|
||||
service=service,
|
||||
)
|
||||
)
|
||||
|
||||
# Handle if there are compliance frameworks passed using --compliance
|
||||
elif compliance_frameworks:
|
||||
checks_to_execute = parse_checks_from_compliance_framework(
|
||||
compliance_frameworks, bulk_compliance_frameworks
|
||||
)
|
||||
if not bulk_compliance_frameworks:
|
||||
bulk_compliance_frameworks = Compliance.get_bulk(provider=provider)
|
||||
for compliance_framework in compliance_frameworks:
|
||||
checks_to_execute.update(
|
||||
CheckMetadata.list(
|
||||
bulk_compliance_frameworks=bulk_compliance_frameworks,
|
||||
compliance_framework=compliance_framework,
|
||||
)
|
||||
)
|
||||
|
||||
# Handle if there are categories passed using --categories
|
||||
elif categories:
|
||||
@@ -92,17 +105,13 @@ def load_checks_to_execute(
|
||||
|
||||
# If there are no checks passed as argument
|
||||
else:
|
||||
# Get all check modules to run with the specific provider
|
||||
checks = recover_checks_from_provider(provider)
|
||||
|
||||
for check_info in checks:
|
||||
# Recover check name from import path (last part)
|
||||
# Format: "providers.{provider}.services.{service}.{check_name}.{check_name}"
|
||||
check_name = check_info[0]
|
||||
# get all checks
|
||||
for check_name in CheckMetadata.list(
|
||||
bulk_checks_metadata=bulk_checks_metadata
|
||||
):
|
||||
checks_to_execute.add(check_name)
|
||||
|
||||
# Only execute threat detection checks if threat-detection category is set
|
||||
if "threat-detection" not in categories:
|
||||
if not categories or "threat-detection" not in categories:
|
||||
for threat_detection_check in check_categories.get("threat-detection", []):
|
||||
checks_to_execute.discard(threat_detection_check)
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ class CIS_Requirement_Attribute(BaseModel):
|
||||
"""CIS Requirement Attribute"""
|
||||
|
||||
Section: str
|
||||
SubSection: Optional[str]
|
||||
Profile: CIS_Requirement_Attribute_Profile
|
||||
AssessmentStatus: CIS_Requirement_Attribute_AssessmentStatus
|
||||
Description: str
|
||||
@@ -169,6 +170,19 @@ class Mitre_Requirement(BaseModel):
|
||||
Checks: list[str]
|
||||
|
||||
|
||||
# KISA-ISMS-P Requirement Attribute
|
||||
class KISA_ISMSP_Requirement_Attribute(BaseModel):
|
||||
"""KISA ISMS-P Requirement Attribute"""
|
||||
|
||||
Domain: str
|
||||
Subdomain: str
|
||||
Section: str
|
||||
AuditChecklist: Optional[list[str]]
|
||||
RelatedRegulations: Optional[list[str]]
|
||||
AuditEvidence: Optional[list[str]]
|
||||
NonComplianceCases: Optional[list[str]]
|
||||
|
||||
|
||||
# Base Compliance Model
|
||||
# TODO: move this to compliance folder
|
||||
class Compliance_Requirement(BaseModel):
|
||||
@@ -183,6 +197,7 @@ class Compliance_Requirement(BaseModel):
|
||||
ENS_Requirement_Attribute,
|
||||
ISO27001_2013_Requirement_Attribute,
|
||||
AWS_Well_Architected_Requirement_Attribute,
|
||||
KISA_ISMSP_Requirement_Attribute,
|
||||
# Generic_Compliance_Requirement_Attribute must be the last one since it is the fallback for generic compliance framework
|
||||
Generic_Compliance_Requirement_Attribute,
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
import yaml
|
||||
from jsonschema import validate
|
||||
|
||||
from prowler.config.config import valid_severities
|
||||
from prowler.lib.check.models import Severity
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
custom_checks_metadata_schema = {
|
||||
@@ -17,7 +17,7 @@ custom_checks_metadata_schema = {
|
||||
"properties": {
|
||||
"Severity": {
|
||||
"type": "string",
|
||||
"enum": valid_severities,
|
||||
"enum": [severity.value for severity in Severity],
|
||||
},
|
||||
"CheckTitle": {
|
||||
"type": "string",
|
||||
|
||||
+223
-10
@@ -1,12 +1,16 @@
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Set
|
||||
|
||||
from pydantic import BaseModel, ValidationError, validator
|
||||
|
||||
from prowler.config.config import valid_severities
|
||||
from prowler.config.config import Provider
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.check.utils import recover_checks_from_provider
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
@@ -54,6 +58,14 @@ class Remediation(BaseModel):
|
||||
Recommendation: Recommendation
|
||||
|
||||
|
||||
class Severity(str, Enum):
|
||||
critical = "critical"
|
||||
high = "high"
|
||||
medium = "medium"
|
||||
low = "low"
|
||||
informational = "informational"
|
||||
|
||||
|
||||
class CheckMetadata(BaseModel):
|
||||
"""
|
||||
Model representing the metadata of a check.
|
||||
@@ -93,7 +105,7 @@ class CheckMetadata(BaseModel):
|
||||
ServiceName: str
|
||||
SubServiceName: str
|
||||
ResourceIdTemplate: str
|
||||
Severity: str
|
||||
Severity: Severity
|
||||
ResourceType: str
|
||||
Description: str
|
||||
Risk: str
|
||||
@@ -122,14 +134,6 @@ class CheckMetadata(BaseModel):
|
||||
def severity_to_lower(severity):
|
||||
return severity.lower()
|
||||
|
||||
@validator("Severity")
|
||||
def valid_severity(severity):
|
||||
if severity not in valid_severities:
|
||||
raise ValueError(
|
||||
f"Invalid severity: {severity}. Severity must be one of {', '.join(valid_severities)}"
|
||||
)
|
||||
return severity
|
||||
|
||||
@staticmethod
|
||||
def get_bulk(provider: str) -> dict[str, "CheckMetadata"]:
|
||||
"""
|
||||
@@ -158,6 +162,215 @@ class CheckMetadata(BaseModel):
|
||||
|
||||
return bulk_check_metadata
|
||||
|
||||
@staticmethod
|
||||
def list(
|
||||
bulk_checks_metadata: dict = None,
|
||||
bulk_compliance_frameworks: dict = None,
|
||||
provider: str = None,
|
||||
severity: str = None,
|
||||
category: str = None,
|
||||
service: str = None,
|
||||
compliance_framework: str = None,
|
||||
) -> Set["CheckMetadata"]:
|
||||
"""
|
||||
Returns a set of checks from the bulk checks metadata.
|
||||
|
||||
Args:
|
||||
provider (str): The provider of the checks.
|
||||
bulk_checks_metadata (dict): The bulk checks metadata.
|
||||
bulk_compliance_frameworks (dict): The bulk compliance frameworks.
|
||||
severity (str): The severity of the checks.
|
||||
category (str): The category of the checks.
|
||||
service (str): The service of the checks.
|
||||
compliance_framework (str): The compliance framework of the checks.
|
||||
|
||||
Returns:
|
||||
set: A set of checks.
|
||||
"""
|
||||
checks_from_provider = set()
|
||||
checks_from_severity = set()
|
||||
checks_from_category = set()
|
||||
checks_from_service = set()
|
||||
checks_from_compliance_framework = set()
|
||||
# If the bulk checks metadata is not provided, get it
|
||||
if not bulk_checks_metadata:
|
||||
bulk_checks_metadata = {}
|
||||
available_providers = [p.value for p in Provider]
|
||||
for provider_name in available_providers:
|
||||
bulk_checks_metadata.update(CheckMetadata.get_bulk(provider_name))
|
||||
if provider:
|
||||
checks_from_provider = {
|
||||
check_name
|
||||
for check_name, check_metadata in bulk_checks_metadata.items()
|
||||
if check_metadata.Provider == provider
|
||||
}
|
||||
if severity:
|
||||
checks_from_severity = CheckMetadata.list_by_severity(
|
||||
bulk_checks_metadata=bulk_checks_metadata, severity=severity
|
||||
)
|
||||
if category:
|
||||
checks_from_category = CheckMetadata.list_by_category(
|
||||
bulk_checks_metadata=bulk_checks_metadata, category=category
|
||||
)
|
||||
if service:
|
||||
checks_from_service = CheckMetadata.list_by_service(
|
||||
bulk_checks_metadata=bulk_checks_metadata, service=service
|
||||
)
|
||||
if compliance_framework:
|
||||
# Loaded here, as it is not always needed
|
||||
if not bulk_compliance_frameworks:
|
||||
bulk_compliance_frameworks = {}
|
||||
available_providers = [p.value for p in Provider]
|
||||
for provider in available_providers:
|
||||
bulk_compliance_frameworks = Compliance.get_bulk(provider=provider)
|
||||
checks_from_compliance_framework = (
|
||||
CheckMetadata.list_by_compliance_framework(
|
||||
bulk_compliance_frameworks=bulk_compliance_frameworks,
|
||||
compliance_framework=compliance_framework,
|
||||
)
|
||||
)
|
||||
|
||||
# Get all the checks:
|
||||
checks = set(bulk_checks_metadata.keys())
|
||||
# Get the intersection of the checks
|
||||
if len(checks_from_provider) > 0 or provider:
|
||||
checks = checks & checks_from_provider
|
||||
if len(checks_from_severity) > 0 or severity:
|
||||
checks = checks & checks_from_severity
|
||||
if len(checks_from_category) > 0 or category:
|
||||
checks = checks & checks_from_category
|
||||
if len(checks_from_service) > 0 or service:
|
||||
checks = checks & checks_from_service
|
||||
if len(checks_from_compliance_framework) > 0 or compliance_framework:
|
||||
checks = checks & checks_from_compliance_framework
|
||||
|
||||
return checks
|
||||
|
||||
@staticmethod
|
||||
def get(bulk_checks_metadata: dict, check_id: str) -> "CheckMetadata":
|
||||
"""
|
||||
Returns the check metadata from the bulk checks metadata.
|
||||
|
||||
Args:
|
||||
bulk_checks_metadata (dict): The bulk checks metadata.
|
||||
check_id (str): The check ID.
|
||||
|
||||
Returns:
|
||||
CheckMetadata: The check metadata.
|
||||
"""
|
||||
|
||||
return bulk_checks_metadata.get(check_id, None)
|
||||
|
||||
@staticmethod
|
||||
def list_by_severity(bulk_checks_metadata: dict, severity: str = None) -> set:
|
||||
"""
|
||||
Returns a set of checks by severity from the bulk checks metadata.
|
||||
|
||||
Args:
|
||||
bulk_checks_metadata (dict): The bulk checks metadata.
|
||||
severity (str): The severity.
|
||||
|
||||
Returns:
|
||||
set: A set of checks by severity.
|
||||
"""
|
||||
checks = set()
|
||||
|
||||
if severity:
|
||||
checks = {
|
||||
check_name
|
||||
for check_name, check_metadata in bulk_checks_metadata.items()
|
||||
if check_metadata.Severity == severity
|
||||
}
|
||||
|
||||
return checks
|
||||
|
||||
@staticmethod
|
||||
def list_by_category(bulk_checks_metadata: dict, category: str = None) -> set:
|
||||
"""
|
||||
Returns a set of checks by category from the bulk checks metadata.
|
||||
|
||||
Args:
|
||||
bulk_checks_metadata (dict): The bulk checks metadata.
|
||||
category (str): The category.
|
||||
|
||||
Returns:
|
||||
set: A set of checks by category.
|
||||
"""
|
||||
checks = set()
|
||||
|
||||
if category:
|
||||
checks = {
|
||||
check_name
|
||||
for check_name, check_metadata in bulk_checks_metadata.items()
|
||||
if category in check_metadata.Categories
|
||||
}
|
||||
|
||||
return checks
|
||||
|
||||
@staticmethod
|
||||
def list_by_service(bulk_checks_metadata: dict, service: str = None) -> set:
|
||||
"""
|
||||
Returns a set of checks by service from the bulk checks metadata.
|
||||
|
||||
Args:
|
||||
bulk_checks_metadata (dict): The bulk checks metadata.
|
||||
service (str): The service.
|
||||
|
||||
Returns:
|
||||
set: A set of checks by service.
|
||||
"""
|
||||
checks = set()
|
||||
|
||||
if service:
|
||||
# This is a special case for the AWS provider since `lambda` is a reserved keyword in Python
|
||||
if service == "awslambda":
|
||||
service = "lambda"
|
||||
checks = {
|
||||
check_name
|
||||
for check_name, check_metadata in bulk_checks_metadata.items()
|
||||
if check_metadata.ServiceName == service
|
||||
}
|
||||
|
||||
return checks
|
||||
|
||||
@staticmethod
|
||||
def list_by_compliance_framework(
|
||||
bulk_compliance_frameworks: dict, compliance_framework: str = None
|
||||
) -> set:
|
||||
"""
|
||||
Returns a set of checks by compliance framework from the bulk compliance frameworks.
|
||||
|
||||
Args:
|
||||
bulk_compliance_frameworks (dict): The bulk compliance frameworks.
|
||||
compliance_framework (str): The compliance framework.
|
||||
|
||||
Returns:
|
||||
set: A set of checks by compliance framework.
|
||||
"""
|
||||
checks = set()
|
||||
|
||||
if compliance_framework:
|
||||
try:
|
||||
checks_from_framework_list = [
|
||||
requirement.Checks
|
||||
for requirement in bulk_compliance_frameworks[
|
||||
compliance_framework
|
||||
].Requirements
|
||||
]
|
||||
# Reduce nested list into a list
|
||||
# Pythonic functional magic
|
||||
checks_from_framework = functools.reduce(
|
||||
lambda x, y: x + y, checks_from_framework_list
|
||||
)
|
||||
# Then union this list of checks with the initial one
|
||||
checks = checks.union(checks_from_framework)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"{e.__class__.__name__}[{e.__traceback__.tb_lineno}] -- {e}"
|
||||
)
|
||||
|
||||
return checks
|
||||
|
||||
|
||||
class Check(ABC, CheckMetadata):
|
||||
"""Prowler Check"""
|
||||
|
||||
@@ -10,9 +10,9 @@ from prowler.config.config import (
|
||||
default_config_file_path,
|
||||
default_fixer_config_file_path,
|
||||
default_output_directory,
|
||||
finding_statuses,
|
||||
valid_severities,
|
||||
)
|
||||
from prowler.lib.check.models import Severity
|
||||
from prowler.lib.outputs.common import Status
|
||||
from prowler.providers.common.arguments import (
|
||||
init_providers_parser,
|
||||
validate_provider_arguments,
|
||||
@@ -138,8 +138,8 @@ Detailed documentation at https://docs.prowler.com
|
||||
common_outputs_parser.add_argument(
|
||||
"--status",
|
||||
nargs="+",
|
||||
help=f"Filter by the status of the findings {finding_statuses}",
|
||||
choices=finding_statuses,
|
||||
help=f"Filter by the status of the findings {[status.value for status in Status]}",
|
||||
choices=[status.value for status in Status],
|
||||
)
|
||||
common_outputs_parser.add_argument(
|
||||
"--output-formats",
|
||||
@@ -177,6 +177,12 @@ Detailed documentation at https://docs.prowler.com
|
||||
common_outputs_parser.add_argument(
|
||||
"--no-banner", "-b", action="store_true", help="Hide Prowler banner"
|
||||
)
|
||||
common_outputs_parser.add_argument(
|
||||
"--no-color",
|
||||
action="store_true",
|
||||
help="Disable color codes in output",
|
||||
)
|
||||
|
||||
common_outputs_parser.add_argument(
|
||||
"--unix-timestamp",
|
||||
action="store_true",
|
||||
@@ -257,8 +263,8 @@ Detailed documentation at https://docs.prowler.com
|
||||
"--severity",
|
||||
"--severities",
|
||||
nargs="+",
|
||||
help=f"Severities to be executed {valid_severities}",
|
||||
choices=valid_severities,
|
||||
help=f"Severities to be executed {[severity.value for severity in Severity]}",
|
||||
choices=[severity.value for severity in Severity],
|
||||
)
|
||||
group.add_argument(
|
||||
"--compliance",
|
||||
|
||||
@@ -5,6 +5,8 @@ import yaml
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.mutelist.models import mutelist_schema
|
||||
from prowler.lib.outputs.common import Status
|
||||
from prowler.lib.outputs.utils import unroll_dict, unroll_tags
|
||||
|
||||
|
||||
class Mutelist(ABC):
|
||||
@@ -237,6 +239,35 @@ class Mutelist(ABC):
|
||||
)
|
||||
return False
|
||||
|
||||
def mute_finding(self, finding):
|
||||
"""
|
||||
Check if the provided finding is muted
|
||||
|
||||
Args:
|
||||
finding (Finding): The finding to be evaluated for muting.
|
||||
|
||||
Returns:
|
||||
Finding: The finding with the status updated if it is muted, otherwise the finding is returned
|
||||
|
||||
"""
|
||||
try:
|
||||
if self.is_muted(
|
||||
finding.account_uid,
|
||||
finding.metadata.CheckID,
|
||||
finding.region,
|
||||
finding.resource_uid,
|
||||
unroll_dict(unroll_tags(finding.resource_tags)),
|
||||
):
|
||||
finding.raw["status"] = finding.status
|
||||
finding.status = Status.MUTED
|
||||
finding.muted = True
|
||||
return finding
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__} -- {error}[{error.__traceback__.tb_lineno}]"
|
||||
)
|
||||
return finding
|
||||
|
||||
def is_excepted(
|
||||
self,
|
||||
exceptions,
|
||||
|
||||
@@ -72,28 +72,32 @@ class ASFF(Output):
|
||||
AWSSecurityFindingFormat(
|
||||
# The following line cannot be changed because it is the format we use to generate unique findings for AWS Security Hub
|
||||
# If changed some findings could be lost because the unique identifier will be different
|
||||
Id=f"prowler-{finding.check_id}-{finding.account_uid}-{finding.region}-{hash_sha512(finding.resource_uid)}",
|
||||
Id=f"prowler-{finding.metadata.CheckID}-{finding.account_uid}-{finding.region}-{hash_sha512(finding.resource_uid)}",
|
||||
ProductArn=f"arn:{finding.partition}:securityhub:{finding.region}::product/prowler/prowler",
|
||||
ProductFields=ProductFields(
|
||||
ProwlerResourceName=finding.resource_uid,
|
||||
),
|
||||
GeneratorId="prowler-" + finding.check_id,
|
||||
GeneratorId="prowler-" + finding.metadata.CheckID,
|
||||
AwsAccountId=finding.account_uid,
|
||||
Types=(
|
||||
finding.check_type.split(",")
|
||||
if finding.check_type
|
||||
finding.metadata.CheckType
|
||||
if finding.metadata.CheckType
|
||||
else ["Software and Configuration Checks"]
|
||||
),
|
||||
FirstObservedAt=timestamp,
|
||||
UpdatedAt=timestamp,
|
||||
CreatedAt=timestamp,
|
||||
Severity=Severity(Label=finding.severity.value),
|
||||
Title=finding.check_title,
|
||||
Description=finding.description,
|
||||
Severity=Severity(Label=finding.metadata.Severity.value),
|
||||
Title=finding.metadata.CheckTitle,
|
||||
Description=(
|
||||
(finding.status_extended[:1000] + "...")
|
||||
if len(finding.status_extended) > 1000
|
||||
else finding.status_extended
|
||||
),
|
||||
Resources=[
|
||||
Resource(
|
||||
Id=finding.resource_uid,
|
||||
Type=finding.resource_type,
|
||||
Type=finding.metadata.ResourceType,
|
||||
Partition=finding.partition,
|
||||
Region=finding.region,
|
||||
Tags=finding.resource_tags,
|
||||
@@ -106,8 +110,8 @@ class ASFF(Output):
|
||||
),
|
||||
Remediation=Remediation(
|
||||
Recommendation=Recommendation(
|
||||
Text=finding.remediation_recommendation_text,
|
||||
Url=finding.remediation_recommendation_url,
|
||||
Text=finding.metadata.Remediation.Recommendation.Text,
|
||||
Url=finding.metadata.Remediation.Recommendation.Url,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,62 +1,26 @@
|
||||
from operator import attrgetter
|
||||
from enum import Enum
|
||||
|
||||
from prowler.config.config import timestamp
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.outputs.utils import unroll_list, unroll_tags
|
||||
from prowler.lib.outputs.utils import unroll_tags
|
||||
from prowler.lib.utils.utils import outputs_unix_timestamp
|
||||
|
||||
|
||||
def get_provider_data_mapping(provider) -> dict:
|
||||
data = {}
|
||||
for generic_field, provider_field in provider.get_output_mapping.items():
|
||||
try:
|
||||
provider_value = attrgetter(provider_field)(provider)
|
||||
data[generic_field] = provider_value
|
||||
except AttributeError:
|
||||
data[generic_field] = ""
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
# TODO: add test for outputs_unix_timestamp
|
||||
def fill_common_finding_data(finding: dict, unix_timestamp: bool) -> dict:
|
||||
finding_data = {
|
||||
"metadata": finding.check_metadata,
|
||||
"timestamp": outputs_unix_timestamp(unix_timestamp, timestamp),
|
||||
"check_id": finding.check_metadata.CheckID,
|
||||
"check_title": finding.check_metadata.CheckTitle,
|
||||
"check_type": ",".join(finding.check_metadata.CheckType),
|
||||
"status": finding.status,
|
||||
"status_extended": finding.status_extended,
|
||||
"muted": finding.muted,
|
||||
"service_name": finding.check_metadata.ServiceName,
|
||||
"subservice_name": finding.check_metadata.SubServiceName,
|
||||
"severity": finding.check_metadata.Severity,
|
||||
"resource_type": finding.check_metadata.ResourceType,
|
||||
"resource_details": finding.resource_details,
|
||||
"resource_tags": unroll_tags(finding.resource_tags),
|
||||
"description": finding.check_metadata.Description,
|
||||
"risk": finding.check_metadata.Risk,
|
||||
"related_url": finding.check_metadata.RelatedUrl,
|
||||
"remediation_recommendation_text": (
|
||||
finding.check_metadata.Remediation.Recommendation.Text
|
||||
),
|
||||
"remediation_recommendation_url": (
|
||||
finding.check_metadata.Remediation.Recommendation.Url
|
||||
),
|
||||
"remediation_code_nativeiac": (
|
||||
finding.check_metadata.Remediation.Code.NativeIaC
|
||||
),
|
||||
"remediation_code_terraform": (
|
||||
finding.check_metadata.Remediation.Code.Terraform
|
||||
),
|
||||
"remediation_code_cli": (finding.check_metadata.Remediation.Code.CLI),
|
||||
"remediation_code_other": (finding.check_metadata.Remediation.Code.Other),
|
||||
"categories": unroll_list(finding.check_metadata.Categories),
|
||||
"depends_on": unroll_list(finding.check_metadata.DependsOn),
|
||||
"related_to": unroll_list(finding.check_metadata.RelatedTo),
|
||||
"notes": finding.check_metadata.Notes,
|
||||
}
|
||||
return finding_data
|
||||
|
||||
|
||||
class Status(str, Enum):
|
||||
PASS = "PASS"
|
||||
FAIL = "FAIL"
|
||||
MANUAL = "MANUAL"
|
||||
MUTED = "MUTED"
|
||||
@@ -94,11 +94,12 @@ def get_cis_table(
|
||||
print(
|
||||
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
|
||||
)
|
||||
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
|
||||
overview_table = [
|
||||
[
|
||||
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
]
|
||||
]
|
||||
print(tabulate(overview_table, tablefmt="rounded_grid"))
|
||||
|
||||
@@ -48,6 +48,7 @@ class AWSCIS(ComplianceOutput):
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
@@ -78,6 +79,7 @@ class AWSCIS(ComplianceOutput):
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
|
||||
@@ -42,12 +42,13 @@ class AzureCIS(ComplianceOutput):
|
||||
compliance_row = AzureCISModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
Subscription=finding.account_name,
|
||||
SubscriptionId=finding.account_uid,
|
||||
Location=finding.region,
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
@@ -73,12 +74,13 @@ class AzureCIS(ComplianceOutput):
|
||||
compliance_row = AzureCISModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
Subscription="",
|
||||
SubscriptionId="",
|
||||
Location="",
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
|
||||
@@ -48,6 +48,7 @@ class GCPCIS(ComplianceOutput):
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
@@ -78,6 +79,7 @@ class GCPCIS(ComplianceOutput):
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
|
||||
@@ -50,6 +50,7 @@ class KubernetesCIS(ComplianceOutput):
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
@@ -81,6 +82,7 @@ class KubernetesCIS(ComplianceOutput):
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_SubSection=attribute.SubSection,
|
||||
Requirements_Attributes_Profile=attribute.Profile,
|
||||
Requirements_Attributes_AssessmentStatus=attribute.AssessmentStatus,
|
||||
Requirements_Attributes_Description=attribute.Description,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -14,6 +16,7 @@ class AWSCISModel(BaseModel):
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_Section: str
|
||||
Requirements_Attributes_SubSection: Optional[str]
|
||||
Requirements_Attributes_Profile: str
|
||||
Requirements_Attributes_AssessmentStatus: str
|
||||
Requirements_Attributes_Description: str
|
||||
@@ -38,12 +41,13 @@ class AzureCISModel(BaseModel):
|
||||
|
||||
Provider: str
|
||||
Description: str
|
||||
Subscription: str
|
||||
SubscriptionId: str
|
||||
Location: str
|
||||
AssessmentDate: str
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_Section: str
|
||||
Requirements_Attributes_SubSection: Optional[str]
|
||||
Requirements_Attributes_Profile: str
|
||||
Requirements_Attributes_AssessmentStatus: str
|
||||
Requirements_Attributes_Description: str
|
||||
@@ -75,6 +79,7 @@ class GCPCISModel(BaseModel):
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_Section: str
|
||||
Requirements_Attributes_SubSection: Optional[str]
|
||||
Requirements_Attributes_Profile: str
|
||||
Requirements_Attributes_AssessmentStatus: str
|
||||
Requirements_Attributes_Description: str
|
||||
@@ -105,6 +110,7 @@ class KubernetesCISModel(BaseModel):
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_Section: str
|
||||
Requirements_Attributes_SubSection: Optional[str]
|
||||
Requirements_Attributes_Profile: str
|
||||
Requirements_Attributes_AssessmentStatus: str
|
||||
Requirements_Attributes_Description: str
|
||||
|
||||
@@ -7,6 +7,7 @@ from prowler.lib.outputs.compliance.ens.ens import get_ens_table
|
||||
from prowler.lib.outputs.compliance.generic.generic_table import (
|
||||
get_generic_compliance_table,
|
||||
)
|
||||
from prowler.lib.outputs.compliance.kisa_ismsp.kisa_ismsp import get_kisa_ismsp_table
|
||||
from prowler.lib.outputs.compliance.mitre_attack.mitre_attack import (
|
||||
get_mitre_attack_table,
|
||||
)
|
||||
@@ -62,6 +63,15 @@ def display_compliance_table(
|
||||
output_directory,
|
||||
compliance_overview,
|
||||
)
|
||||
elif "kisa_isms_" in compliance_framework:
|
||||
get_kisa_ismsp_table(
|
||||
findings,
|
||||
bulk_checks_metadata,
|
||||
compliance_framework,
|
||||
output_filename,
|
||||
output_directory,
|
||||
compliance_overview,
|
||||
)
|
||||
else:
|
||||
get_generic_compliance_table(
|
||||
findings,
|
||||
|
||||
@@ -30,7 +30,7 @@ def get_ens_table(
|
||||
check = bulk_checks_metadata[finding.check_metadata.CheckID]
|
||||
check_compliances = check.Compliance
|
||||
for compliance in check_compliances:
|
||||
if compliance.Framework == "ENS" and compliance.Provider == "AWS":
|
||||
if compliance.Framework == "ENS":
|
||||
for requirement in compliance.Requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
marco_categoria = f"{attribute.Marco}/{attribute.Categoria}"
|
||||
@@ -95,11 +95,12 @@ def get_ens_table(
|
||||
print(
|
||||
f"\nEstado de Cumplimiento de {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL}:"
|
||||
)
|
||||
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
|
||||
overview_table = [
|
||||
[
|
||||
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) NO CUMPLE{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) CUMPLE{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) NO CUMPLE{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) CUMPLE{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
]
|
||||
]
|
||||
print(tabulate(overview_table, tablefmt="rounded_grid"))
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.compliance.ens.models import AzureENSModel
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class AzureENS(ComplianceOutput):
|
||||
"""
|
||||
This class represents the Azure ENS compliance output.
|
||||
|
||||
Attributes:
|
||||
- _data (list): A list to store transformed data from findings.
|
||||
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
|
||||
|
||||
Methods:
|
||||
- transform: Transforms findings into Azure ENS compliance format.
|
||||
"""
|
||||
|
||||
def transform(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
compliance: Compliance,
|
||||
compliance_name: str,
|
||||
) -> None:
|
||||
"""
|
||||
Transforms a list of findings into AWS ENS compliance format.
|
||||
|
||||
Parameters:
|
||||
- findings (list): A list of findings.
|
||||
- compliance (Compliance): A compliance model.
|
||||
- compliance_name (str): The name of the compliance model.
|
||||
|
||||
Returns:
|
||||
- None
|
||||
"""
|
||||
for finding in findings:
|
||||
# Get the compliance requirements for the finding
|
||||
finding_requirements = finding.compliance.get(compliance_name, [])
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AzureENSModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
SubscriptionId=finding.account_name,
|
||||
Location=finding.region,
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
|
||||
Requirements_Attributes_Marco=attribute.Marco,
|
||||
Requirements_Attributes_Categoria=attribute.Categoria,
|
||||
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
|
||||
Requirements_Attributes_Nivel=attribute.Nivel,
|
||||
Requirements_Attributes_Tipo=attribute.Tipo,
|
||||
Requirements_Attributes_Dimensiones=",".join(
|
||||
attribute.Dimensiones
|
||||
),
|
||||
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
|
||||
Requirements_Attributes_Dependencias=",".join(
|
||||
attribute.Dependencias
|
||||
),
|
||||
Status=finding.status,
|
||||
StatusExtended=finding.status_extended,
|
||||
ResourceId=finding.resource_uid,
|
||||
ResourceName=finding.resource_name,
|
||||
CheckId=finding.check_id,
|
||||
Muted=finding.muted,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
# Add manual requirements to the compliance output
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AzureENSModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
SubscriptionId="",
|
||||
Location="",
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
|
||||
Requirements_Attributes_Marco=attribute.Marco,
|
||||
Requirements_Attributes_Categoria=attribute.Categoria,
|
||||
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
|
||||
Requirements_Attributes_Nivel=attribute.Nivel,
|
||||
Requirements_Attributes_Tipo=attribute.Tipo,
|
||||
Requirements_Attributes_Dimensiones=",".join(
|
||||
attribute.Dimensiones
|
||||
),
|
||||
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
|
||||
Requirements_Attributes_Dependencias=",".join(
|
||||
attribute.Dependencias
|
||||
),
|
||||
Status="MANUAL",
|
||||
StatusExtended="Manual check",
|
||||
ResourceId="manual_check",
|
||||
ResourceName="Manual check",
|
||||
CheckId="manual",
|
||||
Muted=False,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
@@ -0,0 +1,103 @@
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.compliance.ens.models import GCPENSModel
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class GCPENS(ComplianceOutput):
|
||||
"""
|
||||
This class represents the GCP ENS compliance output.
|
||||
|
||||
Attributes:
|
||||
- _data (list): A list to store transformed data from findings.
|
||||
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
|
||||
|
||||
Methods:
|
||||
- transform: Transforms findings into GCP ENS compliance format.
|
||||
"""
|
||||
|
||||
def transform(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
compliance: Compliance,
|
||||
compliance_name: str,
|
||||
) -> None:
|
||||
"""
|
||||
Transforms a list of findings into AWS ENS compliance format.
|
||||
|
||||
Parameters:
|
||||
- findings (list): A list of findings.
|
||||
- compliance (Compliance): A compliance model.
|
||||
- compliance_name (str): The name of the compliance model.
|
||||
|
||||
Returns:
|
||||
- None
|
||||
"""
|
||||
for finding in findings:
|
||||
# Get the compliance requirements for the finding
|
||||
finding_requirements = finding.compliance.get(compliance_name, [])
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = GCPENSModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
ProjectId=finding.account_uid,
|
||||
Location=finding.region,
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
|
||||
Requirements_Attributes_Marco=attribute.Marco,
|
||||
Requirements_Attributes_Categoria=attribute.Categoria,
|
||||
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
|
||||
Requirements_Attributes_Nivel=attribute.Nivel,
|
||||
Requirements_Attributes_Tipo=attribute.Tipo,
|
||||
Requirements_Attributes_Dimensiones=",".join(
|
||||
attribute.Dimensiones
|
||||
),
|
||||
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
|
||||
Requirements_Attributes_Dependencias=",".join(
|
||||
attribute.Dependencias
|
||||
),
|
||||
Status=finding.status,
|
||||
StatusExtended=finding.status_extended,
|
||||
ResourceId=finding.resource_uid,
|
||||
ResourceName=finding.resource_name,
|
||||
CheckId=finding.check_id,
|
||||
Muted=finding.muted,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
# Add manual requirements to the compliance output
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = GCPENSModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
ProjectId="",
|
||||
Location="",
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
|
||||
Requirements_Attributes_Marco=attribute.Marco,
|
||||
Requirements_Attributes_Categoria=attribute.Categoria,
|
||||
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
|
||||
Requirements_Attributes_Nivel=attribute.Nivel,
|
||||
Requirements_Attributes_Tipo=attribute.Tipo,
|
||||
Requirements_Attributes_Dimensiones=",".join(
|
||||
attribute.Dimensiones
|
||||
),
|
||||
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
|
||||
Requirements_Attributes_Dependencias=",".join(
|
||||
attribute.Dependencias
|
||||
),
|
||||
Status="MANUAL",
|
||||
StatusExtended="Manual check",
|
||||
ResourceId="manual_check",
|
||||
ResourceName="Manual check",
|
||||
CheckId="manual",
|
||||
Muted=False,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
@@ -28,3 +28,61 @@ class AWSENSModel(BaseModel):
|
||||
CheckId: str
|
||||
Muted: bool
|
||||
ResourceName: str
|
||||
|
||||
|
||||
class AzureENSModel(BaseModel):
|
||||
"""
|
||||
AzureENSModel generates a finding's output in CSV ENS format for Azure.
|
||||
"""
|
||||
|
||||
Provider: str
|
||||
Description: str
|
||||
SubscriptionId: str
|
||||
Location: str
|
||||
AssessmentDate: str
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_IdGrupoControl: str
|
||||
Requirements_Attributes_Marco: str
|
||||
Requirements_Attributes_Categoria: str
|
||||
Requirements_Attributes_DescripcionControl: str
|
||||
Requirements_Attributes_Nivel: str
|
||||
Requirements_Attributes_Tipo: str
|
||||
Requirements_Attributes_Dimensiones: str
|
||||
Requirements_Attributes_ModoEjecucion: str
|
||||
Requirements_Attributes_Dependencias: str
|
||||
Status: str
|
||||
StatusExtended: str
|
||||
ResourceId: str
|
||||
CheckId: str
|
||||
Muted: bool
|
||||
ResourceName: str
|
||||
|
||||
|
||||
class GCPENSModel(BaseModel):
|
||||
"""
|
||||
GCPENSModel generates a finding's output in CSV ENS format for GCP.
|
||||
"""
|
||||
|
||||
Provider: str
|
||||
Description: str
|
||||
ProjectId: str
|
||||
Location: str
|
||||
AssessmentDate: str
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_IdGrupoControl: str
|
||||
Requirements_Attributes_Marco: str
|
||||
Requirements_Attributes_Categoria: str
|
||||
Requirements_Attributes_DescripcionControl: str
|
||||
Requirements_Attributes_Nivel: str
|
||||
Requirements_Attributes_Tipo: str
|
||||
Requirements_Attributes_Dimensiones: str
|
||||
Requirements_Attributes_ModoEjecucion: str
|
||||
Requirements_Attributes_Dependencias: str
|
||||
Status: str
|
||||
StatusExtended: str
|
||||
ResourceId: str
|
||||
CheckId: str
|
||||
Muted: bool
|
||||
ResourceName: str
|
||||
@@ -39,11 +39,12 @@ def get_generic_compliance_table(
|
||||
print(
|
||||
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
|
||||
)
|
||||
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
|
||||
overview_table = [
|
||||
[
|
||||
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
]
|
||||
]
|
||||
print(tabulate(overview_table, tablefmt="rounded_grid"))
|
||||
|
||||
@@ -45,6 +45,8 @@ class AWSISO27001(ComplianceOutput):
|
||||
AccountId=finding.account_uid,
|
||||
Region=finding.region,
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Category=attribute.Category,
|
||||
Requirements_Attributes_Objetive_ID=attribute.Objetive_ID,
|
||||
Requirements_Attributes_Objetive_Name=attribute.Objetive_Name,
|
||||
@@ -67,6 +69,8 @@ class AWSISO27001(ComplianceOutput):
|
||||
AccountId="",
|
||||
Region="",
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Category=attribute.Category,
|
||||
Requirements_Attributes_Objetive_ID=attribute.Objetive_ID,
|
||||
Requirements_Attributes_Objetive_Name=attribute.Objetive_Name,
|
||||
|
||||
@@ -11,6 +11,8 @@ class AWSISO27001Model(BaseModel):
|
||||
AccountId: str
|
||||
Region: str
|
||||
AssessmentDate: str
|
||||
Requirements_Id: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_Category: str
|
||||
Requirements_Attributes_Objetive_ID: str
|
||||
Requirements_Attributes_Objetive_Name: str
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,90 @@
|
||||
from colorama import Fore, Style
|
||||
from tabulate import tabulate
|
||||
|
||||
from prowler.config.config import orange_color
|
||||
|
||||
|
||||
def get_kisa_ismsp_table(
|
||||
findings: list,
|
||||
bulk_checks_metadata: dict,
|
||||
compliance_framework: str,
|
||||
output_filename: str,
|
||||
output_directory: str,
|
||||
compliance_overview: bool,
|
||||
):
|
||||
sections = {}
|
||||
kisa_ismsp_compliance_table = {
|
||||
"Provider": [],
|
||||
"Section": [],
|
||||
"Status": [],
|
||||
"Muted": [],
|
||||
}
|
||||
pass_count = []
|
||||
fail_count = []
|
||||
muted_count = []
|
||||
for index, finding in enumerate(findings):
|
||||
check = bulk_checks_metadata[finding.check_metadata.CheckID]
|
||||
check_compliances = check.Compliance
|
||||
for compliance in check_compliances:
|
||||
if (
|
||||
compliance.Framework.startswith("KISA")
|
||||
and compliance.Version in compliance_framework
|
||||
):
|
||||
for requirement in compliance.Requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
section = attribute.Section
|
||||
# Check if Section exists
|
||||
if section not in sections:
|
||||
sections[section] = {
|
||||
"Status": f"{Fore.GREEN}PASS{Style.RESET_ALL}",
|
||||
"Muted": 0,
|
||||
}
|
||||
if finding.muted:
|
||||
if index not in muted_count:
|
||||
muted_count.append(index)
|
||||
sections[section]["Muted"] += 1
|
||||
else:
|
||||
if finding.status == "FAIL" and index not in fail_count:
|
||||
fail_count.append(index)
|
||||
elif finding.status == "PASS" and index not in pass_count:
|
||||
pass_count.append(index)
|
||||
|
||||
# Add results to table
|
||||
sections = dict(sorted(sections.items()))
|
||||
for section in sections:
|
||||
kisa_ismsp_compliance_table["Provider"].append(compliance.Provider)
|
||||
kisa_ismsp_compliance_table["Section"].append(section)
|
||||
kisa_ismsp_compliance_table["Muted"].append(
|
||||
f"{orange_color}{sections[section]['Muted']}{Style.RESET_ALL}"
|
||||
)
|
||||
if len(fail_count) + len(pass_count) + len(muted_count) > 1:
|
||||
print(
|
||||
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
|
||||
)
|
||||
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
|
||||
overview_table = [
|
||||
[
|
||||
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
]
|
||||
]
|
||||
print(tabulate(overview_table, tablefmt="rounded_grid"))
|
||||
if not compliance_overview:
|
||||
print(
|
||||
f"\nFramework {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Results:"
|
||||
)
|
||||
print(
|
||||
tabulate(
|
||||
kisa_ismsp_compliance_table,
|
||||
headers="keys",
|
||||
tablefmt="rounded_grid",
|
||||
)
|
||||
)
|
||||
print(
|
||||
f"{Style.BRIGHT}* Only sections containing results appear.{Style.RESET_ALL}"
|
||||
)
|
||||
print(f"\nDetailed results of {compliance_framework.upper()} are in:")
|
||||
print(
|
||||
f" - CSV: {output_directory}/compliance/{output_filename}_{compliance_framework}.csv\n"
|
||||
)
|
||||
@@ -0,0 +1,93 @@
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.compliance.kisa_ismsp.models import AWSKISAISMSPModel
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class AWSKISAISMSP(ComplianceOutput):
|
||||
"""
|
||||
This class represents the AWS KISA-ISMS-P compliance output.
|
||||
|
||||
Attributes:
|
||||
- _data (list): A list to store transformed data from findings.
|
||||
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
|
||||
|
||||
Methods:
|
||||
- transform: Transforms findings into AWS KISA-ISMS-P compliance format.
|
||||
"""
|
||||
|
||||
def transform(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
compliance: Compliance,
|
||||
compliance_name: str,
|
||||
) -> None:
|
||||
"""
|
||||
Transforms a list of findings into AWS KISA-ISMS-P compliance format.
|
||||
|
||||
Parameters:
|
||||
- findings (list): A list of findings.
|
||||
- compliance (Compliance): A compliance model.
|
||||
- compliance_name (str): The name of the compliance model.
|
||||
|
||||
Returns:
|
||||
- None
|
||||
"""
|
||||
for finding in findings:
|
||||
# Get the compliance requirements for the finding
|
||||
finding_requirements = finding.compliance.get(compliance_name, [])
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AWSKISAISMSPModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
AccountId=finding.account_uid,
|
||||
Region=finding.region,
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Domain=attribute.Domain,
|
||||
Requirements_Attributes_Subdomain=attribute.Subdomain,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_AuditChecklist=attribute.AuditChecklist,
|
||||
Requirements_Attributes_RelatedRegulations=attribute.RelatedRegulations,
|
||||
Requirements_Attributes_AuditEvidence=attribute.AuditEvidence,
|
||||
Requirements_Attributes_NonComplianceCases=attribute.NonComplianceCases,
|
||||
Status=finding.status,
|
||||
StatusExtended=finding.status_extended,
|
||||
ResourceId=finding.resource_uid,
|
||||
ResourceName=finding.resource_name,
|
||||
CheckId=finding.check_id,
|
||||
Muted=finding.muted,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
# Add manual requirements to the compliance output
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AWSKISAISMSPModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
AccountId="",
|
||||
Region="",
|
||||
AssessmentDate=str(finding.timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Attributes_Domain=attribute.Domain,
|
||||
Requirements_Attributes_Subdomain=attribute.Subdomain,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_AuditChecklist=attribute.AuditChecklist,
|
||||
Requirements_Attributes_RelatedRegulations=attribute.RelatedRegulations,
|
||||
Requirements_Attributes_AuditEvidence=attribute.AuditEvidence,
|
||||
Requirements_Attributes_NonComplianceCases=attribute.NonComplianceCases,
|
||||
Status="MANUAL",
|
||||
StatusExtended="Manual check",
|
||||
ResourceId="manual_check",
|
||||
ResourceName="Manual check",
|
||||
CheckId="manual",
|
||||
Muted=False,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AWSKISAISMSPModel(BaseModel):
|
||||
"""
|
||||
The AWS KISA-ISMS-P Model outputs findings in a format compliant with the AWS KISA-ISMS-P standard
|
||||
"""
|
||||
|
||||
Provider: str
|
||||
Description: str
|
||||
AccountId: str
|
||||
Region: str
|
||||
AssessmentDate: str
|
||||
Requirements_Id: str
|
||||
Requirements_Name: str
|
||||
Requirements_Description: str
|
||||
Requirements_Attributes_Domain: str
|
||||
Requirements_Attributes_Subdomain: str
|
||||
Requirements_Attributes_Section: str
|
||||
Requirements_Attributes_AuditChecklist: Optional[list[str]]
|
||||
Requirements_Attributes_RelatedRegulations: Optional[list[str]]
|
||||
Requirements_Attributes_AuditEvidence: Optional[list[str]]
|
||||
Requirements_Attributes_NonComplianceCases: Optional[list[str]]
|
||||
Status: str
|
||||
StatusExtended: str
|
||||
ResourceId: str
|
||||
ResourceName: str
|
||||
CheckId: str
|
||||
Muted: bool
|
||||
@@ -69,11 +69,12 @@ def get_mitre_attack_table(
|
||||
print(
|
||||
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
|
||||
)
|
||||
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
|
||||
overview_table = [
|
||||
[
|
||||
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
]
|
||||
]
|
||||
print(tabulate(overview_table, tablefmt="rounded_grid"))
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from csv import DictWriter
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
from prowler.lib.outputs.output import Output
|
||||
from prowler.lib.outputs.utils import unroll_dict
|
||||
from prowler.lib.outputs.utils import unroll_dict, unroll_list
|
||||
|
||||
|
||||
class CSV(Output):
|
||||
def transform(self, findings: list[Finding]) -> None:
|
||||
def transform(self, findings: List[Finding]) -> None:
|
||||
"""Transforms the findings into the CSV format.
|
||||
|
||||
Args:
|
||||
@@ -16,16 +17,68 @@ class CSV(Output):
|
||||
"""
|
||||
try:
|
||||
for finding in findings:
|
||||
finding_dict = {k.upper(): v for k, v in finding.dict().items()}
|
||||
finding_dict["RESOURCE_TAGS"] = unroll_dict(finding.resource_tags)
|
||||
finding_dict["COMPLIANCE"] = unroll_dict(
|
||||
finding.compliance, separator=": "
|
||||
finding_dict = {}
|
||||
finding_dict["AUTH_METHOD"] = finding.auth_method
|
||||
finding_dict["TIMESTAMP"] = finding.timestamp
|
||||
finding_dict["ACCOUNT_UID"] = finding.account_uid
|
||||
finding_dict["ACCOUNT_NAME"] = finding.account_name
|
||||
finding_dict["ACCOUNT_EMAIL"] = finding.account_email
|
||||
finding_dict["ACCOUNT_ORGANIZATION_UID"] = (
|
||||
finding.account_organization_uid
|
||||
)
|
||||
finding_dict["ACCOUNT_ORGANIZATION_NAME"] = (
|
||||
finding.account_organization_name
|
||||
)
|
||||
finding_dict["ACCOUNT_TAGS"] = unroll_dict(
|
||||
finding.account_tags, separator=":"
|
||||
)
|
||||
finding_dict["FINDING_UID"] = finding.uid
|
||||
finding_dict["PROVIDER"] = finding.metadata.Provider
|
||||
finding_dict["CHECK_ID"] = finding.metadata.CheckID
|
||||
finding_dict["CHECK_TITLE"] = finding.metadata.CheckTitle
|
||||
finding_dict["CHECK_TYPE"] = unroll_list(finding.metadata.CheckType)
|
||||
finding_dict["STATUS"] = finding.status.value
|
||||
finding_dict["SEVERITY"] = finding.severity.value
|
||||
finding_dict["STATUS_EXTENDED"] = finding.status_extended
|
||||
finding_dict["MUTED"] = finding.muted
|
||||
finding_dict["SERVICE_NAME"] = finding.metadata.ServiceName
|
||||
finding_dict["SUBSERVICE_NAME"] = finding.metadata.SubServiceName
|
||||
finding_dict["SEVERITY"] = finding.metadata.Severity.value
|
||||
finding_dict["RESOURCE_TYPE"] = finding.metadata.ResourceType
|
||||
finding_dict["RESOURCE_UID"] = finding.resource_uid
|
||||
finding_dict["RESOURCE_NAME"] = finding.resource_name
|
||||
finding_dict["RESOURCE_DETAILS"] = finding.resource_details
|
||||
finding_dict["RESOURCE_TAGS"] = unroll_dict(finding.resource_tags)
|
||||
finding_dict["PARTITION"] = finding.partition
|
||||
finding_dict["REGION"] = finding.region
|
||||
finding_dict["DESCRIPTION"] = finding.metadata.Description
|
||||
finding_dict["RISK"] = finding.metadata.Risk
|
||||
finding_dict["RELATED_URL"] = finding.metadata.RelatedUrl
|
||||
finding_dict["REMEDIATION_RECOMMENDATION_TEXT"] = (
|
||||
finding.metadata.Remediation.Recommendation.Text
|
||||
)
|
||||
finding_dict["REMEDIATION_RECOMMENDATION_URL"] = (
|
||||
finding.metadata.Remediation.Recommendation.Url
|
||||
)
|
||||
finding_dict["REMEDIATION_CODE_NATIVEIAC"] = (
|
||||
finding.metadata.Remediation.Code.NativeIaC
|
||||
)
|
||||
finding_dict["REMEDIATION_CODE_TERRAFORM"] = (
|
||||
finding.metadata.Remediation.Code.Terraform
|
||||
)
|
||||
finding_dict["REMEDIATION_CODE_CLI"] = (
|
||||
finding.metadata.Remediation.Code.CLI
|
||||
)
|
||||
finding_dict["REMEDIATION_CODE_OTHER"] = (
|
||||
finding.metadata.Remediation.Code.Other
|
||||
)
|
||||
finding_dict["COMPLIANCE"] = unroll_dict(
|
||||
finding.compliance, separator=": "
|
||||
)
|
||||
finding_dict["CATEGORIES"] = unroll_list(finding.metadata.Categories)
|
||||
finding_dict["DEPENDS_ON"] = unroll_list(finding.metadata.DependsOn)
|
||||
finding_dict["RELATED_TO"] = unroll_list(finding.metadata.RelatedTo)
|
||||
finding_dict["NOTES"] = finding.metadata.Notes
|
||||
finding_dict["PROWLER_VERSION"] = finding.prowler_version
|
||||
self._data.append(finding_dict)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
|
||||
+124
-71
@@ -1,34 +1,17 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Optional, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from prowler.config.config import prowler_version
|
||||
from prowler.lib.check.models import Check_Report
|
||||
from prowler.lib.check.models import Check_Report, CheckMetadata
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.outputs.common import (
|
||||
fill_common_finding_data,
|
||||
get_provider_data_mapping,
|
||||
)
|
||||
from prowler.lib.outputs.common import Status, fill_common_finding_data
|
||||
from prowler.lib.outputs.compliance.compliance import get_check_compliance
|
||||
from prowler.lib.utils.utils import dict_to_lowercase, get_nested_attribute
|
||||
from prowler.providers.common.provider import Provider
|
||||
|
||||
|
||||
class Status(str, Enum):
|
||||
PASS = "PASS"
|
||||
FAIL = "FAIL"
|
||||
MANUAL = "MANUAL"
|
||||
|
||||
|
||||
class Severity(str, Enum):
|
||||
critical = "critical"
|
||||
high = "high"
|
||||
medium = "medium"
|
||||
low = "low"
|
||||
informational = "informational"
|
||||
|
||||
|
||||
class Finding(BaseModel):
|
||||
"""
|
||||
Represents the output model for a finding across different providers.
|
||||
@@ -41,81 +24,130 @@ class Finding(BaseModel):
|
||||
auth_method: str
|
||||
timestamp: Union[int, datetime]
|
||||
account_uid: str
|
||||
# Optional since it depends on permissions
|
||||
account_name: Optional[str]
|
||||
# Optional since it depends on permissions
|
||||
account_email: Optional[str]
|
||||
# Optional since it depends on permissions
|
||||
account_organization_uid: Optional[str]
|
||||
# Optional since it depends on permissions
|
||||
account_organization_name: Optional[str]
|
||||
# Optional since it depends on permissions
|
||||
account_name: Optional[str] = None
|
||||
account_email: Optional[str] = None
|
||||
account_organization_uid: Optional[str] = None
|
||||
account_organization_name: Optional[str] = None
|
||||
metadata: CheckMetadata
|
||||
account_tags: dict = {}
|
||||
finding_uid: str
|
||||
provider: str
|
||||
check_id: str
|
||||
check_title: str
|
||||
check_type: str
|
||||
uid: str
|
||||
status: Status
|
||||
status_extended: str
|
||||
muted: bool = False
|
||||
service_name: str
|
||||
subservice_name: str
|
||||
severity: Severity
|
||||
resource_type: str
|
||||
resource_uid: str
|
||||
resource_name: str
|
||||
resource_details: str
|
||||
resource_tags: dict = {}
|
||||
# Only present for AWS and Azure
|
||||
partition: Optional[str]
|
||||
resource_tags: dict = Field(default_factory=dict)
|
||||
partition: Optional[str] = None
|
||||
region: str
|
||||
description: str
|
||||
risk: str
|
||||
related_url: str
|
||||
remediation_recommendation_text: str
|
||||
remediation_recommendation_url: str
|
||||
remediation_code_nativeiac: str
|
||||
remediation_code_terraform: str
|
||||
remediation_code_cli: str
|
||||
remediation_code_other: str
|
||||
compliance: dict
|
||||
categories: str
|
||||
depends_on: str
|
||||
related_to: str
|
||||
notes: str
|
||||
prowler_version: str = prowler_version
|
||||
raw: dict = Field(default_factory=dict)
|
||||
|
||||
@property
|
||||
def provider(self) -> str:
|
||||
"""
|
||||
Returns the provider from the finding check's metadata.
|
||||
"""
|
||||
return self.metadata.Provider
|
||||
|
||||
@property
|
||||
def check_id(self) -> str:
|
||||
"""
|
||||
Returns the ID from the finding check's metadata.
|
||||
"""
|
||||
return self.metadata.CheckID
|
||||
|
||||
@property
|
||||
def severity(self) -> str:
|
||||
"""
|
||||
Returns the severity from the finding check's metadata.
|
||||
"""
|
||||
return self.metadata.Severity
|
||||
|
||||
@property
|
||||
def resource_type(self) -> str:
|
||||
"""
|
||||
Returns the resource type from the finding check's metadata.
|
||||
"""
|
||||
return self.metadata.ResourceType
|
||||
|
||||
@property
|
||||
def service_name(self) -> str:
|
||||
"""
|
||||
Returns the service name from the finding check's metadata.
|
||||
"""
|
||||
return self.metadata.ServiceName
|
||||
|
||||
def get_metadata(self) -> dict:
|
||||
"""
|
||||
Retrieves the metadata of the object and returns it as a dictionary with all keys in lowercase.
|
||||
Returns:
|
||||
dict: A dictionary containing the metadata with keys converted to lowercase.
|
||||
"""
|
||||
|
||||
return dict_to_lowercase(self.metadata.dict())
|
||||
|
||||
@classmethod
|
||||
def generate_output(
|
||||
cls, provider: Provider, check_output: Check_Report
|
||||
cls, provider: Provider, check_output: Check_Report, output_options
|
||||
) -> "Finding":
|
||||
"""Generates the output for a finding based on the provider and output options
|
||||
|
||||
Args:
|
||||
provider (Provider): the provider object
|
||||
check_output (Check_Report): the check output object
|
||||
output_options: the output options object, depending on the provider
|
||||
Returns:
|
||||
finding_output (Finding): the finding output object
|
||||
|
||||
"""
|
||||
output_options = provider.output_options
|
||||
# TODO: think about get_provider_data_mapping
|
||||
provider_data_mapping = get_provider_data_mapping(provider)
|
||||
# TODO: move fill_common_finding_data
|
||||
common_finding_data = fill_common_finding_data(
|
||||
check_output, output_options.unix_timestamp
|
||||
)
|
||||
unix_timestamp = False
|
||||
if hasattr(output_options, "unix_timestamp"):
|
||||
unix_timestamp = output_options.unix_timestamp
|
||||
|
||||
common_finding_data = fill_common_finding_data(check_output, unix_timestamp)
|
||||
output_data = {}
|
||||
output_data.update(provider_data_mapping)
|
||||
output_data.update(common_finding_data)
|
||||
|
||||
bulk_checks_metadata = {}
|
||||
if hasattr(output_options, "bulk_checks_metadata"):
|
||||
bulk_checks_metadata = output_options.bulk_checks_metadata
|
||||
|
||||
output_data["compliance"] = get_check_compliance(
|
||||
check_output, provider.type, output_options.bulk_checks_metadata
|
||||
check_output, provider.type, bulk_checks_metadata
|
||||
)
|
||||
try:
|
||||
output_data["provider"] = provider.type
|
||||
|
||||
if provider.type == "aws":
|
||||
output_data["account_uid"] = get_nested_attribute(
|
||||
provider, "identity.account"
|
||||
)
|
||||
output_data["account_name"] = get_nested_attribute(
|
||||
provider, "organizations_metadata.account_name"
|
||||
)
|
||||
output_data["account_email"] = get_nested_attribute(
|
||||
provider, "organizations_metadata.account_email"
|
||||
)
|
||||
output_data["account_organization_uid"] = get_nested_attribute(
|
||||
provider, "organizations_metadata.organization_arn"
|
||||
)
|
||||
output_data["account_organization_name"] = get_nested_attribute(
|
||||
provider, "organizations_metadata.organization_id"
|
||||
)
|
||||
output_data["account_tags"] = get_nested_attribute(
|
||||
provider, "organizations_metadata.account_tags"
|
||||
)
|
||||
output_data["partition"] = get_nested_attribute(
|
||||
provider, "identity.partition"
|
||||
)
|
||||
|
||||
# TODO: probably Organization UID is without the account id
|
||||
output_data["auth_method"] = f"profile: {output_data['auth_method']}"
|
||||
output_data["auth_method"] = (
|
||||
f"profile: {get_nested_attribute(provider, 'identity.profile')}"
|
||||
)
|
||||
output_data["resource_name"] = check_output.resource_id
|
||||
output_data["resource_uid"] = check_output.resource_arn
|
||||
output_data["region"] = check_output.region
|
||||
@@ -126,9 +158,9 @@ class Finding(BaseModel):
|
||||
f"{provider.identity.identity_type}: {provider.identity.identity_id}"
|
||||
)
|
||||
# Get the first tenant domain ID, just in case
|
||||
output_data["account_organization_uid"] = output_data[
|
||||
"account_organization_uid"
|
||||
][0]
|
||||
output_data["account_organization_uid"] = get_nested_attribute(
|
||||
provider, "identity.tenant_ids"
|
||||
)[0]
|
||||
output_data["account_uid"] = (
|
||||
output_data["account_organization_uid"]
|
||||
if "Tenant:" in check_output.subscription
|
||||
@@ -138,15 +170,33 @@ class Finding(BaseModel):
|
||||
output_data["resource_name"] = check_output.resource_name
|
||||
output_data["resource_uid"] = check_output.resource_id
|
||||
output_data["region"] = check_output.location
|
||||
# TODO: check the tenant_ids
|
||||
# TODO: we have to get the account organization, the tenant is not that
|
||||
output_data["account_organization_name"] = get_nested_attribute(
|
||||
provider, "identity.tenant_domain"
|
||||
)
|
||||
|
||||
output_data["partition"] = get_nested_attribute(
|
||||
provider, "region_config.name"
|
||||
)
|
||||
# TODO: pending to get the subscription tags
|
||||
# "account_tags": "organizations_metadata.account_details_tags",
|
||||
# TODO: store subscription_name + id pairs
|
||||
# "account_name": "organizations_metadata.account_details_name",
|
||||
# "account_email": "organizations_metadata.account_details_email",
|
||||
|
||||
elif provider.type == "gcp":
|
||||
output_data["auth_method"] = f"Principal: {output_data['auth_method']}"
|
||||
output_data["auth_method"] = (
|
||||
f"Principal: {get_nested_attribute(provider, 'identity.profile')}"
|
||||
)
|
||||
output_data["account_uid"] = provider.projects[
|
||||
check_output.project_id
|
||||
].id
|
||||
output_data["account_name"] = provider.projects[
|
||||
check_output.project_id
|
||||
].name
|
||||
# There is no concept as project email in GCP
|
||||
# "account_email": "organizations_metadata.account_details_email",
|
||||
output_data["account_tags"] = provider.projects[
|
||||
check_output.project_id
|
||||
].labels
|
||||
@@ -165,7 +215,7 @@ class Finding(BaseModel):
|
||||
check_output.project_id
|
||||
].organization.id
|
||||
# TODO: for now is None since we don't retrieve that data
|
||||
output_data["account_organization"] = provider.projects[
|
||||
output_data["account_organization_name"] = provider.projects[
|
||||
check_output.project_id
|
||||
].organization.display_name
|
||||
|
||||
@@ -177,12 +227,15 @@ class Finding(BaseModel):
|
||||
output_data["resource_name"] = check_output.resource_name
|
||||
output_data["resource_uid"] = check_output.resource_id
|
||||
output_data["account_name"] = f"context: {provider.identity.context}"
|
||||
output_data["account_uid"] = get_nested_attribute(
|
||||
provider, "identity.cluster"
|
||||
)
|
||||
output_data["region"] = f"namespace: {check_output.namespace}"
|
||||
|
||||
# check_output Unique ID
|
||||
# TODO: move this to a function
|
||||
# TODO: in Azure, GCP and K8s there are fidings without resource_name
|
||||
output_data["finding_uid"] = (
|
||||
output_data["uid"] = (
|
||||
f"prowler-{provider.type}-{check_output.check_metadata.CheckID}-{output_data['account_uid']}-"
|
||||
f"{output_data['region']}-{output_data['resource_name']}"
|
||||
)
|
||||
|
||||
@@ -39,16 +39,16 @@ class HTML(Output):
|
||||
f"""
|
||||
<tr class="{row_class}">
|
||||
<td>{finding_status}</td>
|
||||
<td>{finding.severity.value}</td>
|
||||
<td>{finding.service_name}</td>
|
||||
<td>{finding.metadata.Severity.value}</td>
|
||||
<td>{finding.metadata.ServiceName}</td>
|
||||
<td>{finding.region.lower()}</td>
|
||||
<td>{finding.check_id.replace("_", "<wbr />_")}</td>
|
||||
<td>{finding.check_title}</td>
|
||||
<td>{finding.metadata.CheckID.replace("_", "<wbr />_")}</td>
|
||||
<td>{finding.metadata.CheckTitle}</td>
|
||||
<td>{finding.resource_uid.replace("<", "<").replace(">", ">").replace("_", "<wbr />_")}</td>
|
||||
<td>{parse_html_string(unroll_dict(finding.resource_tags))}</td>
|
||||
<td>{finding.status_extended.replace("<", "<").replace(">", ">").replace("_", "<wbr />_")}</td>
|
||||
<td><p class="show-read-more">{html.escape(finding.risk)}</p></td>
|
||||
<td><p class="show-read-more">{html.escape(finding.remediation_recommendation_text)}</p> <a class="read-more" href="{finding.remediation_recommendation_url}"><i class="fas fa-external-link-alt"></i></a></td>
|
||||
<td><p class="show-read-more">{html.escape(finding.metadata.Risk)}</p></td>
|
||||
<td><p class="show-read-more">{html.escape(finding.metadata.Remediation.Recommendation.Text)}</p> <a class="read-more" href="{finding.metadata.Remediation.Recommendation.Url}"><i class="fas fa-external-link-alt"></i></a></td>
|
||||
<td><p class="show-read-more">{parse_html_string(unroll_dict(finding.compliance, separator=": "))}</p></td>
|
||||
</tr>
|
||||
"""
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,231 @@
|
||||
from prowler.exceptions.exceptions import ProwlerException
|
||||
|
||||
|
||||
# Exceptions codes from 9000 to 9999 are reserved for Jira exceptions
|
||||
class JiraBaseException(ProwlerException):
|
||||
"""Base class for Jira exceptions."""
|
||||
|
||||
JIRA_ERROR_CODES = {
|
||||
(9000, "JiraNoProjectsError"): {
|
||||
"message": "No projects were found in Jira.",
|
||||
"remediation": "Please create a project in Jira.",
|
||||
},
|
||||
(9001, "JiraAuthenticationError"): {
|
||||
"message": "Failed to authenticate with Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again. Needed scopes are: read:jira-user read:jira-work write:jira-work",
|
||||
},
|
||||
(9002, "JiraTestConnectionError"): {
|
||||
"message": "Failed to connect to Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9003, "JiraCreateIssueError"): {
|
||||
"message": "Failed to create an issue in Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9004, "JiraGetProjectsError"): {
|
||||
"message": "Failed to get projects from Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9005, "JiraGetCloudIDError"): {
|
||||
"message": "Failed to get the cloud ID from Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9006, "JiraGetCloudIDNoResourcesError"): {
|
||||
"message": "No resources were found in Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9007, "JiraGetCloudIDResponseError"): {
|
||||
"message": "Failed to get the cloud ID from Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9008, "JiraRefreshTokenResponseError"): {
|
||||
"message": "Failed to refresh the access token, response code did not match 200.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9009, "JiraRefreshTokenError"): {
|
||||
"message": "Failed to refresh the access token.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9010, "JiraGetAccessTokenError"): {
|
||||
"message": "Failed to get the access token.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9011, "JiraGetAuthResponseError"): {
|
||||
"message": "Failed to authenticate with Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9012, "JiraGetProjectsResponseError"): {
|
||||
"message": "Failed to get projects from Jira, response code did not match 200.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9013, "JiraSendFindingsResponseError"): {
|
||||
"message": "Failed to send findings to Jira, response code did not match 201.",
|
||||
"remediation": "Please check the finding format and try again.",
|
||||
},
|
||||
(9014, "JiraGetAvailableIssueTypesError"): {
|
||||
"message": "Failed to get available issue types from Jira.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9015, "JiraGetAvailableIssueTypesResponseError"): {
|
||||
"message": "Failed to get available issue types from Jira, response code did not match 200.",
|
||||
"remediation": "Please check the connection settings and permissions and try again.",
|
||||
},
|
||||
(9016, "JiraInvalidIssueTypeError"): {
|
||||
"message": "The issue type is invalid.",
|
||||
"remediation": "Please check the issue type and try again.",
|
||||
},
|
||||
(9017, "JiraNoTokenError"): {
|
||||
"message": "No token was found.",
|
||||
"remediation": "Make sure the token is set when using the Jira integration.",
|
||||
},
|
||||
(9018, "JiraInvalidProjectKeyError"): {
|
||||
"message": "The project key is invalid.",
|
||||
"remediation": "Please check the project key and try again.",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
module = "Jira"
|
||||
error_info = self.JIRA_ERROR_CODES.get((code, self.__class__.__name__))
|
||||
if message:
|
||||
error_info["message"] = message
|
||||
super().__init__(
|
||||
code=code,
|
||||
source=module,
|
||||
file=file,
|
||||
original_exception=original_exception,
|
||||
error_info=error_info,
|
||||
)
|
||||
|
||||
|
||||
class JiraNoProjectsError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9000, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraAuthenticationError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9001, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraTestConnectionError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9002, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraCreateIssueError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9003, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetProjectsError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9004, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetCloudIDError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9005, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetCloudIDNoResourcesError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9006, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetCloudIDResponseError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9007, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraRefreshTokenResponseError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9008, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraRefreshTokenError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9009, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetAccessTokenError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9010, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetAuthResponseError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9011, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetProjectsResponseError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9012, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraSendFindingsResponseError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9013, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetAvailableIssueTypesError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9014, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraGetAvailableIssueTypesResponseError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9015, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraInvalidIssueTypeError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9016, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraNoTokenError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9017, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class JiraInvalidProjectKeyError(JiraBaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
9018, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
from py_ocsf_models.events.base_event import SeverityID, StatusID
|
||||
@@ -9,7 +10,6 @@ from py_ocsf_models.events.findings.detection_finding import (
|
||||
from py_ocsf_models.events.findings.finding import ActivityID, FindingInformation
|
||||
from py_ocsf_models.objects.account import Account, TypeID
|
||||
from py_ocsf_models.objects.cloud import Cloud
|
||||
from py_ocsf_models.objects.container import Container
|
||||
from py_ocsf_models.objects.group import Group
|
||||
from py_ocsf_models.objects.metadata import Metadata
|
||||
from py_ocsf_models.objects.organization import Organization
|
||||
@@ -37,7 +37,7 @@ class OCSF(Output):
|
||||
- transform(findings: List[Finding]) -> None: Transforms the findings into the OCSF Detection Finding format.
|
||||
- batch_write_data_to_file() -> None: Writes the findings to a file using the OCSF Detection Finding format using the `Output._file_descriptor`.
|
||||
- get_account_type_id_by_provider(provider: str) -> TypeID: Returns the TypeID based on the provider.
|
||||
- get_finding_status_id(status: str, muted: bool) -> StatusID: Returns the StatusID based on the status and muted values.
|
||||
- get_finding_status_id(muted: bool) -> StatusID: Returns the StatusID based on the muted value.
|
||||
|
||||
References:
|
||||
- OCSF: https://schema.ocsf.io/1.2.0/classes/detection_finding
|
||||
@@ -54,37 +54,50 @@ class OCSF(Output):
|
||||
for finding in findings:
|
||||
finding_activity = ActivityID.Create
|
||||
cloud_account_type = self.get_account_type_id_by_provider(
|
||||
finding.provider
|
||||
finding.metadata.Provider
|
||||
)
|
||||
finding_severity = getattr(
|
||||
SeverityID, finding.severity.capitalize(), SeverityID.Unknown
|
||||
)
|
||||
finding_status = self.get_finding_status_id(
|
||||
finding.status, finding.muted
|
||||
SeverityID,
|
||||
finding.metadata.Severity.capitalize(),
|
||||
SeverityID.Unknown,
|
||||
)
|
||||
finding_status = self.get_finding_status_id(finding.muted)
|
||||
|
||||
detection_finding = DetectionFinding(
|
||||
message=finding.status_extended,
|
||||
activity_id=finding_activity.value,
|
||||
activity_name=finding_activity.name,
|
||||
finding_info=FindingInformation(
|
||||
created_time=finding.timestamp,
|
||||
desc=finding.description,
|
||||
title=finding.check_title,
|
||||
uid=finding.finding_uid,
|
||||
created_time_dt=finding.timestamp,
|
||||
created_time=(
|
||||
int(finding.timestamp.timestamp())
|
||||
if isinstance(finding.timestamp, datetime)
|
||||
else finding.timestamp
|
||||
),
|
||||
desc=finding.metadata.Description,
|
||||
title=finding.metadata.CheckTitle,
|
||||
uid=finding.uid,
|
||||
name=finding.resource_name,
|
||||
product_uid="prowler",
|
||||
types=finding.metadata.CheckType,
|
||||
),
|
||||
time_dt=finding.timestamp,
|
||||
time=(
|
||||
int(finding.timestamp.timestamp())
|
||||
if isinstance(finding.timestamp, datetime)
|
||||
else finding.timestamp
|
||||
),
|
||||
event_time=finding.timestamp,
|
||||
remediation=Remediation(
|
||||
desc=finding.remediation_recommendation_text,
|
||||
desc=finding.metadata.Remediation.Recommendation.Text,
|
||||
references=list(
|
||||
filter(
|
||||
None,
|
||||
[
|
||||
finding.remediation_code_nativeiac,
|
||||
finding.remediation_code_terraform,
|
||||
finding.remediation_code_cli,
|
||||
finding.remediation_code_other,
|
||||
finding.remediation_recommendation_url,
|
||||
finding.metadata.Remediation.Code.NativeIaC,
|
||||
finding.metadata.Remediation.Code.Terraform,
|
||||
finding.metadata.Remediation.Code.CLI,
|
||||
finding.metadata.Remediation.Code.Other,
|
||||
finding.metadata.Remediation.Recommendation.Url,
|
||||
],
|
||||
)
|
||||
),
|
||||
@@ -95,60 +108,73 @@ class OCSF(Output):
|
||||
status=finding_status.name,
|
||||
status_code=finding.status,
|
||||
status_detail=finding.status_extended,
|
||||
risk_details=finding.risk,
|
||||
resources=[
|
||||
ResourceDetails(
|
||||
labels=unroll_dict_to_list(finding.resource_tags),
|
||||
name=finding.resource_name,
|
||||
uid=finding.resource_uid,
|
||||
group=Group(name=finding.service_name),
|
||||
type=finding.resource_type,
|
||||
# TODO: this should be included only if using the Cloud profile
|
||||
cloud_partition=finding.partition,
|
||||
region=finding.region,
|
||||
data={"details": finding.resource_details},
|
||||
)
|
||||
],
|
||||
risk_details=finding.metadata.Risk,
|
||||
resources=(
|
||||
[
|
||||
ResourceDetails(
|
||||
labels=unroll_dict_to_list(finding.resource_tags),
|
||||
name=finding.resource_name,
|
||||
uid=finding.resource_uid,
|
||||
group=Group(name=finding.metadata.ServiceName),
|
||||
type=finding.metadata.ResourceType,
|
||||
# TODO: this should be included only if using the Cloud profile
|
||||
cloud_partition=finding.partition,
|
||||
region=finding.region,
|
||||
data={"details": finding.resource_details},
|
||||
)
|
||||
]
|
||||
if finding.metadata.Provider != "kubernetes"
|
||||
else [
|
||||
ResourceDetails(
|
||||
labels=unroll_dict_to_list(finding.resource_tags),
|
||||
name=finding.resource_name,
|
||||
uid=finding.resource_uid,
|
||||
group=Group(name=finding.metadata.ServiceName),
|
||||
type=finding.metadata.ResourceType,
|
||||
data={"details": finding.resource_details},
|
||||
namespace=finding.region.replace("namespace: ", ""),
|
||||
)
|
||||
]
|
||||
),
|
||||
metadata=Metadata(
|
||||
event_code=finding.check_id,
|
||||
event_code=finding.metadata.CheckID,
|
||||
product=Product(
|
||||
uid="prowler",
|
||||
name="Prowler",
|
||||
vendor_name="Prowler",
|
||||
version=finding.prowler_version,
|
||||
),
|
||||
profiles=(
|
||||
["cloud", "datetime"]
|
||||
if finding.metadata.Provider != "kubernetes"
|
||||
else ["container", "datetime"]
|
||||
),
|
||||
tenant_uid=finding.account_organization_uid,
|
||||
),
|
||||
type_uid=DetectionFindingTypeID.Create,
|
||||
type_name=DetectionFindingTypeID.Create.name,
|
||||
type_name=f"Detection Finding: {DetectionFindingTypeID.Create.name}",
|
||||
unmapped={
|
||||
"check_type": finding.check_type,
|
||||
"related_url": finding.related_url,
|
||||
"categories": finding.categories,
|
||||
"depends_on": finding.depends_on,
|
||||
"related_to": finding.related_to,
|
||||
"notes": finding.notes,
|
||||
"related_url": finding.metadata.RelatedUrl,
|
||||
"categories": finding.metadata.Categories,
|
||||
"depends_on": finding.metadata.DependsOn,
|
||||
"related_to": finding.metadata.RelatedTo,
|
||||
"notes": finding.metadata.Notes,
|
||||
"compliance": finding.compliance,
|
||||
},
|
||||
)
|
||||
|
||||
if finding.provider == "kubernetes":
|
||||
detection_finding.container = Container(
|
||||
name=finding.resource_name,
|
||||
uid=finding.resource_uid,
|
||||
)
|
||||
# TODO: Get the PID of the namespace (we only have the name of the namespace)
|
||||
# detection_finding.namespace_pid=,
|
||||
else:
|
||||
if finding.provider != "kubernetes":
|
||||
detection_finding.cloud = Cloud(
|
||||
account=Account(
|
||||
name=finding.account_name,
|
||||
type_id=cloud_account_type.value,
|
||||
type=cloud_account_type.name,
|
||||
type=cloud_account_type.name.replace("_", " "),
|
||||
uid=finding.account_uid,
|
||||
labels=unroll_dict_to_list(finding.account_tags),
|
||||
),
|
||||
org=Organization(
|
||||
uid=finding.account_organization_uid,
|
||||
name=finding.account_organization_name,
|
||||
# TODO: add the org unit id and name
|
||||
),
|
||||
provider=finding.provider,
|
||||
region=finding.region,
|
||||
@@ -208,20 +234,17 @@ class OCSF(Output):
|
||||
return type_id
|
||||
|
||||
@staticmethod
|
||||
def get_finding_status_id(status: str, muted: bool) -> StatusID:
|
||||
def get_finding_status_id(muted: bool) -> StatusID:
|
||||
"""
|
||||
Returns the StatusID based on the status and muted values.
|
||||
Returns the StatusID based on the muted value.
|
||||
|
||||
Args:
|
||||
status (str): The status value
|
||||
muted (bool): The muted value
|
||||
|
||||
Returns:
|
||||
StatusID: The StatusID based on the status and muted values
|
||||
StatusID: The StatusID based on the muted value
|
||||
"""
|
||||
status_id = StatusID.Other
|
||||
if status == "FAIL":
|
||||
status_id = StatusID.New
|
||||
status_id = StatusID.New
|
||||
if muted:
|
||||
status_id = StatusID.Suppressed
|
||||
return status_id
|
||||
@@ -25,10 +25,12 @@ def stdout_report(finding, color, verbose, status, fix):
|
||||
)
|
||||
|
||||
|
||||
# TODO: Only pass check_findings, provider.output_options and provider.type
|
||||
def report(check_findings, provider):
|
||||
# TODO: Only pass check_findings, output_options and provider.type
|
||||
def report(check_findings, provider, output_options):
|
||||
try:
|
||||
output_options = provider.output_options
|
||||
verbose = False
|
||||
if hasattr(output_options, "verbose"):
|
||||
verbose = output_options.verbose
|
||||
if check_findings:
|
||||
# TO-DO Generic Function
|
||||
if provider.type == "aws":
|
||||
@@ -39,21 +41,27 @@ def report(check_findings, provider):
|
||||
|
||||
for finding in check_findings:
|
||||
# Print findings by stdout
|
||||
status = []
|
||||
if hasattr(output_options, "status"):
|
||||
status = output_options.status
|
||||
fixer = False
|
||||
if hasattr(output_options, "fixer"):
|
||||
fixer = output_options.fixer
|
||||
color = set_report_color(finding.status, finding.muted)
|
||||
stdout_report(
|
||||
finding,
|
||||
color,
|
||||
output_options.verbose,
|
||||
output_options.status,
|
||||
output_options.fixer,
|
||||
verbose,
|
||||
status,
|
||||
fixer,
|
||||
)
|
||||
|
||||
else: # No service resources in the whole account
|
||||
color = set_report_color("MANUAL")
|
||||
if output_options.verbose:
|
||||
if verbose:
|
||||
print(f"\t{color}INFO{Style.RESET_ALL} There are no resources")
|
||||
# Separator between findings and bar
|
||||
if output_options.verbose:
|
||||
if verbose:
|
||||
print()
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
@@ -82,9 +90,14 @@ def extract_findings_statistics(findings: list) -> dict:
|
||||
extract_findings_statistics takes a list of findings and returns the following dict with the aggregated statistics
|
||||
{
|
||||
"total_pass": 0,
|
||||
"total_muted_pass": 0,
|
||||
"total_fail": 0,
|
||||
"total_muted_fail": 0,
|
||||
"resources_count": 0,
|
||||
"findings_count": 0,
|
||||
"critical_failed_findings": [],
|
||||
"critical_passed_findings": []
|
||||
"all_fails_are_muted": False
|
||||
}
|
||||
"""
|
||||
logger.info("Extracting audit statistics...")
|
||||
@@ -96,18 +109,42 @@ def extract_findings_statistics(findings: list) -> dict:
|
||||
resources = set()
|
||||
findings_count = 0
|
||||
all_fails_are_muted = True
|
||||
critical_severity_pass = 0
|
||||
critical_severity_fail = 0
|
||||
high_severity_pass = 0
|
||||
high_severity_fail = 0
|
||||
medium_severity_pass = 0
|
||||
medium_severity_fail = 0
|
||||
low_severity_pass = 0
|
||||
low_severity_fail = 0
|
||||
|
||||
for finding in findings:
|
||||
# Save the resource_id
|
||||
resources.add(finding.resource_id)
|
||||
|
||||
if finding.status == "PASS":
|
||||
if finding.check_metadata.Severity == "critical":
|
||||
critical_severity_pass += 1
|
||||
if finding.check_metadata.Severity == "high":
|
||||
high_severity_pass += 1
|
||||
if finding.check_metadata.Severity == "medium":
|
||||
medium_severity_pass += 1
|
||||
if finding.check_metadata.Severity == "low":
|
||||
low_severity_pass += 1
|
||||
total_pass += 1
|
||||
findings_count += 1
|
||||
if finding.muted is True:
|
||||
muted_pass += 1
|
||||
|
||||
if finding.status == "FAIL":
|
||||
if finding.check_metadata.Severity == "critical":
|
||||
critical_severity_fail += 1
|
||||
if finding.check_metadata.Severity == "high":
|
||||
high_severity_fail += 1
|
||||
if finding.check_metadata.Severity == "medium":
|
||||
medium_severity_fail += 1
|
||||
if finding.check_metadata.Severity == "low":
|
||||
low_severity_fail += 1
|
||||
total_fail += 1
|
||||
findings_count += 1
|
||||
if finding.muted is True:
|
||||
@@ -121,6 +158,14 @@ def extract_findings_statistics(findings: list) -> dict:
|
||||
stats["total_muted_fail"] = muted_fail
|
||||
stats["resources_count"] = len(resources)
|
||||
stats["findings_count"] = findings_count
|
||||
stats["total_critical_severity_fail"] = critical_severity_fail
|
||||
stats["total_critical_severity_pass"] = critical_severity_pass
|
||||
stats["total_high_severity_fail"] = high_severity_fail
|
||||
stats["total_high_severity_pass"] = high_severity_pass
|
||||
stats["total_medium_severity_fail"] = medium_severity_fail
|
||||
stats["total_medium_severity_pass"] = medium_severity_pass
|
||||
stats["total_low_severity_fail"] = medium_severity_fail
|
||||
stats["total_low_severity_pass"] = medium_severity_pass
|
||||
stats["all_fails_are_muted"] = all_fails_are_muted
|
||||
|
||||
return stats
|
||||
File renamed without changes.
@@ -0,0 +1,61 @@
|
||||
from prowler.exceptions.exceptions import ProwlerException
|
||||
|
||||
|
||||
# Exceptions codes from 8000 to 8999 are reserved for Slack exceptions
|
||||
class SlackBaseException(ProwlerException):
|
||||
"""Base class for Slack errors."""
|
||||
|
||||
SLACK_ERROR_CODES = {
|
||||
(8000, "SlackClientError"): {
|
||||
"message": "Slack ClientError occurred",
|
||||
"remediation": "Check your Slack client configuration and permissions.",
|
||||
},
|
||||
(8001, "SlackNoCredentialsError"): {
|
||||
"message": "Invalid Slack credentials found",
|
||||
"remediation": "Some aspect of authentication cannot be validated. Either the provided token is invalid or the request originates from an IP address disallowed from making the request.",
|
||||
},
|
||||
(8002, "SlackChannelNotFound"): {
|
||||
"message": "Slack channel not found",
|
||||
"remediation": "Check the channel name and ensure it exists.",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
error_info = self.SLACK_ERROR_CODES.get((code, self.__class__.__name__))
|
||||
if message:
|
||||
error_info["message"] = message
|
||||
super().__init__(
|
||||
code,
|
||||
source="Slack",
|
||||
file=file,
|
||||
original_exception=original_exception,
|
||||
error_info=error_info,
|
||||
)
|
||||
|
||||
|
||||
class SlackCredentialsError(SlackBaseException):
|
||||
"""Base class for Slack credentials errors."""
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
super().__init__(code, file, original_exception, message)
|
||||
|
||||
|
||||
class SlackClientError(SlackCredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
8000, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class SlackNoCredentialsError(SlackCredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
8001, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class SlackChannelNotFound(SlackCredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
8002, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
Loaded 100 of 1569 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user