mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-26 05:48:03 +00:00
Compare commits
554 Commits
5.5.0
...
M365-testi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
221310ba94 | ||
|
|
33135f6c9a | ||
|
|
4aaacc9e16 | ||
|
|
165d09768f | ||
|
|
6b8af51c23 | ||
|
|
e40591a25f | ||
|
|
d6ac87341b | ||
|
|
d431af6186 | ||
|
|
7b3b728207 | ||
|
|
cfab4dcef5 | ||
|
|
cd48037670 | ||
|
|
ab7e352029 | ||
|
|
79ce899812 | ||
|
|
29d4159b8c | ||
|
|
aaf7188365 | ||
|
|
6cbba3c5b1 | ||
|
|
9510b4c3ef | ||
|
|
c7754c4331 | ||
|
|
7800320189 | ||
|
|
972c88e421 | ||
|
|
3ee420769a | ||
|
|
f8d9873655 | ||
|
|
3f5508847a | ||
|
|
ed54b39211 | ||
|
|
a6dd97b6bc | ||
|
|
b6e1551397 | ||
|
|
c8567b5d2b | ||
|
|
361269370a | ||
|
|
3d9d118d2c | ||
|
|
b50a91d3e6 | ||
|
|
0843c49475 | ||
|
|
3fe65b7a03 | ||
|
|
68369ca54f | ||
|
|
63f4b4f7d4 | ||
|
|
754a0748da | ||
|
|
aa3182ebc5 | ||
|
|
32d27df0ba | ||
|
|
6439f0a5f3 | ||
|
|
19476632ff | ||
|
|
d4c12e4632 | ||
|
|
52bd48168f | ||
|
|
c0d935e232 | ||
|
|
24dfd47329 | ||
|
|
fbae338689 | ||
|
|
186fd88f8c | ||
|
|
14ff34c00a | ||
|
|
a66fa394d3 | ||
|
|
931766fe08 | ||
|
|
c134914896 | ||
|
|
25dac080a5 | ||
|
|
910d39eee4 | ||
|
|
d604ae5569 | ||
|
|
42f46b0fb1 | ||
|
|
abb5864224 | ||
|
|
2e2a2bd89a | ||
|
|
f8ee841921 | ||
|
|
ceda8c76d2 | ||
|
|
afe0b7443f | ||
|
|
9b773897d2 | ||
|
|
d6ec4c2c96 | ||
|
|
14ef169e99 | ||
|
|
22141f9706 | ||
|
|
a5c6fee5b4 | ||
|
|
d3a5a5c0a1 | ||
|
|
5d81869de4 | ||
|
|
73ebf95d89 | ||
|
|
9f4574f4ff | ||
|
|
cb239b20ab | ||
|
|
3ef79588b4 | ||
|
|
61000e386b | ||
|
|
53cb57901f | ||
|
|
993ff4d78e | ||
|
|
8fb10fbbf7 | ||
|
|
11e834f639 | ||
|
|
62bf2fbb9c | ||
|
|
e57930d6c2 | ||
|
|
e0c417a466 | ||
|
|
b55f8efed1 | ||
|
|
7cbc60d977 | ||
|
|
5b7912b558 | ||
|
|
57fca3e54d | ||
|
|
e31c27b123 | ||
|
|
74f1da818e | ||
|
|
910cfa601b | ||
|
|
fe321c3f8a | ||
|
|
43de0d405f | ||
|
|
ac6ed31c8e | ||
|
|
9d47437de4 | ||
|
|
eb7a62ff77 | ||
|
|
67bc16b46d | ||
|
|
8552a578a0 | ||
|
|
a5d277e045 | ||
|
|
6dbf2ac606 | ||
|
|
b1569ac2f3 | ||
|
|
3d0145b522 | ||
|
|
44174526d6 | ||
|
|
0fd395ea83 | ||
|
|
5e9d4a80a1 | ||
|
|
e4d234fe03 | ||
|
|
3202184718 | ||
|
|
41e576f4f1 | ||
|
|
d8dce07019 | ||
|
|
2b0a3144c7 | ||
|
|
62fbce0b5e | ||
|
|
5a59bb335c | ||
|
|
2719991630 | ||
|
|
6a3b8c4674 | ||
|
|
191fbf0177 | ||
|
|
228dd2952a | ||
|
|
97db38aa25 | ||
|
|
dc953a6e22 | ||
|
|
51e796a48d | ||
|
|
024f1425df | ||
|
|
a7ed610da9 | ||
|
|
7ba99f22cd | ||
|
|
b8ce09ec34 | ||
|
|
c243110a49 | ||
|
|
ee27636f32 | ||
|
|
f2f41c9c44 | ||
|
|
9312890e6a | ||
|
|
9578281b4f | ||
|
|
08690068fc | ||
|
|
e06a33de84 | ||
|
|
6a3db10fda | ||
|
|
bbed445efa | ||
|
|
9d65fb0bf2 | ||
|
|
34f03ca110 | ||
|
|
87c038f0c2 | ||
|
|
b3014f03b1 | ||
|
|
d39598c9fc | ||
|
|
5ea9106259 | ||
|
|
bcc0b59de1 | ||
|
|
5d6ed640f0 | ||
|
|
dd1cc2d025 | ||
|
|
52e5cc23e4 | ||
|
|
76a8e2be1f | ||
|
|
d989425490 | ||
|
|
1e324b7ed2 | ||
|
|
e68aa62f94 | ||
|
|
332b98a1ab | ||
|
|
dd05ef7974 | ||
|
|
d6862766d3 | ||
|
|
f52d005e2d | ||
|
|
bf475234a5 | ||
|
|
cd5985c056 | ||
|
|
ce33dbf823 | ||
|
|
0a9d0688a7 | ||
|
|
24784f2ce5 | ||
|
|
7a1e611b88 | ||
|
|
3073150008 | ||
|
|
9923def4cb | ||
|
|
a7f612303f | ||
|
|
64c2a2217a | ||
|
|
4689d7a952 | ||
|
|
87cd143967 | ||
|
|
e37fd05d58 | ||
|
|
acc708bda5 | ||
|
|
c7460bb69c | ||
|
|
84b273dab9 | ||
|
|
bb7ce2157e | ||
|
|
07b9e1d3a4 | ||
|
|
96a879d761 | ||
|
|
283127c3f4 | ||
|
|
beeee80a0b | ||
|
|
06b62826b4 | ||
|
|
d0736af209 | ||
|
|
716c8c1a5f | ||
|
|
e6cdda1bd9 | ||
|
|
2747a633bc | ||
|
|
74118f5cfe | ||
|
|
598bdf28bb | ||
|
|
d75f681c87 | ||
|
|
c7956ede6a | ||
|
|
64f5a69e84 | ||
|
|
bfb15c34b8 | ||
|
|
638b3ac0cd | ||
|
|
9d6147a037 | ||
|
|
802c786ac2 | ||
|
|
c8be8dbd9a | ||
|
|
7053b2bb37 | ||
|
|
447bf832cd | ||
|
|
7c4571b55e | ||
|
|
eb7c16aba5 | ||
|
|
b09e83b171 | ||
|
|
bb149a30a7 | ||
|
|
d5be35af49 | ||
|
|
f6aa56d92b | ||
|
|
6a4df15c47 | ||
|
|
72de5fdb1b | ||
|
|
a7f55d06af | ||
|
|
97da78d4e7 | ||
|
|
c4f6161c73 | ||
|
|
db7ffea24d | ||
|
|
489b5abf82 | ||
|
|
3a55c2ee07 | ||
|
|
64d866271c | ||
|
|
1ab2a80eab | ||
|
|
89d4c521ba | ||
|
|
f2e19d377a | ||
|
|
2b7b887b87 | ||
|
|
44c70b5d01 | ||
|
|
7514484c42 | ||
|
|
9594c4c99f | ||
|
|
56445c9753 | ||
|
|
07419fd5e1 | ||
|
|
2e4dd12b41 | ||
|
|
fed2046c49 | ||
|
|
db79db4786 | ||
|
|
6f027e3c57 | ||
|
|
bdb877009f | ||
|
|
6564ec1ff5 | ||
|
|
443dc067b3 | ||
|
|
6221650c5f | ||
|
|
034d0fd1f4 | ||
|
|
e617ff0460 | ||
|
|
4b1ed607a7 | ||
|
|
137365a670 | ||
|
|
1891a1b24f | ||
|
|
e57e070866 | ||
|
|
66998cd1ad | ||
|
|
c0b1833446 | ||
|
|
329a72c77c | ||
|
|
2610ee9d0c | ||
|
|
a13ca9034e | ||
|
|
5d1abb3689 | ||
|
|
e1d1c6d154 | ||
|
|
e18e0e7cd4 | ||
|
|
eaf3d07a3f | ||
|
|
c88ae32b7f | ||
|
|
605613e220 | ||
|
|
d2772000ec | ||
|
|
42939a79f5 | ||
|
|
ed17931117 | ||
|
|
66df5f7a1c | ||
|
|
fc6e6696e5 | ||
|
|
465748c8a1 | ||
|
|
e59cd71bbf | ||
|
|
8a76fea310 | ||
|
|
0e46be54ec | ||
|
|
dc81813fdf | ||
|
|
eaa0df16bb | ||
|
|
c23e911028 | ||
|
|
06b96a1007 | ||
|
|
fa545c591f | ||
|
|
e828b780c7 | ||
|
|
eca8c5cabd | ||
|
|
b7bce6008f | ||
|
|
2fdf89883d | ||
|
|
6c5d4bbaaa | ||
|
|
cb2f926d4f | ||
|
|
12c01b437e | ||
|
|
3253a58942 | ||
|
|
199f7f14ea | ||
|
|
d42406d765 | ||
|
|
2276ffb1f6 | ||
|
|
218fb3afb0 | ||
|
|
a9fb890979 | ||
|
|
54ebf5b455 | ||
|
|
c9a0475aa8 | ||
|
|
5567d9f88c | ||
|
|
56f3e661ae | ||
|
|
1aa4479a10 | ||
|
|
7b625d0a91 | ||
|
|
fd0529529d | ||
|
|
af43191954 | ||
|
|
2ce2ca7c91 | ||
|
|
a0fc3db665 | ||
|
|
feb458027f | ||
|
|
e5a5b7af5c | ||
|
|
ad456ae2fe | ||
|
|
690cb51f6c | ||
|
|
14aaa2f376 | ||
|
|
6e47ca2c41 | ||
|
|
0d99d2be9b | ||
|
|
c322ef00e7 | ||
|
|
3513421225 | ||
|
|
b0e6bfbefe | ||
|
|
f7a918730e | ||
|
|
cef33319c5 | ||
|
|
2036a59210 | ||
|
|
e5eccb6227 | ||
|
|
48c2c8567c | ||
|
|
bbeef0299f | ||
|
|
bec5584d63 | ||
|
|
bdc759d34c | ||
|
|
8db442d8ba | ||
|
|
9e7a0d4175 | ||
|
|
9c33b3f5a9 | ||
|
|
7e7e2c87dc | ||
|
|
2f741f35a8 | ||
|
|
c411466df7 | ||
|
|
9679939307 | ||
|
|
8539423b22 | ||
|
|
81edafdf09 | ||
|
|
e0a262882a | ||
|
|
89237ab99e | ||
|
|
0f414e451e | ||
|
|
1180522725 | ||
|
|
81c7ebf123 | ||
|
|
258f05e6f4 | ||
|
|
53efb1c153 | ||
|
|
26014a9705 | ||
|
|
00ef037e45 | ||
|
|
669ec74e67 | ||
|
|
c4528200b0 | ||
|
|
ba7cd0250a | ||
|
|
c5e97678a1 | ||
|
|
337a46cdcc | ||
|
|
7f74b67f1f | ||
|
|
5dcc48d2e5 | ||
|
|
8b04aab07d | ||
|
|
eab4f6cf2e | ||
|
|
7f8d623283 | ||
|
|
dbffed8f1f | ||
|
|
7e3688fdd0 | ||
|
|
2e111e9ad3 | ||
|
|
6d6070ff3f | ||
|
|
391bbde353 | ||
|
|
3c56eb3762 | ||
|
|
7c14ea354b | ||
|
|
c96aad0b77 | ||
|
|
a9dd3e424b | ||
|
|
8a144a4046 | ||
|
|
75f86d7267 | ||
|
|
bbf875fc2f | ||
|
|
59d491f61b | ||
|
|
ed640a1324 | ||
|
|
e86fbcaef7 | ||
|
|
7f48212054 | ||
|
|
a2c5c71baf | ||
|
|
b904f81cb9 | ||
|
|
d64fe374dd | ||
|
|
fe25e7938e | ||
|
|
931df361bf | ||
|
|
d7c45f4aee | ||
|
|
5e5bef581b | ||
|
|
2d9e95d812 | ||
|
|
e5f979d106 | ||
|
|
c7a5815203 | ||
|
|
03e268722e | ||
|
|
78a2774329 | ||
|
|
c1b5ab7f53 | ||
|
|
b861d97ad4 | ||
|
|
f3abcc9dd6 | ||
|
|
cab13fe018 | ||
|
|
cc4b19c7ce | ||
|
|
a754d9aee5 | ||
|
|
22b54b2d8d | ||
|
|
d12ca6301a | ||
|
|
bc1b2ad9ab | ||
|
|
1782ab1514 | ||
|
|
0384fc50e3 | ||
|
|
cc46dee9ee | ||
|
|
ed5a0ae45a | ||
|
|
928ccfefb8 | ||
|
|
7f6bfb7b3e | ||
|
|
bcbc9bf675 | ||
|
|
0ec4366f4c | ||
|
|
ff72b7eea1 | ||
|
|
a32ca19251 | ||
|
|
b79508956a | ||
|
|
d76c5bd658 | ||
|
|
580e11126c | ||
|
|
736d40546a | ||
|
|
88810d2bb5 | ||
|
|
3a8f4d2ffb | ||
|
|
1fe125a65f | ||
|
|
0ff4df0836 | ||
|
|
16b4775e2d | ||
|
|
c3a13b8a29 | ||
|
|
d1053375b7 | ||
|
|
0fa4538256 | ||
|
|
738644f288 | ||
|
|
2f80b055ac | ||
|
|
fd62a1df10 | ||
|
|
a85d0ebd0a | ||
|
|
2c06902baa | ||
|
|
76ac6429fe | ||
|
|
43cae66b0d | ||
|
|
dacddecc7d | ||
|
|
dcb9267c2f | ||
|
|
ff35fd90fa | ||
|
|
7469377079 | ||
|
|
c8441f8d38 | ||
|
|
abf4eb0ffc | ||
|
|
93717cc830 | ||
|
|
b629bc81f8 | ||
|
|
f628897fe1 | ||
|
|
54b82a78e3 | ||
|
|
377faf145f | ||
|
|
69e316948f | ||
|
|
62cbff4f53 | ||
|
|
5582265e9d | ||
|
|
fb5ea3c324 | ||
|
|
9b5f676f50 | ||
|
|
88cfc0fa7e | ||
|
|
665bfa2f13 | ||
|
|
b89b1a64f4 | ||
|
|
9ba657c261 | ||
|
|
bce958b8e6 | ||
|
|
914012de2b | ||
|
|
8d1c476aed | ||
|
|
567c729e9e | ||
|
|
3f03dd20e4 | ||
|
|
1c778354da | ||
|
|
3a149fa459 | ||
|
|
f3b121950d | ||
|
|
43c13b7ba1 | ||
|
|
9447b33800 | ||
|
|
2934752eeb | ||
|
|
dd6d8c71fd | ||
|
|
80267c389b | ||
|
|
acfbaf75d5 | ||
|
|
5f54377407 | ||
|
|
552aa64741 | ||
|
|
d64f611f51 | ||
|
|
a96cc92d77 | ||
|
|
3858cccc41 | ||
|
|
072828512a | ||
|
|
a73ffe5642 | ||
|
|
8e784a5b6d | ||
|
|
1b6f9332f1 | ||
|
|
db8b472729 | ||
|
|
867b371522 | ||
|
|
c0d7c9fc7d | ||
|
|
bb4685cf90 | ||
|
|
6a95426749 | ||
|
|
ef6af8e84d | ||
|
|
763130f253 | ||
|
|
1256c040e9 | ||
|
|
18b7b48a99 | ||
|
|
627c11503f | ||
|
|
712ba84f06 | ||
|
|
5186e029b3 | ||
|
|
5bfaedf903 | ||
|
|
5061da6897 | ||
|
|
c159a28016 | ||
|
|
82a1b1c921 | ||
|
|
bf2210d0f4 | ||
|
|
8f0772cb94 | ||
|
|
5b57079ecd | ||
|
|
350d759517 | ||
|
|
edd793c9f5 | ||
|
|
545c2dc685 | ||
|
|
84955c066c | ||
|
|
06dd03b170 | ||
|
|
47bc2ed2dc | ||
|
|
44281afc54 | ||
|
|
4d2859d145 | ||
|
|
45d44a1669 | ||
|
|
ddd83b340e | ||
|
|
ccdb54d7c3 | ||
|
|
bcc246d950 | ||
|
|
62139e252a | ||
|
|
86950c3a0a | ||
|
|
f4865ef68d | ||
|
|
ea7209e7ae | ||
|
|
998c551cf3 | ||
|
|
e6f29b0116 | ||
|
|
eb90bb39dc | ||
|
|
ad189b35ad | ||
|
|
7d2989a233 | ||
|
|
862137ae7d | ||
|
|
c86e082d9a | ||
|
|
80fe048f97 | ||
|
|
f2bffb3ce7 | ||
|
|
cbe2f9eef8 | ||
|
|
688f41f570 | ||
|
|
a29197637e | ||
|
|
7a2712a37f | ||
|
|
189f5cfd8c | ||
|
|
e509480892 | ||
|
|
7f7955351a | ||
|
|
46f1db21a8 | ||
|
|
fbe7bc6951 | ||
|
|
f658507847 | ||
|
|
374078683b | ||
|
|
114c4e0886 | ||
|
|
67c62766d4 | ||
|
|
3f2947158d | ||
|
|
278a7cb356 | ||
|
|
890158a79c | ||
|
|
4dc1602b77 | ||
|
|
bbba0abac9 | ||
|
|
d04fd807c6 | ||
|
|
3456df4cf1 | ||
|
|
f56aaa791e | ||
|
|
465a758770 | ||
|
|
0f7c0c1b2c | ||
|
|
bf8d10b6f6 | ||
|
|
20d04553d6 | ||
|
|
b56d62e3c4 | ||
|
|
9a332dcba1 | ||
|
|
166d9f8823 | ||
|
|
42f5eed75f | ||
|
|
01a7db18dd | ||
|
|
d4507465a3 | ||
|
|
3ac92ed10a | ||
|
|
43c76ca85c | ||
|
|
54d87fa96a | ||
|
|
f041f17268 | ||
|
|
31c80a6967 | ||
|
|
783ce136f4 | ||
|
|
f829145781 | ||
|
|
389337f8cd | ||
|
|
a0713c2d66 | ||
|
|
f94d3cbce4 | ||
|
|
8d8994b468 | ||
|
|
784a9097a5 | ||
|
|
b9601626e3 | ||
|
|
dc80b011f2 | ||
|
|
ee7d32d460 | ||
|
|
43fd9ee94e | ||
|
|
8821a91f3f | ||
|
|
98d9256f92 | ||
|
|
b35495eaa7 | ||
|
|
74d6b614b3 | ||
|
|
dd63c16a74 | ||
|
|
4280266a96 | ||
|
|
b1f02098ff | ||
|
|
95189b574a | ||
|
|
c5d23503bf | ||
|
|
77950f6069 | ||
|
|
ec5f2b3753 | ||
|
|
9e7104fb7f | ||
|
|
6b3b6ca45e | ||
|
|
20b8b0b24e | ||
|
|
4e11540458 | ||
|
|
ee87f2676d | ||
|
|
74a90aab98 | ||
|
|
48ff9a5100 | ||
|
|
3dfd578ee5 | ||
|
|
0db46cdc81 | ||
|
|
fdac58d031 | ||
|
|
df9d4ce856 | ||
|
|
e6ae4e97e8 | ||
|
|
10a4c28922 | ||
|
|
8a828c6e51 | ||
|
|
d7b40905ff | ||
|
|
f9a3b5f3cd | ||
|
|
b73b89242f | ||
|
|
23a0f6e8de | ||
|
|
87967abc3f | ||
|
|
ce60c286dc | ||
|
|
90fd9b0eb8 | ||
|
|
ca262a6797 | ||
|
|
c056d39775 | ||
|
|
1c4426ea4b | ||
|
|
36520bd7a1 | ||
|
|
badf0ace76 | ||
|
|
f1f61249e0 | ||
|
|
b371cac18c | ||
|
|
1846535d8d | ||
|
|
d7d9118b9b |
2
.env
2
.env
@@ -123,7 +123,7 @@ SENTRY_ENVIRONMENT=local
|
||||
SENTRY_RELEASE=local
|
||||
|
||||
#### Prowler release version ####
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.5.0
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.6.0
|
||||
|
||||
# Social login credentials
|
||||
SOCIAL_GOOGLE_OAUTH_CALLBACK_URL="${AUTH_URL}/api/auth/callback/google"
|
||||
|
||||
93
.github/dependabot.yml
vendored
93
.github/dependabot.yml
vendored
@@ -9,8 +9,8 @@ updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 25
|
||||
target-branch: master
|
||||
labels:
|
||||
- "dependencies"
|
||||
@@ -31,8 +31,8 @@ updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 25
|
||||
target-branch: master
|
||||
labels:
|
||||
- "dependencies"
|
||||
@@ -53,46 +53,47 @@ updates:
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 25
|
||||
target-branch: master
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "docker"
|
||||
|
||||
# Dependabot Updates are temporary disabled - 2025/04/15
|
||||
# v4.6
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v4.6
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "pip"
|
||||
- "v4"
|
||||
# - 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"
|
||||
# - package-ecosystem: "github-actions"
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
# open-pull-requests-limit: 10
|
||||
# target-branch: v4.6
|
||||
# labels:
|
||||
# - "dependencies"
|
||||
# - "github_actions"
|
||||
# - "v4"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v4.6
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "docker"
|
||||
- "v4"
|
||||
# - package-ecosystem: "docker"
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
# open-pull-requests-limit: 10
|
||||
# target-branch: v4.6
|
||||
# labels:
|
||||
# - "dependencies"
|
||||
# - "docker"
|
||||
# - "v4"
|
||||
|
||||
# Dependabot Updates are temporary disabled - 2025/03/19
|
||||
# v3
|
||||
@@ -107,13 +108,13 @@ updates:
|
||||
# - "pip"
|
||||
# - "v3"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: v3
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github_actions"
|
||||
- "v3"
|
||||
# - package-ecosystem: "github-actions"
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
# interval: "monthly"
|
||||
# open-pull-requests-limit: 10
|
||||
# target-branch: v3
|
||||
# labels:
|
||||
# - "dependencies"
|
||||
# - "github_actions"
|
||||
# - "v3"
|
||||
|
||||
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -16,6 +16,7 @@ Please include a summary of the change and which issue is fixed. List any depend
|
||||
- [ ] Review if code is being documented following this specification https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
|
||||
- [ ] Review if backport is needed.
|
||||
- [ ] Review if is needed to change the [Readme.md](https://github.com/prowler-cloud/prowler/blob/master/README.md)
|
||||
- [ ] Ensure new entries are added to [CHANGELOG.md](https://github.com/prowler-cloud/prowler/blob/master/prowler/CHANGELOG.md), if applicable.
|
||||
|
||||
#### API
|
||||
- [ ] Verify if API specs need to be regenerated.
|
||||
|
||||
2
.github/workflows/api-pull-request.yml
vendored
2
.github/workflows/api-pull-request.yml
vendored
@@ -167,7 +167,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
|
||||
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
|
||||
34
.github/workflows/pull-request-merged.yml
vendored
Normal file
34
.github/workflows/pull-request-merged.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Prowler - Merged Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches: ['master']
|
||||
types: ['closed']
|
||||
|
||||
jobs:
|
||||
trigger-cloud-pull-request:
|
||||
name: Trigger Cloud Pull Request
|
||||
if: github.event.pull_request.merged == true && github.repository == 'prowler-cloud/prowler'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set short git commit SHA
|
||||
id: vars
|
||||
run: |
|
||||
shortSha=$(git rev-parse --short ${{ github.sha }})
|
||||
echo "SHORT_SHA=${shortSha}" >> $GITHUB_ENV
|
||||
|
||||
- name: Trigger pull request
|
||||
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0
|
||||
with:
|
||||
token: ${{ secrets.PROWLER_BOT_ACCESS_TOKEN }}
|
||||
repository: ${{ secrets.CLOUD_DISPATCH }}
|
||||
event-type: prowler-pull-request-merged
|
||||
client-payload: '{
|
||||
"PROWLER_COMMIT_SHA": "${{ github.sha }}",
|
||||
"PROWLER_COMMIT_SHORT_SHA": "${{ env.SHORT_SHA }}",
|
||||
"PROWLER_PR_TITLE": "${{ github.event.pull_request.title }}",
|
||||
"PROWLER_PR_LABELS": ${{ toJson(github.event.pull_request.labels.*.name) }},
|
||||
"PROWLER_PR_BODY": ${{ toJson(github.event.pull_request.body) }}
|
||||
}'
|
||||
2
.github/workflows/sdk-codeql.yml
vendored
2
.github/workflows/sdk-codeql.yml
vendored
@@ -21,6 +21,7 @@ on:
|
||||
paths-ignore:
|
||||
- 'ui/**'
|
||||
- 'api/**'
|
||||
- '.github/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- "master"
|
||||
@@ -30,6 +31,7 @@ on:
|
||||
paths-ignore:
|
||||
- 'ui/**'
|
||||
- 'api/**'
|
||||
- '.github/**'
|
||||
schedule:
|
||||
- cron: '00 12 * * *'
|
||||
|
||||
|
||||
3
.github/workflows/sdk-pull-request.yml
vendored
3
.github/workflows/sdk-pull-request.yml
vendored
@@ -34,6 +34,7 @@ jobs:
|
||||
permissions/**
|
||||
api/**
|
||||
ui/**
|
||||
prowler/CHANGELOG.md
|
||||
README.md
|
||||
mkdocs.yml
|
||||
.backportrc.json
|
||||
@@ -113,7 +114,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
|
||||
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
|
||||
4
.github/workflows/sdk-pypi-release.yml
vendored
4
.github/workflows/sdk-pypi-release.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
PYTHON_VERSION: 3.11
|
||||
CACHE: "poetry"
|
||||
# CACHE: "poetry"
|
||||
|
||||
jobs:
|
||||
repository-check:
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
cache: ${{ env.CACHE }}
|
||||
# cache: ${{ env.CACHE }}
|
||||
|
||||
- name: Build Prowler package
|
||||
run: |
|
||||
|
||||
@@ -4,7 +4,7 @@ name: SDK - Refresh AWS services' regions
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 9 * * *" #runs at 09:00 UTC everyday
|
||||
- cron: "0 9 * * 1" # runs at 09:00 UTC every Monday
|
||||
|
||||
env:
|
||||
GITHUB_BRANCH: "master"
|
||||
|
||||
2
.github/workflows/ui-pull-request.yml
vendored
2
.github/workflows/ui-pull-request.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM python:3.12.9-alpine3.20
|
||||
FROM python:3.12.10-alpine3.20
|
||||
|
||||
LABEL maintainer="https://github.com/prowler-cloud/prowler"
|
||||
|
||||
|
||||
@@ -72,10 +72,10 @@ 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 | 564 | 82 | 33 | 10 |
|
||||
| GCP | 78 | 13 | 7 | 3 |
|
||||
| Azure | 140 | 18 | 7 | 3 |
|
||||
| GCP | 79 | 13 | 7 | 3 |
|
||||
| Azure | 140 | 18 | 8 | 3 |
|
||||
| Kubernetes | 83 | 7 | 4 | 7 |
|
||||
| Microsoft365 | 5 | 2 | 1 | 0 |
|
||||
| M365 | 5 | 2 | 1 | 0 |
|
||||
| NHN (Unofficial) | 6 | 2 | 1 | 0 |
|
||||
|
||||
> You can list the checks, services, compliance frameworks and categories with `prowler <provider> --list-checks`, `prowler <provider> --list-services`, `prowler <provider> --list-compliance` and `prowler <provider> --list-categories`.
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
|
||||
## [v1.7.0] (UNRELEASED)
|
||||
|
||||
### Added
|
||||
|
||||
- Added M365 as a new provider [(#7563)](https://github.com/prowler-cloud/prowler/pull/7563).
|
||||
|
||||
---
|
||||
|
||||
## [v1.6.0] (Prowler v5.5.0)
|
||||
@@ -46,10 +53,6 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
- Handled exception when a provider has no secret in test connection [(#7283)](https://github.com/prowler-cloud/prowler/pull/7283).
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- Support for developing new integrations [(#7167)](https://github.com/prowler-cloud/prowler/pull/7167).
|
||||
|
||||
---
|
||||
|
||||
## [v1.5.0] (Prowler v5.4.0)
|
||||
|
||||
@@ -1,13 +1,34 @@
|
||||
FROM python:3.12.8-alpine3.20 AS build
|
||||
FROM python:3.12-slim-bookworm AS build
|
||||
|
||||
LABEL maintainer="https://github.com/prowler-cloud/api"
|
||||
|
||||
# hadolint ignore=DL3018
|
||||
RUN apk --no-cache add gcc python3-dev musl-dev linux-headers curl-dev
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc python3-dev bash \
|
||||
libcurl4-openssl-dev ca-certificates less ncurses-base \
|
||||
krb5-multidev libgcc-s1 gettext libssl3 \
|
||||
libstdc++6 tzdata liburcu8 zlib1g libicu72 curl openssh-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PowerShell
|
||||
RUN ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "x86_64" ]; then \
|
||||
curl -L https://github.com/PowerShell/PowerShell/releases/download/v7.5.0/powershell-7.5.0-linux-x64.tar.gz -o /tmp/powershell.tar.gz ; \
|
||||
elif [ "$ARCH" = "aarch64" ]; then \
|
||||
curl -L https://github.com/PowerShell/PowerShell/releases/download/v7.5.0/powershell-7.5.0-linux-arm64.tar.gz -o /tmp/powershell.tar.gz ; \
|
||||
else \
|
||||
echo "Unsupported architecture: $ARCH" && exit 1 ; \
|
||||
fi && \
|
||||
mkdir -p /opt/microsoft/powershell/7 && \
|
||||
tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/7 && \
|
||||
chmod +x /opt/microsoft/powershell/7/pwsh && \
|
||||
ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh && \
|
||||
rm /tmp/powershell.tar.gz
|
||||
|
||||
# Add prowler user
|
||||
RUN addgroup --gid 1000 prowler && \
|
||||
adduser --uid 1000 --gid 1000 --disabled-password --gecos "" prowler
|
||||
|
||||
RUN apk --no-cache upgrade && \
|
||||
addgroup -g 1000 prowler && \
|
||||
adduser -D -u 1000 -G prowler prowler
|
||||
USER prowler
|
||||
|
||||
WORKDIR /home/prowler
|
||||
@@ -17,7 +38,7 @@ COPY pyproject.toml ./
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir poetry
|
||||
|
||||
COPY src/backend/ ./backend/
|
||||
COPY src/backend/ ./backend/
|
||||
|
||||
ENV PATH="/home/prowler/.local/bin:$PATH"
|
||||
|
||||
@@ -30,12 +51,13 @@ COPY docker-entrypoint.sh ./docker-entrypoint.sh
|
||||
WORKDIR /home/prowler/backend
|
||||
|
||||
# Development image
|
||||
# hadolint ignore=DL3006
|
||||
FROM build AS dev
|
||||
|
||||
USER 0
|
||||
# hadolint ignore=DL3018
|
||||
RUN apk --no-cache add curl vim
|
||||
USER root
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl vim \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER prowler
|
||||
|
||||
|
||||
1803
api/poetry.lock
generated
1803
api/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ dependencies = [
|
||||
"drf-spectacular==0.27.2",
|
||||
"drf-spectacular-jsonapi==0.5.1",
|
||||
"gunicorn==23.0.0",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@v5.5",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@master",
|
||||
"psycopg2-binary==2.9.9",
|
||||
"pytest-celery[redis] (>=1.0.1,<2.0.0)",
|
||||
"sentry-sdk[django] (>=2.20.0,<3.0.0)",
|
||||
@@ -46,6 +46,7 @@ coverage = "7.5.4"
|
||||
django-silk = "5.3.2"
|
||||
docker = "7.1.0"
|
||||
freezegun = "1.5.1"
|
||||
marshmallow = ">=3.15.0,<4.0.0"
|
||||
mypy = "1.10.1"
|
||||
pylint = "3.2.5"
|
||||
pytest = "8.2.2"
|
||||
|
||||
28
api/src/backend/api/migrations/0017_m365_added.py
Normal file
28
api/src/backend/api/migrations/0017_m365_added.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.1.7 on 2025-04-16 08:47
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
import api.db_utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("api", "0016_finding_compliance_resource_details_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="provider",
|
||||
name="provider",
|
||||
field=api.db_utils.ProviderEnumField(
|
||||
choices=[
|
||||
("aws", "AWS"),
|
||||
("azure", "Azure"),
|
||||
("gcp", "GCP"),
|
||||
("kubernetes", "Kubernetes"),
|
||||
("m365", "M365"),
|
||||
],
|
||||
default="aws",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -191,6 +191,7 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
AZURE = "azure", _("Azure")
|
||||
GCP = "gcp", _("GCP")
|
||||
KUBERNETES = "kubernetes", _("Kubernetes")
|
||||
M365 = "m365", _("M365")
|
||||
|
||||
@staticmethod
|
||||
def validate_aws_uid(value):
|
||||
@@ -214,6 +215,15 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_m365_uid(value):
|
||||
if not re.match(r"^[a-zA-Z0-9-]+\.onmicrosoft\.com$", value):
|
||||
raise ModelValidationError(
|
||||
detail="M365 tenant ID must be a valid domain.",
|
||||
code="m365-uid",
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_gcp_uid(value):
|
||||
if not re.match(r"^[a-z][a-z0-9-]{5,29}$", value):
|
||||
|
||||
@@ -83,11 +83,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -99,6 +101,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -106,6 +109,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -450,11 +454,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -466,6 +472,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -473,6 +480,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -962,11 +970,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -978,6 +988,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -985,6 +996,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -1395,11 +1407,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -1411,6 +1425,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -1418,6 +1433,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2047,11 +2063,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2063,6 +2081,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -2070,6 +2089,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2204,11 +2224,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2220,6 +2242,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -2227,6 +2250,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2377,11 +2401,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2393,6 +2419,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -2400,6 +2427,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2863,11 +2891,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider__in]
|
||||
schema:
|
||||
@@ -3441,11 +3471,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -3457,6 +3489,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -3464,6 +3497,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -4167,11 +4201,13 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -4183,6 +4219,7 @@ paths:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -4190,6 +4227,7 @@ paths:
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -8347,6 +8385,33 @@ components:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- type: object
|
||||
title: M365 Static Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
client_secret:
|
||||
type: string
|
||||
description: The client secret associated with the application
|
||||
(client) ID, providing secure access.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory
|
||||
where the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
encrypted_password:
|
||||
type: string
|
||||
description: User encrypted password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- encrypted_password
|
||||
- type: object
|
||||
title: GCP Static Credentials
|
||||
properties:
|
||||
@@ -8814,12 +8879,14 @@ components:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
type: string
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
uid:
|
||||
type: string
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
@@ -8926,12 +8993,14 @@ components:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
type: string
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
uid:
|
||||
type: string
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
@@ -8969,12 +9038,14 @@ components:
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
- m365
|
||||
type: string
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
* `gcp` - GCP
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
uid:
|
||||
type: string
|
||||
minLength: 3
|
||||
@@ -9559,6 +9630,33 @@ components:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- type: object
|
||||
title: M365 Static Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
client_secret:
|
||||
type: string
|
||||
description: The client secret associated with the application
|
||||
(client) ID, providing secure access.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory where
|
||||
the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
encrypted_password:
|
||||
type: string
|
||||
description: User encrypted password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- encrypted_password
|
||||
- type: object
|
||||
title: GCP Static Credentials
|
||||
properties:
|
||||
@@ -9741,6 +9839,33 @@ components:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- type: object
|
||||
title: M365 Static Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
client_secret:
|
||||
type: string
|
||||
description: The client secret associated with the application
|
||||
(client) ID, providing secure access.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory
|
||||
where the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
encrypted_password:
|
||||
type: string
|
||||
description: User encrypted password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- encrypted_password
|
||||
- type: object
|
||||
title: GCP Static Credentials
|
||||
properties:
|
||||
@@ -9939,6 +10064,33 @@ components:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- type: object
|
||||
title: M365 Static Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
client_secret:
|
||||
type: string
|
||||
description: The client secret associated with the application
|
||||
(client) ID, providing secure access.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory where
|
||||
the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
encrypted_password:
|
||||
type: string
|
||||
description: User encrypted password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- encrypted_password
|
||||
- type: object
|
||||
title: GCP Static Credentials
|
||||
properties:
|
||||
|
||||
@@ -92,3 +92,31 @@ class TestResourceModel:
|
||||
|
||||
assert len(resource.tags.filter(tenant_id=tenant_id)) == 0
|
||||
assert resource.get_tags(tenant_id=tenant_id) == {}
|
||||
|
||||
|
||||
# @pytest.mark.django_db
|
||||
# class TestFindingModel:
|
||||
# def test_add_finding_with_long_uid(
|
||||
# self, providers_fixture, scans_fixture, resources_fixture
|
||||
# ):
|
||||
# provider, *_ = providers_fixture
|
||||
# tenant_id = provider.tenant_id
|
||||
|
||||
# long_uid = "1" * 500
|
||||
# _ = Finding.objects.create(
|
||||
# tenant_id=tenant_id,
|
||||
# uid=long_uid,
|
||||
# delta=Finding.DeltaChoices.NEW,
|
||||
# check_metadata={},
|
||||
# status=StatusChoices.PASS,
|
||||
# status_extended="",
|
||||
# severity="high",
|
||||
# impact="high",
|
||||
# raw_result={},
|
||||
# check_id="test_check",
|
||||
# scan=scans_fixture[0],
|
||||
# first_seen_at=None,
|
||||
# muted=False,
|
||||
# compliance={},
|
||||
# )
|
||||
# assert Finding.objects.filter(uid=long_uid).exists()
|
||||
|
||||
@@ -19,6 +19,7 @@ from prowler.providers.aws.aws_provider import AwsProvider
|
||||
from prowler.providers.azure.azure_provider import AzureProvider
|
||||
from prowler.providers.gcp.gcp_provider import GcpProvider
|
||||
from prowler.providers.kubernetes.kubernetes_provider import KubernetesProvider
|
||||
from prowler.providers.m365.m365_provider import M365Provider
|
||||
|
||||
|
||||
class TestMergeDicts:
|
||||
@@ -104,6 +105,7 @@ class TestReturnProwlerProvider:
|
||||
(Provider.ProviderChoices.GCP.value, GcpProvider),
|
||||
(Provider.ProviderChoices.AZURE.value, AzureProvider),
|
||||
(Provider.ProviderChoices.KUBERNETES.value, KubernetesProvider),
|
||||
(Provider.ProviderChoices.M365.value, M365Provider),
|
||||
],
|
||||
)
|
||||
def test_return_prowler_provider(self, provider_type, expected_provider):
|
||||
@@ -176,6 +178,10 @@ class TestGetProwlerProviderKwargs:
|
||||
Provider.ProviderChoices.KUBERNETES.value,
|
||||
{"context": "provider_uid"},
|
||||
),
|
||||
(
|
||||
Provider.ProviderChoices.M365.value,
|
||||
{},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_get_prowler_provider_kwargs(self, provider_type, expected_extra_kwargs):
|
||||
|
||||
@@ -11,6 +11,7 @@ from prowler.providers.azure.azure_provider import AzureProvider
|
||||
from prowler.providers.common.models import Connection
|
||||
from prowler.providers.gcp.gcp_provider import GcpProvider
|
||||
from prowler.providers.kubernetes.kubernetes_provider import KubernetesProvider
|
||||
from prowler.providers.m365.m365_provider import M365Provider
|
||||
|
||||
|
||||
class CustomOAuth2Client(OAuth2Client):
|
||||
@@ -51,14 +52,14 @@ def merge_dicts(default_dict: dict, replacement_dict: dict) -> dict:
|
||||
|
||||
def return_prowler_provider(
|
||||
provider: Provider,
|
||||
) -> [AwsProvider | AzureProvider | GcpProvider | KubernetesProvider]:
|
||||
) -> [AwsProvider | AzureProvider | GcpProvider | KubernetesProvider | M365Provider]:
|
||||
"""Return the Prowler provider class based on the given provider type.
|
||||
|
||||
Args:
|
||||
provider (Provider): The provider object containing the provider type and associated secrets.
|
||||
|
||||
Returns:
|
||||
AwsProvider | AzureProvider | GcpProvider | KubernetesProvider: The corresponding provider class.
|
||||
AwsProvider | AzureProvider | GcpProvider | KubernetesProvider | M365Provider: The corresponding provider class.
|
||||
|
||||
Raises:
|
||||
ValueError: If the provider type specified in `provider.provider` is not supported.
|
||||
@@ -72,6 +73,8 @@ def return_prowler_provider(
|
||||
prowler_provider = AzureProvider
|
||||
case Provider.ProviderChoices.KUBERNETES.value:
|
||||
prowler_provider = KubernetesProvider
|
||||
case Provider.ProviderChoices.M365.value:
|
||||
prowler_provider = M365Provider
|
||||
case _:
|
||||
raise ValueError(f"Provider type {provider.provider} not supported")
|
||||
return prowler_provider
|
||||
@@ -104,15 +107,15 @@ def get_prowler_provider_kwargs(provider: Provider) -> dict:
|
||||
|
||||
def initialize_prowler_provider(
|
||||
provider: Provider,
|
||||
) -> AwsProvider | AzureProvider | GcpProvider | KubernetesProvider:
|
||||
) -> AwsProvider | AzureProvider | GcpProvider | KubernetesProvider | M365Provider:
|
||||
"""Initialize a Prowler provider instance based on the given provider type.
|
||||
|
||||
Args:
|
||||
provider (Provider): The provider object containing the provider type and associated secrets.
|
||||
|
||||
Returns:
|
||||
AwsProvider | AzureProvider | GcpProvider | KubernetesProvider: An instance of the corresponding provider class
|
||||
(`AwsProvider`, `AzureProvider`, `GcpProvider`, or `KubernetesProvider`) initialized with the
|
||||
AwsProvider | AzureProvider | GcpProvider | KubernetesProvider | M365Provider: An instance of the corresponding provider class
|
||||
(`AwsProvider`, `AzureProvider`, `GcpProvider`, `KubernetesProvider` or `M365Provider`) initialized with the
|
||||
provider's secrets.
|
||||
"""
|
||||
prowler_provider = return_prowler_provider(provider)
|
||||
@@ -130,10 +133,12 @@ def prowler_provider_connection_test(provider: Provider) -> Connection:
|
||||
Connection: A connection object representing the result of the connection test for the specified provider.
|
||||
"""
|
||||
prowler_provider = return_prowler_provider(provider)
|
||||
|
||||
try:
|
||||
prowler_provider_kwargs = provider.secret.secret
|
||||
except Provider.secret.RelatedObjectDoesNotExist as secret_error:
|
||||
return Connection(is_connected=False, error=secret_error)
|
||||
|
||||
return prowler_provider.test_connection(
|
||||
**prowler_provider_kwargs, provider_id=provider.uid, raise_on_exception=False
|
||||
)
|
||||
|
||||
172
api/src/backend/api/v1/serializer_utils/providers.py
Normal file
172
api/src/backend/api/v1/serializer_utils/providers.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework_json_api import serializers
|
||||
|
||||
|
||||
@extend_schema_field(
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "AWS Static Credentials",
|
||||
"properties": {
|
||||
"aws_access_key_id": {
|
||||
"type": "string",
|
||||
"description": "The AWS access key ID. Required for environments where no IAM role is being "
|
||||
"assumed and direct AWS access is needed.",
|
||||
},
|
||||
"aws_secret_access_key": {
|
||||
"type": "string",
|
||||
"description": "The AWS secret access key. Must accompany 'aws_access_key_id' to authorize "
|
||||
"access to AWS resources.",
|
||||
},
|
||||
"aws_session_token": {
|
||||
"type": "string",
|
||||
"description": "The session token associated with temporary credentials. Only needed for "
|
||||
"session-based or temporary AWS access.",
|
||||
},
|
||||
},
|
||||
"required": ["aws_access_key_id", "aws_secret_access_key"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "AWS Assume Role",
|
||||
"properties": {
|
||||
"role_arn": {
|
||||
"type": "string",
|
||||
"description": "The Amazon Resource Name (ARN) of the role to assume. Required for AWS role "
|
||||
"assumption.",
|
||||
},
|
||||
"external_id": {
|
||||
"type": "string",
|
||||
"description": "An identifier to enhance security for role assumption.",
|
||||
},
|
||||
"aws_access_key_id": {
|
||||
"type": "string",
|
||||
"description": "The AWS access key ID. Only required if the environment lacks pre-configured "
|
||||
"AWS credentials.",
|
||||
},
|
||||
"aws_secret_access_key": {
|
||||
"type": "string",
|
||||
"description": "The AWS secret access key. Required if 'aws_access_key_id' is provided or if "
|
||||
"no AWS credentials are pre-configured.",
|
||||
},
|
||||
"aws_session_token": {
|
||||
"type": "string",
|
||||
"description": "The session token for temporary credentials, if applicable.",
|
||||
},
|
||||
"session_duration": {
|
||||
"type": "integer",
|
||||
"minimum": 900,
|
||||
"maximum": 43200,
|
||||
"default": 3600,
|
||||
"description": "The duration (in seconds) for the role session.",
|
||||
},
|
||||
"role_session_name": {
|
||||
"type": "string",
|
||||
"description": "An identifier for the role session, useful for tracking sessions in AWS logs. "
|
||||
"The regex used to validate this parameter is a string of characters consisting of "
|
||||
"upper- and lower-case alphanumeric characters with no spaces. You can also include "
|
||||
"underscores or any of the following characters: =,.@-\n\n"
|
||||
"Examples:\n"
|
||||
"- MySession123\n"
|
||||
"- User_Session-1\n"
|
||||
"- Test.Session@2",
|
||||
"pattern": "^[a-zA-Z0-9=,.@_-]+$",
|
||||
},
|
||||
},
|
||||
"required": ["role_arn", "external_id"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Azure Static Credentials",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure application (client) ID for authentication in Azure AD.",
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string",
|
||||
"description": "The client secret associated with the application (client) ID, providing "
|
||||
"secure access.",
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure tenant ID, representing the directory where the application is "
|
||||
"registered.",
|
||||
},
|
||||
},
|
||||
"required": ["client_id", "client_secret", "tenant_id"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "M365 Static Credentials",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure application (client) ID for authentication in Azure AD.",
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string",
|
||||
"description": "The client secret associated with the application (client) ID, providing "
|
||||
"secure access.",
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure tenant ID, representing the directory where the application is "
|
||||
"registered.",
|
||||
},
|
||||
"user": {
|
||||
"type": "email",
|
||||
"description": "User microsoft email address.",
|
||||
},
|
||||
"encrypted_password": {
|
||||
"type": "string",
|
||||
"description": "User encrypted password.",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"tenant_id",
|
||||
"user",
|
||||
"encrypted_password",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "GCP Static Credentials",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"description": "The client ID from Google Cloud, used to identify the application for GCP "
|
||||
"access.",
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string",
|
||||
"description": "The client secret associated with the GCP client ID, required for secure "
|
||||
"access.",
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"description": "A refresh token that allows the application to obtain new access tokens for "
|
||||
"extended use.",
|
||||
},
|
||||
},
|
||||
"required": ["client_id", "client_secret", "refresh_token"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Kubernetes Static Credentials",
|
||||
"properties": {
|
||||
"kubeconfig_content": {
|
||||
"type": "string",
|
||||
"description": "The content of the Kubernetes kubeconfig file, encoded as a string.",
|
||||
}
|
||||
},
|
||||
"required": ["kubeconfig_content"],
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
class ProviderSecretField(serializers.JSONField):
|
||||
pass
|
||||
@@ -42,6 +42,7 @@ from api.v1.serializer_utils.integrations import (
|
||||
IntegrationCredentialField,
|
||||
S3ConfigSerializer,
|
||||
)
|
||||
from api.v1.serializer_utils.providers import ProviderSecretField
|
||||
|
||||
# Tokens
|
||||
|
||||
@@ -1141,6 +1142,8 @@ class BaseWriteProviderSecretSerializer(BaseWriteSerializer):
|
||||
serializer = GCPProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.KUBERNETES.value:
|
||||
serializer = KubernetesProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.M365.value:
|
||||
serializer = M365ProviderSecret(data=secret)
|
||||
else:
|
||||
raise serializers.ValidationError(
|
||||
{"provider": f"Provider type not supported {provider_type}"}
|
||||
@@ -1180,6 +1183,17 @@ class AzureProviderSecret(serializers.Serializer):
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class M365ProviderSecret(serializers.Serializer):
|
||||
client_id = serializers.CharField()
|
||||
client_secret = serializers.CharField()
|
||||
tenant_id = serializers.CharField()
|
||||
user = serializers.EmailField()
|
||||
encrypted_password = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class GCPProviderSecret(serializers.Serializer):
|
||||
client_id = serializers.CharField()
|
||||
client_secret = serializers.CharField()
|
||||
@@ -1211,141 +1225,6 @@ class AWSRoleAssumptionProviderSecret(serializers.Serializer):
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
@extend_schema_field(
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "AWS Static Credentials",
|
||||
"properties": {
|
||||
"aws_access_key_id": {
|
||||
"type": "string",
|
||||
"description": "The AWS access key ID. Required for environments where no IAM role is being "
|
||||
"assumed and direct AWS access is needed.",
|
||||
},
|
||||
"aws_secret_access_key": {
|
||||
"type": "string",
|
||||
"description": "The AWS secret access key. Must accompany 'aws_access_key_id' to authorize "
|
||||
"access to AWS resources.",
|
||||
},
|
||||
"aws_session_token": {
|
||||
"type": "string",
|
||||
"description": "The session token associated with temporary credentials. Only needed for "
|
||||
"session-based or temporary AWS access.",
|
||||
},
|
||||
},
|
||||
"required": ["aws_access_key_id", "aws_secret_access_key"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "AWS Assume Role",
|
||||
"properties": {
|
||||
"role_arn": {
|
||||
"type": "string",
|
||||
"description": "The Amazon Resource Name (ARN) of the role to assume. Required for AWS role "
|
||||
"assumption.",
|
||||
},
|
||||
"external_id": {
|
||||
"type": "string",
|
||||
"description": "An identifier to enhance security for role assumption.",
|
||||
},
|
||||
"aws_access_key_id": {
|
||||
"type": "string",
|
||||
"description": "The AWS access key ID. Only required if the environment lacks pre-configured "
|
||||
"AWS credentials.",
|
||||
},
|
||||
"aws_secret_access_key": {
|
||||
"type": "string",
|
||||
"description": "The AWS secret access key. Required if 'aws_access_key_id' is provided or if "
|
||||
"no AWS credentials are pre-configured.",
|
||||
},
|
||||
"aws_session_token": {
|
||||
"type": "string",
|
||||
"description": "The session token for temporary credentials, if applicable.",
|
||||
},
|
||||
"session_duration": {
|
||||
"type": "integer",
|
||||
"minimum": 900,
|
||||
"maximum": 43200,
|
||||
"default": 3600,
|
||||
"description": "The duration (in seconds) for the role session.",
|
||||
},
|
||||
"role_session_name": {
|
||||
"type": "string",
|
||||
"description": "An identifier for the role session, useful for tracking sessions in AWS logs. "
|
||||
"The regex used to validate this parameter is a string of characters consisting of "
|
||||
"upper- and lower-case alphanumeric characters with no spaces. You can also include "
|
||||
"underscores or any of the following characters: =,.@-\n\n"
|
||||
"Examples:\n"
|
||||
"- MySession123\n"
|
||||
"- User_Session-1\n"
|
||||
"- Test.Session@2",
|
||||
"pattern": "^[a-zA-Z0-9=,.@_-]+$",
|
||||
},
|
||||
},
|
||||
"required": ["role_arn", "external_id"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Azure Static Credentials",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure application (client) ID for authentication in Azure AD.",
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string",
|
||||
"description": "The client secret associated with the application (client) ID, providing "
|
||||
"secure access.",
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure tenant ID, representing the directory where the application is "
|
||||
"registered.",
|
||||
},
|
||||
},
|
||||
"required": ["client_id", "client_secret", "tenant_id"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "GCP Static Credentials",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"description": "The client ID from Google Cloud, used to identify the application for GCP "
|
||||
"access.",
|
||||
},
|
||||
"client_secret": {
|
||||
"type": "string",
|
||||
"description": "The client secret associated with the GCP client ID, required for secure "
|
||||
"access.",
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"description": "A refresh token that allows the application to obtain new access tokens for "
|
||||
"extended use.",
|
||||
},
|
||||
},
|
||||
"required": ["client_id", "client_secret", "refresh_token"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Kubernetes Static Credentials",
|
||||
"properties": {
|
||||
"kubeconfig_content": {
|
||||
"type": "string",
|
||||
"description": "The content of the Kubernetes kubeconfig file, encoded as a string.",
|
||||
}
|
||||
},
|
||||
"required": ["kubeconfig_content"],
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
class ProviderSecretField(serializers.JSONField):
|
||||
pass
|
||||
|
||||
|
||||
class ProviderSecretSerializer(RLSSerializer):
|
||||
"""
|
||||
Serializer for the ProviderSecret model.
|
||||
|
||||
@@ -237,8 +237,9 @@ DJANGO_OUTPUT_S3_AWS_SECRET_ACCESS_KEY = env.str(
|
||||
DJANGO_OUTPUT_S3_AWS_SESSION_TOKEN = env.str("DJANGO_OUTPUT_S3_AWS_SESSION_TOKEN", "")
|
||||
DJANGO_OUTPUT_S3_AWS_DEFAULT_REGION = env.str("DJANGO_OUTPUT_S3_AWS_DEFAULT_REGION", "")
|
||||
|
||||
DJANGO_DELETION_BATCH_SIZE = env.int("DJANGO_DELETION_BATCH_SIZE", 5000)
|
||||
# HTTP Security Headers
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"
|
||||
|
||||
DJANGO_DELETION_BATCH_SIZE = env.int("DJANGO_DELETION_BATCH_SIZE", 5000)
|
||||
|
||||
@@ -399,7 +399,6 @@ mainConfig:
|
||||
[
|
||||
"RSA-1024",
|
||||
"P-192",
|
||||
"SHA-1",
|
||||
]
|
||||
|
||||
# AWS EKS Configuration
|
||||
|
||||
24
dashboard/compliance/soc2_azure.py
Normal file
24
dashboard/compliance/soc2_azure.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_format3
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_DESCRIPTION",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_format3(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID"
|
||||
)
|
||||
@@ -175,7 +175,7 @@ Due to the complexity and differences of each provider use the rest of the provi
|
||||
- [GCP](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/gcp/gcp_provider.py)
|
||||
- [Azure](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/azure/azure_provider.py)
|
||||
- [Kubernetes](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/kubernetes/kubernetes_provider.py)
|
||||
- [Microsoft365](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/microsoft365/microsoft365_provider.py)
|
||||
- [M365](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/m365/m365_provider.py)
|
||||
|
||||
To facilitate understanding here is a pseudocode of how the most basic provider could be with examples.
|
||||
|
||||
|
||||
@@ -237,4 +237,4 @@ It is really important to check if the current Prowler's permissions for each pr
|
||||
- AWS: https://docs.prowler.cloud/en/latest/getting-started/requirements/#aws-authentication
|
||||
- Azure: https://docs.prowler.cloud/en/latest/getting-started/requirements/#permissions
|
||||
- GCP: https://docs.prowler.cloud/en/latest/getting-started/requirements/#gcp-authentication
|
||||
- Microsoft365: https://docs.prowler.cloud/en/latest/getting-started/requirements/#microsoft365-authentication
|
||||
- M365: https://docs.prowler.cloud/en/latest/getting-started/requirements/#m365-authentication
|
||||
|
||||
@@ -40,8 +40,8 @@ If your IAM entity enforces MFA you can use `--mfa` and Prowler will ask you to
|
||||
|
||||
Prowler for Azure supports the following authentication types. To use each one you need to pass the proper flag to the execution:
|
||||
|
||||
- [Service principal application](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#service-principal-object) (recommended).
|
||||
- Current az cli credentials stored.
|
||||
- [Service Principal Application](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#service-principal-object) (recommended).
|
||||
- Current AZ CLI credentials stored.
|
||||
- Interactive browser authentication.
|
||||
- [Managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) authentication.
|
||||
|
||||
@@ -98,17 +98,34 @@ Prowler will follow the same credentials search as [Google authentication librar
|
||||
2. [User credentials set up by using the Google Cloud CLI](https://cloud.google.com/docs/authentication/application-default-credentials#personal)
|
||||
3. [The attached service account, returned by the metadata server](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa)
|
||||
|
||||
Those credentials must be associated to a user or service account with proper permissions to do all checks. To make sure, add the `Viewer` role to the member associated with the credentials.
|
||||
### Needed permissions
|
||||
|
||||
Prowler for Google Cloud needs the following permissions to be set:
|
||||
|
||||
- **Viewer (`roles/viewer`) IAM role**: granted at the project / folder / org level in order to scan the target projects
|
||||
|
||||
- **Project level settings**: you need to have at least one project with the below settings:
|
||||
- Identity and Access Management (IAM) API (`iam.googleapis.com`) enabled by either using the
|
||||
[Google Cloud API UI](https://console.cloud.google.com/apis/api/iam.googleapis.com/metrics) or
|
||||
by using the gcloud CLI `gcloud services enable iam.googleapis.com --project <your-project-id>` command
|
||||
- Service Usage Consumer (`roles/serviceusage.serviceUsageConsumer`) IAM role
|
||||
- Set the quota project to be this project by either running `gcloud auth application-default set-quota-project <project-id>` or by setting an environment variable:
|
||||
`export GOOGLE_CLOUD_QUOTA_PROJECT=<project-id>`
|
||||
|
||||
|
||||
The above settings must be associated to a user or service account.
|
||||
|
||||
|
||||
???+ note
|
||||
By default, `prowler` will scan all accessible GCP Projects, use flag `--project-ids` to specify the projects to be scanned.
|
||||
|
||||
## Microsoft365
|
||||
## Microsoft 365
|
||||
|
||||
Prowler for Microsoft365 currently supports the following authentication types:
|
||||
Prowler for M365 currently supports the following authentication types:
|
||||
|
||||
- [Service principal application](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#service-principal-object) (recommended).
|
||||
- Current az cli credentials stored.
|
||||
- [Service Principal Application](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#service-principal-object).
|
||||
- Service Principal Application and Microsoft User Credentials (**recommended**).
|
||||
- Current AZ CLI credentials stored.
|
||||
- Interactive browser authentication.
|
||||
|
||||
|
||||
@@ -128,6 +145,35 @@ export AZURE_TENANT_ID="XXXXXXXXX"
|
||||
If you try to execute Prowler with the `--sp-env-auth` flag and those variables are empty or not exported, the execution is going to fail.
|
||||
Follow the instructions in the [Create Prowler Service Principal](../tutorials/azure/create-prowler-service-principal.md) section to create a service principal.
|
||||
|
||||
### Service Principal and User Credentials authentication (recommended)
|
||||
This authentication method follows the same approach as the service principal method but introduces two additional environment variables for user credentials: `M365_USER` and `M365_ENCRYPTED_PASSWORD`.
|
||||
|
||||
```console
|
||||
export AZURE_CLIENT_ID="XXXXXXXXX"
|
||||
export AZURE_CLIENT_SECRET="XXXXXXXXX"
|
||||
export AZURE_TENANT_ID="XXXXXXXXX"
|
||||
export M365_USER="your_email@example.com"
|
||||
export M365_ENCRYPTED_PASSWORD="6500780061006d0070006c006500700061007300730077006f0072006400" # replace this to yours
|
||||
```
|
||||
|
||||
These two new environment variables are required to execute the PowerShell modules needed to retrieve information from M365 services. Prowler will use service principal authentication to log into MS Graph and user credentials to authenticate to Microsoft PowerShell modules.
|
||||
|
||||
The `M365_USER` should be your Microsoft account email, and `M365_ENCRYPTED_PASSWORD` must be an encrypted SecureString.
|
||||
To convert your password into a valid encrypted string, run the following commands in PowerShell:
|
||||
|
||||
```console
|
||||
$securePassword = ConvertTo-SecureString "examplepassword" -AsPlainText -Force
|
||||
$encryptedPassword = $securePassword | ConvertFrom-SecureString
|
||||
```
|
||||
|
||||
If everything is done correctly, you will see the encrypted string that you need to set as the `M365_ENCRYPTED_PASSWORD` environment variable.
|
||||
```console
|
||||
Write-Output $encryptedPassword
|
||||
6500780061006d0070006c006500700061007300730077006f0072006400
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Interactive Browser authentication
|
||||
|
||||
To use `--browser-auth` the user needs to authenticate against Azure using the default browser to start the scan, also `--tenant-id` flag is required.
|
||||
|
||||
@@ -170,7 +170,7 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
|
||||
* `Python >= 3.9, <= 3.12`
|
||||
* `Python pip >= 21.0.0`
|
||||
* AWS, GCP, Azure, Microsoft365 and/or Kubernetes credentials
|
||||
* AWS, GCP, Azure, M365 and/or Kubernetes credentials
|
||||
|
||||
_Commands_:
|
||||
|
||||
@@ -423,7 +423,7 @@ While the scan is running, start exploring the findings in these sections:
|
||||
|
||||
### Prowler CLI
|
||||
|
||||
To run Prowler, you will need to specify the provider (e.g `aws`, `gcp`, `azure`, `microsoft365` or `kubernetes`):
|
||||
To run Prowler, you will need to specify the provider (e.g `aws`, `gcp`, `azure`, `m365` or `kubernetes`):
|
||||
|
||||
???+ note
|
||||
If no provider specified, AWS will be used for backward compatibility with most of v2 options.
|
||||
@@ -565,23 +565,23 @@ kubectl logs prowler-XXXXX --namespace prowler-ns
|
||||
???+ note
|
||||
By default, `prowler` will scan all namespaces in your active Kubernetes context. Use the flag `--context` to specify the context to be scanned and `--namespaces` to specify the namespaces to be scanned.
|
||||
|
||||
#### Microsoft365
|
||||
#### Microsoft 365
|
||||
|
||||
With Microsoft365 you need to specify which auth method is going to be used:
|
||||
With M365 you need to specify which auth method is going to be used:
|
||||
|
||||
```console
|
||||
# To use service principal authentication
|
||||
prowler microsoft365 --sp-env-auth
|
||||
prowler m365 --sp-env-auth
|
||||
|
||||
# To use az cli authentication
|
||||
prowler microsoft365 --az-cli-auth
|
||||
prowler m365 --az-cli-auth
|
||||
|
||||
# To use browser authentication
|
||||
prowler microsoft365 --browser-auth --tenant-id "XXXXXXXX"
|
||||
prowler m365 --browser-auth --tenant-id "XXXXXXXX"
|
||||
|
||||
```
|
||||
|
||||
See more details about Microsoft365 Authentication in [Requirements](getting-started/requirements.md#microsoft365)
|
||||
See more details about M365 Authentication in [Requirements](getting-started/requirements/#microsoft-365)
|
||||
|
||||
## Prowler v2 Documentation
|
||||
For **Prowler v2 Documentation**, please check it out [here](https://github.com/prowler-cloud/prowler/blob/8818f47333a0c1c1a457453c87af0ea5b89a385f/README.md).
|
||||
|
||||
@@ -97,14 +97,15 @@ The following list includes all the Kubernetes checks with configurable variable
|
||||
| `kubelet_strong_ciphers_only` | `kubelet_strong_ciphers` | String |
|
||||
|
||||
|
||||
## Microsoft365
|
||||
## M365
|
||||
|
||||
### Configurable Checks
|
||||
The following list includes all the Microsoft365 checks with configurable variables that can be changed in the configuration yaml file:
|
||||
The following list includes all the Microsoft 365 checks with configurable variables that can be changed in the configuration yaml file:
|
||||
|
||||
| Check Name | Value | Type |
|
||||
|---------------------------------------------------------------|--------------------------------------------------|-----------------|
|
||||
| `entra_admin_users_sign_in_frequency_enabled` | `sign_in_frequency` | Integer |
|
||||
| `teams_external_file_sharing_restricted` | `allowed_cloud_storage_services` | List of Strings |
|
||||
|
||||
|
||||
## Config YAML File Structure
|
||||
@@ -504,10 +505,20 @@ kubernetes:
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
]
|
||||
|
||||
# Microsoft365 Configuration
|
||||
microsoft365:
|
||||
# Conditional Access Policy
|
||||
# policy.session_controls.sign_in_frequency.frequency in hours
|
||||
sign_in_frequency: 4
|
||||
# M365 Configuration
|
||||
m365:
|
||||
# Entra Conditional Access Policy
|
||||
# m365.entra_admin_users_sign_in_frequency_enabled
|
||||
sign_in_frequency: 4 # 4 hours
|
||||
# Teams Settings
|
||||
# m365.teams_external_file_sharing_restricted
|
||||
allowed_cloud_storage_services:
|
||||
[
|
||||
#"allow_box",
|
||||
#"allow_drop_box",
|
||||
#"allow_egnyte",
|
||||
#"allow_google_drive",
|
||||
#"allow_share_file",
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
@@ -4,8 +4,13 @@ Prowler will use by default your User Account credentials, you can configure it
|
||||
|
||||
- `gcloud init` to use a new account
|
||||
- `gcloud config set account <account>` to use an existing account
|
||||
- `gcloud auth application-default login`
|
||||
|
||||
Then, obtain your access credentials using: `gcloud auth application-default login`
|
||||
This will generate Application Default Credentials (ADC) that Prowler will use automatically.
|
||||
|
||||
---
|
||||
|
||||
## Using a Service Account key file
|
||||
|
||||
Otherwise, you can generate and download Service Account keys in JSON format (refer to https://cloud.google.com/iam/docs/creating-managing-service-account-keys) and provide the location of the file with the following argument:
|
||||
|
||||
@@ -16,14 +21,60 @@ prowler gcp --credentials-file path
|
||||
???+ note
|
||||
`prowler` will scan the GCP project associated with the credentials.
|
||||
|
||||
---
|
||||
|
||||
Prowler will follow the same credentials search as [Google authentication libraries](https://cloud.google.com/docs/authentication/application-default-credentials#search_order):
|
||||
## Using an access token
|
||||
|
||||
1. [GOOGLE_APPLICATION_CREDENTIALS environment variable](https://cloud.google.com/docs/authentication/application-default-credentials#GAC)
|
||||
2. [User credentials set up by using the Google Cloud CLI](https://cloud.google.com/docs/authentication/application-default-credentials#personal)
|
||||
3. [The attached service account, returned by the metadata server](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa)
|
||||
If you already have an access token (e.g., generated with `gcloud auth print-access-token`), you can run Prowler with:
|
||||
|
||||
Those credentials must be associated to a user or service account with proper permissions to do all checks. To make sure, add the `Viewer` role to the member associated with the credentials.
|
||||
```bash
|
||||
export CLOUDSDK_AUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
|
||||
prowler gcp --project-ids <project-id>
|
||||
```
|
||||
|
||||
???+ note
|
||||
If using this method, it's recommended to also set the default project explicitly:
|
||||
```bash
|
||||
export GOOGLE_CLOUD_PROJECT=<project-id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Credentials lookup order
|
||||
|
||||
Prowler follows the same search order as [Google authentication libraries](https://cloud.google.com/docs/authentication/application-default-credentials#search_order):
|
||||
|
||||
1. [`GOOGLE_APPLICATION_CREDENTIALS` environment variable](https://cloud.google.com/docs/authentication/application-default-credentials#GAC)
|
||||
2. [`CLOUDSDK_AUTH_ACCESS_TOKEN` + optional `GOOGLE_CLOUD_PROJECT`](https://cloud.google.com/sdk/gcloud/reference/auth/print-access-token)
|
||||
3. [User credentials set up by using the Google Cloud CLI](https://cloud.google.com/docs/authentication/application-default-credentials#personal)
|
||||
4. [Attached service account (e.g., Cloud Run, GCE, Cloud Functions)](https://cloud.google.com/docs/authentication/application-default-credentials#attached-sa)
|
||||
|
||||
???+ note
|
||||
The credentials must belong to a user or service account with the necessary permissions.
|
||||
To ensure full access, assign the roles/viewer IAM role to the identity being used.
|
||||
|
||||
???+ note
|
||||
Prowler will use the enabled Google Cloud APIs to get the information needed to perform the checks.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Needed permissions
|
||||
|
||||
Prowler for Google Cloud needs the following permissions to be set:
|
||||
|
||||
- **Viewer (`roles/viewer`) IAM role**: granted at the project / folder / org level in order to scan the target projects
|
||||
|
||||
- **Project level settings**: you need to have at least one project with the below settings:
|
||||
- Identity and Access Management (IAM) API (`iam.googleapis.com`) enabled by either using the
|
||||
[Google Cloud API UI](https://console.cloud.google.com/apis/api/iam.googleapis.com/metrics) or
|
||||
by using the gcloud CLI `gcloud services enable iam.googleapis.com --project <your-project-id>` command
|
||||
- Service Usage Consumer (`roles/serviceusage.serviceUsageConsumer`) IAM role
|
||||
- Set the quota project to be this project by either running `gcloud auth application-default set-quota-project <project-id>` or by setting an environment variable:
|
||||
`export GOOGLE_CLOUD_QUOTA_PROJECT=<project-id>`
|
||||
|
||||
|
||||
The above settings must be associated to a user or service account.
|
||||
|
||||
???+ note
|
||||
Prowler will use the enabled Google Cloud APIs to get the information needed to perform the checks.
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
# Microsoft365 authentication
|
||||
# Microsoft 365 authentication
|
||||
|
||||
By default Prowler uses MsGraph Python SDK identity package authentication methods using the class `ClientSecretCredential`.
|
||||
This allows Prowler to authenticate against microsoft365 using the following methods:
|
||||
This allows Prowler to authenticate against Microsoft 365 using the following methods:
|
||||
|
||||
- Service principal authentication by environment variables (Enterprise Application)
|
||||
- Service principal and Microsoft user credentials by environment variabled (using PowerShell requires this authentication method)
|
||||
- Current CLI credentials stored
|
||||
- Interactive browser authentication
|
||||
|
||||
|
||||
To launch the tool first you need to specify which method is used through the following flags:
|
||||
|
||||
```console
|
||||
# To use service principal (app) authentication and Microsoft user credentials (to use PowerShell)
|
||||
prowler m365 --env-auth
|
||||
|
||||
# To use service principal authentication
|
||||
prowler microsoft365 --sp-env-auth
|
||||
prowler m365 --sp-env-auth
|
||||
|
||||
# To use cli authentication
|
||||
prowler microsoft365 --az-cli-auth
|
||||
prowler m365 --az-cli-auth
|
||||
|
||||
# To use browser authentication
|
||||
prowler microsoft365 --browser-auth --tenant-id "XXXXXXXX"
|
||||
prowler m365 --browser-auth --tenant-id "XXXXXXXX"
|
||||
```
|
||||
|
||||
To use Prowler you need to set up also the permissions required to access your resources in your Microsoft365 account, to more details refer to [Requirements](../../getting-started/requirements.md)
|
||||
To use Prowler you need to set up also the permissions required to access your resources in your Microsoft 365 account, to more details refer to [Requirements](../../getting-started/requirements.md#microsoft-365)
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"# from prowler.providers.gcp.gcp_provider import GcpProvider\n",
|
||||
"# from prowler.providers.azure.azure_provider import AzureProvider\n",
|
||||
"# from prowler.providers.kubernetes.kubernetes_provider import KubernetesProvider\n",
|
||||
"# from prowler.providers.microsoft365.microsoft365_provider import Microsoft365Provider"
|
||||
"# from prowler.providers.m365.m365_provider import M365Provider"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
1902
poetry.lock
generated
1902
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
36
prowler/CHANGELOG.md
Normal file
36
prowler/CHANGELOG.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Prowler SDK Changelog
|
||||
|
||||
All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
## [v5.6.0] (Prowler UNRELEASED)
|
||||
|
||||
### Added
|
||||
|
||||
- Add SOC2 compliance framework to Azure [(#7489)](https://github.com/prowler-cloud/prowler/pull/7489).
|
||||
- Add check for unused Service Accounts in GCP [(#7419)](https://github.com/prowler-cloud/prowler/pull/7419).
|
||||
- Add Powershell to Microsoft365 [(#7331)](https://github.com/prowler-cloud/prowler/pull/7331).
|
||||
- Add service Defender to Microsoft365 with one check for Common Attachments filter enabled in Malware Policies [(#7425)](https://github.com/prowler-cloud/prowler/pull/7425).
|
||||
- Add check for Antiphishing Policy well configured in service Defender in M365 [(#7453)](https://github.com/prowler-cloud/prowler/pull/7453).
|
||||
- Add check for Notifications for Internal users enabled in Malware Policies from service Defender in M365 [(#7435)](https://github.com/prowler-cloud/prowler/pull/7435).
|
||||
- Support CLOUDSDK_AUTH_ACCESS_TOKEN in GCP [(#7495)](https://github.com/prowler-cloud/prowler/pull/7495).
|
||||
- Add service Exchange to Microsoft365 with one check for Organizations Mailbox Auditing enabled [(#7408)](https://github.com/prowler-cloud/prowler/pull/7408)
|
||||
- Add check for Bypass Disable in every Mailbox for service Defender in M365 [(#7418)](https://github.com/prowler-cloud/prowler/pull/7418)
|
||||
- Add new check `teams_email_sending_to_channel_disabled` [(#7533)](https://github.com/prowler-cloud/prowler/pull/7533)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix package name location in pyproject.toml while replicating for prowler-cloud [(#7531)](https://github.com/prowler-cloud/prowler/pull/7531).
|
||||
- Remove cache in PyPI release action [(#7532)](https://github.com/prowler-cloud/prowler/pull/7532).
|
||||
- Add the correct values for logger.info inside iam service [(#7526)](https://github.com/prowler-cloud/prowler/pull/7526).
|
||||
|
||||
---
|
||||
|
||||
## [v5.5.1] (Prowler v5.5.1)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add default name to contacts in Azure Defender [(#7483)](https://github.com/prowler-cloud/prowler/pull/7483).
|
||||
- Handle projects without ID in GCP [(#7496)](https://github.com/prowler-cloud/prowler/pull/7496).
|
||||
- Restore packages location in PyProject [(#7510)](https://github.com/prowler-cloud/prowler/pull/7510).
|
||||
|
||||
---
|
||||
@@ -51,7 +51,7 @@ from prowler.lib.outputs.compliance.cis.cis_aws import AWSCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_azure import AzureCIS
|
||||
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.cis.cis_microsoft365 import Microsoft365CIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_m365 import M365CIS
|
||||
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
|
||||
@@ -85,7 +85,7 @@ 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
|
||||
from prowler.providers.microsoft365.models import Microsoft365OutputOptions
|
||||
from prowler.providers.m365.models import M365OutputOptions
|
||||
from prowler.providers.nhn.models import NHNOutputOptions
|
||||
|
||||
|
||||
@@ -268,8 +268,8 @@ def prowler():
|
||||
output_options = KubernetesOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "microsoft365":
|
||||
output_options = Microsoft365OutputOptions(
|
||||
elif provider == "m365":
|
||||
output_options = M365OutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "nhn":
|
||||
@@ -666,7 +666,7 @@ def prowler():
|
||||
generated_outputs["compliance"].append(generic_compliance)
|
||||
generic_compliance.batch_write_data_to_file()
|
||||
|
||||
elif provider == "microsoft365":
|
||||
elif provider == "m365":
|
||||
for compliance_name in input_compliance_frameworks:
|
||||
if compliance_name.startswith("cis_"):
|
||||
# Generate CIS Finding Object
|
||||
@@ -674,7 +674,7 @@ def prowler():
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
cis = Microsoft365CIS(
|
||||
cis = M365CIS(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
file_path=filename,
|
||||
|
||||
623
prowler/compliance/azure/soc2_azure.json
Normal file
623
prowler/compliance/azure/soc2_azure.json
Normal file
@@ -0,0 +1,623 @@
|
||||
{
|
||||
"Framework": "SOC2",
|
||||
"Version": "",
|
||||
"Provider": "Azure",
|
||||
"Description": "System and Organization Controls (SOC), defined by the American Institute of Certified Public Accountants (AICPA), is the name of a set of reports that's produced during an audit. It's intended for use by service organizations (organizations that provide information systems as a service to other organizations) to issue validated reports of internal controls over those information systems to the users of those services. The reports focus on controls grouped into five categories known as Trust Service Principles.",
|
||||
"Requirements": [
|
||||
{
|
||||
"Id": "cc_1_3",
|
||||
"Name": "CC1.3 COSO Principle 3: Management establishes, with board oversight, structures, reporting lines, and appropriate authorities and responsibilities in the pursuit of objectives",
|
||||
"Description": "Considers All Structures of the Entity - Management and the board of directors consider the multiple structures used (including operating units, legal entities, geographic distribution, and outsourced service providers) to support the achievement of objectives. Establishes Reporting Lines - Management designs and evaluates lines of reporting for each entity structure to enable execution of authorities and responsibilities and flow of information to manage the activities of the entity. Defines, Assigns, and Limits Authorities and Responsibilities - Management and the board of directors delegate authority, define responsibilities, and use appropriate processes and technology to assign responsibility and segregate duties as necessary at the various levels of the organization. Additional points of focus specifically related to all engagements using the trust services criteria: Addresses Specific Requirements When Defining Authorities and Responsibilities—Management and the board of directors consider requirements relevant to security, availability, processing integrity, confidentiality, and privacy when defining authorities and responsibilities. Considers Interactions With External Parties When Establishing Structures, Reporting Lines, Authorities, and Responsibilities — Management and the board of directors consider the need for the entity to interact with and monitor the activities of external parties when establishing structures, reporting lines, authorities, and responsibilities.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_1_3",
|
||||
"Section": "CC1.0 - Common Criteria Related to Control Environment",
|
||||
"Service": "entra",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"entra_conditional_access_policy_require_mfa_for_management_api",
|
||||
"entra_global_admin_in_less_than_five_users",
|
||||
"entra_non_privileged_user_has_mfa",
|
||||
"entra_policy_default_users_cannot_create_security_groups",
|
||||
"entra_policy_ensure_default_user_cannot_create_apps",
|
||||
"entra_policy_ensure_default_user_cannot_create_tenants",
|
||||
"entra_policy_guest_invite_only_for_admin_roles",
|
||||
"entra_policy_guest_users_access_restrictions",
|
||||
"entra_policy_restricts_user_consent_for_apps",
|
||||
"entra_policy_user_consent_for_verified_apps",
|
||||
"entra_privileged_user_has_mfa",
|
||||
"entra_security_defaults_enabled",
|
||||
"entra_trusted_named_locations_exists",
|
||||
"entra_user_with_vm_access_has_mfa",
|
||||
"entra_users_cannot_create_microsoft_365_groups"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_2_1",
|
||||
"Name": "CC2.1 COSO Principle 13: The entity obtains or generates and uses relevant, quality information to support the functioning of internal control",
|
||||
"Description": "Identifies Information Requirements - A process is in place to identify the information required and expected to support the functioning of the other components of internal control and the achievement of the entity’s objectives. Captures Internal and External Sources of Data - Information systems capture internal and external sources of data. Processes Relevant Data Into Information - Information systems process and transform relevant data into information. Maintains Quality Throughout Processing - Information systems produce information that is timely, current, accurate, complete, accessible, protected, verifiable, and retained. Information is reviewed to assess its relevance in supporting the internal control components.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_2_1",
|
||||
"Section": "CC2.0 - Common Criteria Related to Communication and Information",
|
||||
"Service": "monitor",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
"monitor_alert_create_update_security_solution",
|
||||
"monitor_alert_create_update_sqlserver_fr",
|
||||
"monitor_alert_delete_nsg",
|
||||
"monitor_alert_delete_policy_assignment",
|
||||
"monitor_alert_delete_public_ip_address_rule",
|
||||
"monitor_alert_delete_security_solution",
|
||||
"monitor_alert_delete_sqlserver_fr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_3_1",
|
||||
"Name": "CC3.1 COSO Principle 6: The entity specifies objectives with sufficient clarity to enable the identification and assessment of risks relating to objectives",
|
||||
"Description": "Operations Objectives: Reflects Management's Choices - Operations objectives reflect management's choices about structure, industry considerations, and performance of the entity. Considers Tolerances for Risk - Management considers the acceptable levels of variation relative to the achievement of operations objectives. External Financial Reporting Objectives: Complies With Applicable Accounting Standards - Financial reporting objectives are consistent with accounting principles suitable and available for that entity. The accounting principles selected are appropriate in the circumstances. External Nonfinancial Reporting Objectives: Complies With Externally Established Frameworks - Management establishes objectives consistent with laws and regulations or standards and frameworks of recognized external organizations. Reflects Entity Activities - External reporting reflects the underlying transactions and events within a range of acceptable limits. Considers the Required Level of Precision—Management reflects the required level of precision and accuracy suitable for user needs and based on criteria established by third parties in nonfinancial reporting. Internal Reporting Objectives: Reflects Management's Choices - Internal reporting provides management with accurate and complete information regarding management's choices and information needed in managing the entity. Considers the Required Level of Precision—Management reflects the required level of precision and accuracy suitable for user needs in nonfinancial reporting objectives and materiality within financial reporting objectives. Reflects Entity Activities—Internal reporting reflects the underlying transactions and events within a range of acceptable limits. Compliance Objectives: Reflects External Laws and Regulations - Laws and regulations establish minimum standards of conduct, which the entity integrates into compliance objectives. Considers Tolerances for Risk - Management considers the acceptable levels of variation relative to the achievement of operations objectives. Additional point of focus specifically related to all engagements using the trust services criteria: Establishes Sub-objectives to Support Objectives—Management identifies sub-objectives related to security, availability, processing integrity, confidentiality, and privacy to support the achievement of the entity’s objectives related to reporting, operations, and compliance.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_3_1",
|
||||
"Section": "CC3.0 - Common Criteria Related to Risk Assessment",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_ensure_defender_for_app_services_is_on",
|
||||
"defender_ensure_defender_for_arm_is_on",
|
||||
"defender_ensure_defender_for_azure_sql_databases_is_on",
|
||||
"defender_ensure_defender_for_containers_is_on",
|
||||
"defender_ensure_defender_for_cosmosdb_is_on",
|
||||
"defender_ensure_defender_for_databases_is_on",
|
||||
"defender_ensure_defender_for_dns_is_on",
|
||||
"defender_ensure_defender_for_keyvault_is_on",
|
||||
"defender_ensure_defender_for_os_relational_databases_is_on",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_sql_servers_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on",
|
||||
"defender_ensure_iot_hub_defender_is_on",
|
||||
"defender_ensure_mcas_is_enabled",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"defender_ensure_notify_emails_to_owners",
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"sqlserver_microsoft_defender_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_3_2",
|
||||
"Name": "CC3.2 COSO Principle 7: The entity identifies risks to the achievement of its objectives across the entity and analyzes risks as a basis for determining how the risks should be managed",
|
||||
"Description": "Includes Entity, Subsidiary, Division, Operating Unit, and Functional Levels - The entity identifies and assesses risk at the entity, subsidiary, division, operating unit, and functional levels relevant to the achievement of objectives. Analyzes Internal and External Factors - Risk identification considers both internal and external factors and their impact on the achievement of objectives. Involves Appropriate Levels of Management - The entity puts into place effective risk assessment mechanisms that involve appropriate levels of management. Estimates Significance of Risks Identified - Identified risks are analyzed through a process that includes estimating the potential significance of the risk. Determines How to Respond to Risks - Risk assessment includes considering how the risk should be managed and whether to accept, avoid, reduce, or share the risk. Additional points of focus specifically related to all engagements using the trust services criteria: Identifies and Assesses Criticality of Information Assets and Identifies Threats and Vulnerabilities - The entity's risk identification and assessment process includes (1) identifying information assets, including physical devices and systems, virtual devices, software, data and data flows, external information systems, and organizational roles; (2) assessing the criticality of those information assets; (3) identifying the threats to the assets from intentional (including malicious) and unintentional acts and environmental events; and (4) identifying the vulnerabilities of the identified assets.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_3_2",
|
||||
"Section": "CC3.0 - Common Criteria Related to Risk Assessment",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_additional_email_configured_with_a_security_contact",
|
||||
"defender_assessments_vm_endpoint_protection_installed",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"defender_auto_provisioning_vulnerabilty_assessments_machines_on",
|
||||
"defender_container_images_resolved_vulnerabilities",
|
||||
"defender_container_images_scan_enabled",
|
||||
"defender_ensure_defender_for_app_services_is_on",
|
||||
"defender_ensure_defender_for_arm_is_on",
|
||||
"defender_ensure_defender_for_azure_sql_databases_is_on",
|
||||
"defender_ensure_defender_for_containers_is_on",
|
||||
"defender_ensure_defender_for_cosmosdb_is_on",
|
||||
"defender_ensure_defender_for_databases_is_on",
|
||||
"defender_ensure_defender_for_dns_is_on",
|
||||
"defender_ensure_defender_for_keyvault_is_on",
|
||||
"defender_ensure_defender_for_os_relational_databases_is_on",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_sql_servers_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on",
|
||||
"defender_ensure_iot_hub_defender_is_on",
|
||||
"defender_ensure_mcas_is_enabled",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"defender_ensure_notify_emails_to_owners",
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"sqlserver_microsoft_defender_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_3_3",
|
||||
"Name": "CC3.3 COSO Principle 8: The entity considers the potential for fraud in assessing risks to the achievement of objectives",
|
||||
"Description": "Considers Various Types of Fraud - The assessment of fraud considers fraudulent reporting, possible loss of assets, and corruption resulting from the various ways that fraud and misconduct can occur. Assesses Incentives and Pressures - The assessment of fraud risks considers incentives and pressures. Assesses Opportunities - The assessment of fraud risk considers opportunities for unauthorized acquisition,use, or disposal of assets, altering the entity’s reporting records, or committing other inappropriate acts. Assesses Attitudes and Rationalizations - The assessment of fraud risk considers how management and other personnel might engage in or justify inappropriate actions. Additional point of focus specifically related to all engagements using the trust services criteria: Considers the Risks Related to the Use of IT and Access to Information - The assessment of fraud risks includes consideration of threats and vulnerabilities that arise specifically from the use of IT and access to information.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_3_3",
|
||||
"Section": "CC3.0 - Common Criteria Related to Risk Assessment",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"aks_clusters_created_with_private_nodes",
|
||||
"app_function_identity_without_admin_privileges",
|
||||
"containerregistry_uses_private_link",
|
||||
"cosmosdb_account_use_private_endpoints",
|
||||
"entra_non_privileged_user_has_mfa",
|
||||
"entra_privileged_user_has_mfa",
|
||||
"entra_user_with_vm_access_has_mfa",
|
||||
"keyvault_private_endpoints",
|
||||
"monitor_storage_account_with_activity_logs_is_private",
|
||||
"storage_ensure_private_endpoints_in_storage_accounts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_4_2",
|
||||
"Name": "CC4.2 COSO Principle 17: The entity evaluates and communicates internal control deficiencies in a timely manner to those parties responsible for taking corrective action, including senior management and the board of directors, as appropriate",
|
||||
"Description": "Assesses Results - Management and the board of directors, as appropriate, assess results of ongoing and separate evaluations. Communicates Deficiencies - Deficiencies are communicated to parties responsible for taking corrective action and to senior management and the board of directors, as appropriate. Monitors Corrective Action - Management tracks whether deficiencies are remedied on a timely basis.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_4_2",
|
||||
"Section": "CC4.0 - Monitoring Activities",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_additional_email_configured_with_a_security_contact",
|
||||
"defender_assessments_vm_endpoint_protection_installed",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"defender_auto_provisioning_vulnerabilty_assessments_machines_on",
|
||||
"defender_container_images_resolved_vulnerabilities",
|
||||
"defender_container_images_scan_enabled",
|
||||
"defender_ensure_defender_for_app_services_is_on",
|
||||
"defender_ensure_defender_for_arm_is_on",
|
||||
"defender_ensure_defender_for_azure_sql_databases_is_on",
|
||||
"defender_ensure_defender_for_containers_is_on",
|
||||
"defender_ensure_defender_for_cosmosdb_is_on",
|
||||
"defender_ensure_defender_for_databases_is_on",
|
||||
"defender_ensure_defender_for_dns_is_on",
|
||||
"defender_ensure_defender_for_keyvault_is_on",
|
||||
"defender_ensure_defender_for_os_relational_databases_is_on",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_sql_servers_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on",
|
||||
"defender_ensure_iot_hub_defender_is_on",
|
||||
"defender_ensure_mcas_is_enabled",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"defender_ensure_notify_emails_to_owners",
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"sqlserver_microsoft_defender_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_5_2",
|
||||
"Name": "CC5.2 COSO Principle 11: The entity also selects and develops general control activities over technology to support the achievement of objectives",
|
||||
"Description": "Determines Dependency Between the Use of Technology in Business Processes and Technology General Controls - Management understands and determines the dependency and linkage between business processes, automated control activities, and technology general controls. Establishes Relevant Technology Infrastructure Control Activities - Management selects and develops control activities over the technology infrastructure, which are designed and implemented to help ensure the completeness, accuracy, and availability of technology processing. Establishes Relevant Security Management Process Controls Activities - Management selects and develops control activities that are designed and implemented to restrict technology access rights to authorized users commensurate with their job responsibilities and to protect the entity’s assets from external threats. Establishes Relevant Technology Acquisition, Development, and Maintenance Process Control Activities - Management selects and develops control activities over the acquisition, development, and maintenance of technology and its infrastructure to achieve management’s objectives.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_5_2",
|
||||
"Section": "CC5.0 - Control Activities",
|
||||
"Service": "monitor",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
"monitor_alert_create_update_security_solution",
|
||||
"monitor_alert_create_update_sqlserver_fr",
|
||||
"monitor_alert_delete_nsg",
|
||||
"monitor_alert_delete_policy_assignment",
|
||||
"monitor_alert_delete_public_ip_address_rule",
|
||||
"monitor_alert_delete_security_solution",
|
||||
"monitor_alert_delete_sqlserver_fr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_6_1",
|
||||
"Name": "CC6.1 The entity implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events to meet the entity's objectives",
|
||||
"Description": "Identifies and Manages the Inventory of Information Assets - The entity identifies, inventories, classifies, and manages information assets. Restricts Logical Access - Logical access to information assets, including hardware, data (at-rest, during processing, or in transmission), software, administrative authorities, mobile devices, output, and offline system components is restricted through the use of access control software and rule sets. Identifies and Authenticates Users - Persons, infrastructure and software are identified and authenticated prior to accessing information assets, whether locally or remotely. Considers Network Segmentation - Network segmentation permits unrelated portions of the entity's information system to be isolated from each other. Manages Points of Access - Points of access by outside entities and the types of data that flow through the points of access are identified, inventoried, and managed. The types of individuals and systems using each point of access are identified, documented, and managed. Restricts Access to Information Assets - Combinations of data classification, separate data structures, port restrictions, access protocol restrictions, user identification, and digital certificates are used to establish access control rules for information assets. Manages Identification and Authentication - Identification and authentication requirements are established, documented, and managed for individuals and systems accessing entity information, infrastructure and software. Manages Credentials for Infrastructure and Software - New internal and external infrastructure and software are registered, authorized, and documented prior to being granted access credentials and implemented on the network or access point. Credentials are removed and access is disabled when access is no longer required or the infrastructure and software are no longer in use. Uses Encryption to Protect Data - The entity uses encryption to supplement other measures used to protect data-at-rest, when such protections are deemed appropriate based on assessed risk. Protects Encryption Keys - Processes are in place to protect encryption keys during generation, storage, use, and destruction.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_6_1",
|
||||
"Section": "CC6.0 - Logical and Physical Access",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"aks_clusters_public_access_disabled",
|
||||
"app_function_not_publicly_accessible",
|
||||
"containerregistry_not_publicly_accessible",
|
||||
"network_public_ip_shodan",
|
||||
"storage_blob_public_access_level_is_disabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_6_2",
|
||||
"Name": "CC6.2 Prior to issuing system credentials and granting system access, the entity registers and authorizes new internal and external users whose access is administered by the entity",
|
||||
"Description": "Prior to issuing system credentials and granting system access, the entity registers and authorizes new internal and external users whose access is administered by the entity. For those users whose access is administered by the entity, user system credentials are removed when user access is no longer authorized. Controls Access Credentials to Protected Assets - Information asset access credentials are created based on an authorization from the system's asset owner or authorized custodian. Removes Access to Protected Assets When Appropriate - Processes are in place to remove credential access when an individual no longer requires such access. Reviews Appropriateness of Access Credentials - The appropriateness of access credentials is reviewed on a periodic basis for unnecessary and inappropriate individuals with credentials.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_6_2",
|
||||
"Section": "CC6.0 - Logical and Physical Access",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"mysql_flexible_server_minimum_tls_version_12",
|
||||
"mysql_flexible_server_ssl_connection_enabled",
|
||||
"postgresql_flexible_server_enforce_ssl_enabled",
|
||||
"sqlserver_recommended_minimal_tls_version",
|
||||
"sqlserver_tde_encrypted_with_cmk",
|
||||
"sqlserver_tde_encryption_enabled",
|
||||
"sqlserver_unrestricted_inbound_access",
|
||||
"storage_secure_transfer_required_is_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_6_3",
|
||||
"Name": "CC6.3 The entity authorizes, modifies, or removes access to data, software, functions, and other protected information assets based on roles, responsibilities, or the system design and changes, giving consideration to the concepts of least privilege and segregation of duties, to meet the entity’s objectives",
|
||||
"Description": "Creates or Modifies Access to Protected Information Assets - Processes are in place to create or modify access to protected information assets based on authorization from the asset’s owner. Removes Access to Protected Information Assets - Processes are in place to remove access to protected information assets when an individual no longer requires access. Uses Role-Based Access Controls - Role-based access control is utilized to support segregation of incompatible functions.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_6_3",
|
||||
"Section": "CC6.0 - Logical and Physical Access",
|
||||
"Service": "entra",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"entra_non_privileged_user_has_mfa",
|
||||
"entra_privileged_user_has_mfa",
|
||||
"entra_user_with_vm_access_has_mfa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_6_6",
|
||||
"Name": "CC6.6 The entity implements logical access security measures to protect against threats from sources outside its system boundaries",
|
||||
"Description": "Restricts Access — The types of activities that can occur through a communication channel (for example, FTP site, router port) are restricted. Protects Identification and Authentication Credentials — Identification and authentication credentials are protected during transmission outside its system boundaries. Requires Additional Authentication or Credentials — Additional authentication information or credentials are required when accessing the system from outside its boundaries. Implements Boundary Protection Systems — Boundary protection systems (for example, firewalls, demilitarized zones, and intrusion detection systems) are implemented to protect external access points from attempts and unauthorized access and are monitored to detect such attempts.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_6_6",
|
||||
"Section": "CC6.0 - Logical and Physical Access",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"network_http_internet_access_restricted",
|
||||
"network_rdp_internet_access_restricted",
|
||||
"network_ssh_internet_access_restricted",
|
||||
"network_udp_internet_access_restricted",
|
||||
"mysql_flexible_server_ssl_connection_enabled",
|
||||
"postgresql_flexible_server_enforce_ssl_enabled",
|
||||
"app_minimum_tls_version_12",
|
||||
"mysql_flexible_server_minimum_tls_version_12",
|
||||
"sqlserver_recommended_minimal_tls_version",
|
||||
"storage_ensure_minimum_tls_version_12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_6_7",
|
||||
"Name": "CC6.7 The entity restricts the transmission, movement, and removal of information to authorized internal and external users and processes, and protects it during transmission, movement, or removal to meet the entity’s objectives",
|
||||
"Description": "Restricts the Ability to Perform Transmission - Data loss prevention processes and technologies are used to restrict ability to authorize and execute transmission, movement and removal of information. Uses Encryption Technologies or Secure Communication Channels to Protect Data - Encryption technologies or secured communication channels are used to protect transmission of data and other communications beyond connectivity access points. Protects Removal Media - Encryption technologies and physical asset protections are used for removable media (such as USB drives and back-up tapes), as appropriate. Protects Mobile Devices - Processes are in place to protect mobile devices (such as laptops, smart phones and tablets) that serve as information assets.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_6_7",
|
||||
"Section": "CC6.0 - Logical and Physical Access",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"app_minimum_tls_version_12",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"sqlserver_tde_encrypted_with_cmk",
|
||||
"sqlserver_tde_encryption_enabled",
|
||||
"storage_ensure_encryption_with_customer_managed_keys",
|
||||
"storage_infrastructure_encryption_is_enabled",
|
||||
"storage_secure_transfer_required_is_enabled",
|
||||
"vm_ensure_attached_disks_encrypted_with_cmk",
|
||||
"vm_ensure_unattached_disks_encrypted_with_cmk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_6_8",
|
||||
"Name": "CC6.8 The entity implements controls to prevent or detect and act upon the introduction of unauthorized or malicious software to meet the entity’s objectives",
|
||||
"Description": "Restricts Application and Software Installation - The ability to install applications and software is restricted to authorized individuals. Detects Unauthorized Changes to Software and Configuration Parameters - Processes are in place to detect changes to software and configuration parameters that may be indicative of unauthorized or malicious software. Uses a Defined Change Control Process - A management-defined change control process is used for the implementation of software. Uses Antivirus and Anti-Malware Software - Antivirus and anti-malware software is implemented and maintained to provide for the interception or detection and remediation of malware. Scans Information Assets from Outside the Entity for Malware and Other Unauthorized Software - Procedures are in place to scan information assets that have been transferred or returned to the entity’s custody for malware and other unauthorized software and to remove any items detected prior to its implementation on the network.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_6_8",
|
||||
"Section": "CC6.0 - Logical and Physical Access",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_additional_email_configured_with_a_security_contact",
|
||||
"defender_assessments_vm_endpoint_protection_installed",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"defender_auto_provisioning_vulnerabilty_assessments_machines_on",
|
||||
"defender_container_images_resolved_vulnerabilities",
|
||||
"defender_container_images_scan_enabled",
|
||||
"defender_ensure_defender_for_app_services_is_on",
|
||||
"defender_ensure_defender_for_arm_is_on",
|
||||
"defender_ensure_defender_for_azure_sql_databases_is_on",
|
||||
"defender_ensure_defender_for_containers_is_on",
|
||||
"defender_ensure_defender_for_cosmosdb_is_on",
|
||||
"defender_ensure_defender_for_databases_is_on",
|
||||
"defender_ensure_defender_for_dns_is_on",
|
||||
"defender_ensure_defender_for_keyvault_is_on",
|
||||
"defender_ensure_defender_for_os_relational_databases_is_on",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_sql_servers_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on",
|
||||
"defender_ensure_iot_hub_defender_is_on",
|
||||
"defender_ensure_mcas_is_enabled",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"defender_ensure_notify_emails_to_owners",
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"sqlserver_microsoft_defender_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_7_1",
|
||||
"Name": "CC7.1 To meet its objectives, the entity uses detection and monitoring procedures to identify (1) changes to configurations that result in the introduction of new vulnerabilities, and (2) susceptibilities to newly discovered vulnerabilities",
|
||||
"Description": "Uses Defined Configuration Standards - Management has defined configuration standards. Monitors Infrastructure and Software - The entity monitors infrastructure and software for noncompliance with the standards, which could threaten the achievement of the entity's objectives. Implements Change-Detection Mechanisms - The IT system includes a change-detection mechanism (for example, file integrity monitoring tools) to alert personnel to unauthorized modifications of critical system files, configuration files, or content files. Detects Unknown or Unauthorized Components - Procedures are in place to detect the introduction of unknown or unauthorized components. Conducts Vulnerability Scans - The entity conducts vulnerability scans designed to identify potential vulnerabilities or misconfigurations on a periodic basis and after any significant change in the environment and takes action to remediate identified deficiencies on a timely basis.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_7_1",
|
||||
"Section": "CC7.0 - System Operations",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_additional_email_configured_with_a_security_contact",
|
||||
"defender_assessments_vm_endpoint_protection_installed",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"defender_auto_provisioning_vulnerabilty_assessments_machines_on",
|
||||
"defender_container_images_resolved_vulnerabilities",
|
||||
"defender_container_images_scan_enabled",
|
||||
"defender_ensure_defender_for_app_services_is_on",
|
||||
"defender_ensure_defender_for_arm_is_on",
|
||||
"defender_ensure_defender_for_azure_sql_databases_is_on",
|
||||
"defender_ensure_defender_for_containers_is_on",
|
||||
"defender_ensure_defender_for_cosmosdb_is_on",
|
||||
"defender_ensure_defender_for_databases_is_on",
|
||||
"defender_ensure_defender_for_dns_is_on",
|
||||
"defender_ensure_defender_for_keyvault_is_on",
|
||||
"defender_ensure_defender_for_os_relational_databases_is_on",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_sql_servers_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on",
|
||||
"defender_ensure_iot_hub_defender_is_on",
|
||||
"defender_ensure_mcas_is_enabled",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"defender_ensure_notify_emails_to_owners",
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_ensure_wdatp_is_enabled",
|
||||
"sqlserver_microsoft_defender_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_7_2",
|
||||
"Name": "CC7.2 The entity monitors system components and the operation of those components for anomalies that are indicative of malicious acts, natural disasters, and errors affecting the entity's ability to meet its objectives; anomalies are analyzed to determine whether they represent security events",
|
||||
"Description": "Implements Detection Policies, Procedures, and Tools - Detection policies and procedures are defined and implemented, and detection tools are implemented on Infrastructure and software to identify anomalies in the operation or unusual activity on systems. Procedures may include (1) a defined governance process for security event detection and management that includes provision of resources; (2) use of intelligence sources to identify newly discovered threats and vulnerabilities; and (3) logging of unusual system activities. Designs Detection Measures - Detection measures are designed to identify anomalies that could result from actual or attempted (1) compromise of physical barriers; (2) unauthorized actions of authorized personnel; (3) use of compromised identification and authentication credentials; (4) unauthorized access from outside the system boundaries; (5) compromise of authorized external parties; and (6) implementation or connection of unauthorized hardware and software. Implements Filters to Analyze Anomalies - Management has implemented procedures to filter, summarize, and analyze anomalies to identify security events. Monitors Detection Tools for Effective Operation - Management has implemented processes to monitor the effectiveness of detection tools.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_7_2",
|
||||
"Section": "CC7.0 - System Operations",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"app_http_logs_enabled",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"keyvault_logging_enabled",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"monitor_storage_account_with_activity_logs_is_private",
|
||||
"mysql_flexible_server_audit_log_connection_activated",
|
||||
"mysql_flexible_server_audit_log_enabled",
|
||||
"network_flow_log_captured_sent",
|
||||
"network_flow_log_more_than_90_days",
|
||||
"postgresql_flexible_server_log_checkpoints_on",
|
||||
"postgresql_flexible_server_log_connections_on",
|
||||
"postgresql_flexible_server_log_disconnections_on",
|
||||
"postgresql_flexible_server_log_retention_days_greater_3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_7_3",
|
||||
"Name": "CC7.3 The entity evaluates security events to determine whether they could or have resulted in a failure of the entity to meet its objectives (security incidents) and, if so, takes actions to prevent or address such failures",
|
||||
"Description": "Responds to Security Incidents - Procedures are in place for responding to security incidents and evaluating the effectiveness of those policies and procedures on a periodic basis. Communicates and Reviews Detected Security Events - Detected security events are communicated to and reviewed by the individuals responsible for the management of the security program and actions are taken, if necessary. Develops and Implements Procedures to Analyze Security Incidents - Procedures are in place to analyze security incidents and determine system impact. Assesses the Impact on Personal Information - Detected security events are evaluated to determine whether they could or did result in the unauthorized disclosure or use of personal information and whether there has been a failure to comply with applicable laws or regulations. Determines Personal Information Used or Disclosed - When an unauthorized use or disclosure of personal information has occurred, the affected information is identified.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_7_3",
|
||||
"Section": "CC7.0 - System Operations",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"app_http_logs_enabled",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"keyvault_logging_enabled",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"monitor_storage_account_with_activity_logs_is_private",
|
||||
"mysql_flexible_server_audit_log_connection_activated",
|
||||
"mysql_flexible_server_audit_log_enabled",
|
||||
"network_flow_log_captured_sent",
|
||||
"network_flow_log_more_than_90_days",
|
||||
"postgresql_flexible_server_log_checkpoints_on",
|
||||
"postgresql_flexible_server_log_connections_on",
|
||||
"postgresql_flexible_server_log_disconnections_on",
|
||||
"postgresql_flexible_server_log_retention_days_greater_3",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
"monitor_alert_create_update_security_solution",
|
||||
"monitor_alert_create_update_sqlserver_fr",
|
||||
"monitor_alert_delete_nsg",
|
||||
"monitor_alert_delete_policy_assignment",
|
||||
"monitor_alert_delete_public_ip_address_rule",
|
||||
"monitor_alert_delete_security_solution",
|
||||
"monitor_alert_delete_sqlserver_fr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_7_4",
|
||||
"Name": "CC7.4 The entity responds to identified security incidents by executing a defined incident response program to understand, contain, remediate, and communicate security incidents, as appropriate",
|
||||
"Description": "Assigns Roles and Responsibilities - Roles and responsibilities for the design, implementation, maintenance, and execution of the incident response program are assigned, including the use of external resources when necessary. Contains Security Incidents - Procedures are in place to contain security incidents that actively threaten entity objectives. Mitigates Ongoing Security Incidents - Procedures are in place to mitigate the effects of ongoing security incidents. Ends Threats Posed by Security Incidents - Procedures are in place to end the threats posed by security incidents through closure of the vulnerability, removal of unauthorized access, and other remediation actions. Restores Operations - Procedures are in place to restore data and business operations to an interim state that permits the achievement of entity objectives. Develops and Implements Communication Protocols for Security Incidents - Protocols for communicating security incidents and actions taken to affected parties are developed and implemented to meet the entity's objectives. Obtains Understanding of Nature of Incident and Determines Containment Strategy - An understanding of the nature (for example, the method by which the incident occurred and the affected system resources) and severity of the security incident is obtained to determine the appropriate containment strategy, including (1) a determination of the appropriate response time frame, and (2) the determination and execution of the containment approach. Remediates Identified Vulnerabilities - Identified vulnerabilities are remediated through the development and execution of remediation activities. Communicates Remediation Activities - Remediation activities are documented and communicated in accordance with the incident response program. Evaluates the Effectiveness of Incident Response - The design of incident response activities is evaluated for effectiveness on a periodic basis. Periodically Evaluates Incidents - Periodically, management reviews incidents related to security, availability, processing integrity, confidentiality, and privacy and identifies the need for system changes based on incident patterns and root causes. Communicates Unauthorized Use and Disclosure - Events that resulted in unauthorized use or disclosure of personal information are communicated to the data subjects, legal and regulatory authorities, and others as required. Application of Sanctions - The conduct of individuals and organizations operating under the authority of the entity and involved in the unauthorized use or disclosure of personal information is evaluated and, if appropriate, sanctioned in accordance with entity policies and legal and regulatory requirements.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_7_4",
|
||||
"Section": "CC7.0 - System Operations",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
"monitor_alert_create_update_security_solution",
|
||||
"monitor_alert_create_update_sqlserver_fr",
|
||||
"monitor_alert_delete_nsg",
|
||||
"monitor_alert_delete_policy_assignment",
|
||||
"monitor_alert_delete_public_ip_address_rule",
|
||||
"monitor_alert_delete_security_solution",
|
||||
"monitor_alert_delete_sqlserver_fr",
|
||||
"storage_ensure_soft_delete_is_enabled",
|
||||
"vm_ensure_attached_disks_encrypted_with_cmk",
|
||||
"vm_ensure_unattached_disks_encrypted_with_cmk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_7_5",
|
||||
"Name": "CC7.5 The entity identifies, develops, and implements activities to recover from identified security incidents",
|
||||
"Description": "Restores the Affected Environment - The activities restore the affected environment to functional operation by rebuilding systems, updating software, installing patches, and changing configurations, as needed. Communicates Information About the Event - Communications about the nature of the incident, recovery actions taken, and activities required for the prevention of future security events are made to management and others as appropriate (internal and external). Determines Root Cause of the Event - The root cause of the event is determined. Implements Changes to Prevent and Detect Recurrences - Additional architecture or changes to preventive and detective controls, or both, are implemented to prevent and detect recurrences on a timely basis. Improves Response and Recovery Procedures - Lessons learned are analyzed, and the incident response plan and recovery procedures are improved. Implements Incident Recovery Plan Testing - Incident recovery plan testing is performed on a periodic basis. The testing includes (1) development of testing scenarios based on threat likelihood and magnitude; (2) consideration of relevant system components from across the entity that can impair availability; (3) scenarios that consider the potential for the lack of availability of key personnel; and (4) revision of continuity plans and systems based on test results.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_7_5",
|
||||
"Section": "CC7.0 - System Operations",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"vm_ensure_attached_disks_encrypted_with_cmk",
|
||||
"vm_ensure_unattached_disks_encrypted_with_cmk",
|
||||
"storage_ensure_encryption_with_customer_managed_keys",
|
||||
"storage_infrastructure_encryption_is_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_8_1",
|
||||
"Name": "CC8.1 The entity authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures to meet its objectives",
|
||||
"Description": "Manages Changes Throughout the System Lifecycle - A process for managing system changes throughout the lifecycle of the system and its components (infrastructure, data, software and procedures) is used to support system availability and processing integrity. Authorizes Changes - A process is in place to authorize system changes prior to development. Designs and Develops Changes - A process is in place to design and develop system changes. Documents Changes - A process is in place to document system changes to support ongoing maintenance of the system and to support system users in performing their responsibilities. Tracks System Changes - A process is in place to track system changes prior to implementation. Configures Software - A process is in place to select and implement the configuration parameters used to control the functionality of software. Tests System Changes - A process is in place to test system changes prior to implementation. Approves System Changes - A process is in place to approve system changes prior to implementation. Deploys System Changes - A process is in place to implement system changes. Identifies and Evaluates System Changes - Objectives affected by system changes are identified, and the ability of the modified system to meet the objectives is evaluated throughout the system development life cycle. Identifies Changes in Infrastructure, Data, Software, and Procedures Required to Remediate Incidents - Changes in infrastructure, data, software, and procedures required to remediate incidents to continue to meet objectives are identified, and the change process is initiated upon identification. Creates Baseline Configuration of IT Technology - A baseline configuration of IT and control systems is created and maintained. Provides for Changes Necessary in Emergency Situations - A process is in place for authorizing, designing, testing, approving and implementing changes necessary in emergency situations (that is, changes that need to be implemented in an urgent timeframe). Protects Confidential Information - The entity protects confidential information during system design, development, testing, implementation, and change processes to meet the entity’s objectives related to confidentiality. Protects Personal Information - The entity protects personal information during system design, development, testing, implementation, and change processes to meet the entity’s objectives related to privacy.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_8_1",
|
||||
"Section": "CC8.0 - Change Management",
|
||||
"Service": "monitor",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
"monitor_alert_create_update_security_solution",
|
||||
"monitor_alert_create_update_sqlserver_fr",
|
||||
"monitor_alert_delete_nsg",
|
||||
"monitor_alert_delete_policy_assignment",
|
||||
"monitor_alert_delete_public_ip_address_rule",
|
||||
"monitor_alert_delete_security_solution",
|
||||
"monitor_alert_delete_sqlserver_fr",
|
||||
"monitor_diagnostic_setting_with_appropriate_categories",
|
||||
"monitor_diagnostic_settings_exists",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_a_1_1",
|
||||
"Name": "A1.2 The entity authorizes, designs, develops or acquires, implements, operates, approves, maintains, and monitors environmental protections, software, data back-up processes, and recovery infrastructure to meet its objectives",
|
||||
"Description": "Measures Current Usage - The use of the system components is measured to establish a baseline for capacity management and to use when evaluating the risk of impaired availability due to capacity constraints. Forecasts Capacity - The expected average and peak use of system components is forecasted and compared to system capacity and associated tolerances. Forecasting considers capacity in the event of the failure of system components that constrain capacity. Makes Changes Based on Forecasts - The system change management process is initiated when forecasted usage exceeds capacity tolerances.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_a_1_1",
|
||||
"Section": "CCA1.0 - Additional Criterial for Availability",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"app_http_logs_enabled",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"keyvault_logging_enabled",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"monitor_storage_account_with_activity_logs_is_private",
|
||||
"mysql_flexible_server_audit_log_connection_activated",
|
||||
"mysql_flexible_server_audit_log_enabled",
|
||||
"network_flow_log_captured_sent",
|
||||
"network_flow_log_more_than_90_days",
|
||||
"postgresql_flexible_server_log_checkpoints_on",
|
||||
"postgresql_flexible_server_log_connections_on",
|
||||
"postgresql_flexible_server_log_disconnections_on",
|
||||
"postgresql_flexible_server_log_retention_days_greater_3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_c_1_1",
|
||||
"Name": "C1.1 The entity identifies and maintains confidential information to meet the entity’s objectives related to confidentiality",
|
||||
"Description": "Identifies Confidential information - Procedures are in place to identify and designate confidential information when it is received or created and to determine the period over which the confidential information is to be retained. Protects Confidential Information from Destruction - Procedures are in place to protect confidential information from erasure or destruction during the specified retention period of the information",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_c_1_1",
|
||||
"Section": "CCC1.0 - Additional Criterial for Confidentiality",
|
||||
"Service": "storage",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"storage_ensure_soft_delete_is_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "cc_c_1_2",
|
||||
"Name": "C1.2 The entity disposes of confidential information to meet the entity’s objectives related to confidentiality",
|
||||
"Description": "Identifies Confidential Information for Destruction - Procedures are in place to identify confidential information requiring destruction when the end of the retention period is reached. Destroys Confidential Information - Procedures are in place to erase or otherwise destroy confidential information that has been identified for destruction.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "cc_c_1_2",
|
||||
"Section": "CCC1.0 - Additional Criterial for Confidentiality",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"network_flow_log_more_than_90_days",
|
||||
"postgresql_flexible_server_log_retention_days_greater_3",
|
||||
"sqlserver_auditing_retention_90_days",
|
||||
"storage_ensure_soft_delete_is_enabled"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -145,6 +145,7 @@
|
||||
"iam_sa_no_administrative_privileges",
|
||||
"iam_sa_no_user_managed_keys",
|
||||
"iam_sa_user_managed_key_rotate_90_days",
|
||||
"iam_service_account_unused",
|
||||
"apikeys_key_rotated_in_90_days",
|
||||
"apikeys_api_restrictions_configured"
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"Framework": "CIS",
|
||||
"Version": "4.0",
|
||||
"Provider": "Microsoft365",
|
||||
"Provider": "M365",
|
||||
"Description": "The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for establishing a secure configuration posture for Microsoft 365 Cloud offerings running on any OS.",
|
||||
"Requirements": [
|
||||
{
|
||||
@@ -12,7 +12,7 @@ from prowler.lib.logger import logger
|
||||
|
||||
timestamp = datetime.today()
|
||||
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
|
||||
prowler_version = "5.5.0"
|
||||
prowler_version = "5.6.0"
|
||||
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"
|
||||
@@ -28,7 +28,7 @@ class Provider(str, Enum):
|
||||
GCP = "gcp"
|
||||
AZURE = "azure"
|
||||
KUBERNETES = "kubernetes"
|
||||
MICROSOFT365 = "microsoft365"
|
||||
M365 = "m365"
|
||||
NHN = "nhn"
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ def load_and_validate_config_file(provider: str, config_file_path: str) -> dict:
|
||||
# and a new format with a key for each provider to include their configuration values within.
|
||||
if any(
|
||||
key in config_file
|
||||
for key in ["aws", "gcp", "azure", "kubernetes", "microsoft365"]
|
||||
for key in ["aws", "gcp", "azure", "kubernetes", "m365"]
|
||||
):
|
||||
config = config_file.get(provider, {})
|
||||
else:
|
||||
|
||||
@@ -327,7 +327,6 @@ aws:
|
||||
[
|
||||
"RSA-1024",
|
||||
"P-192",
|
||||
"SHA-1",
|
||||
]
|
||||
|
||||
# AWS EKS Configuration
|
||||
@@ -447,6 +446,10 @@ gcp:
|
||||
# GCP Compute Configuration
|
||||
# gcp.compute_public_address_shodan
|
||||
shodan_api_key: null
|
||||
# GCP Service Account and user-managed keys unused configuration
|
||||
# gcp.iam_service_account_unused
|
||||
# gcp.iam_sa_user_managed_key_unused
|
||||
max_unused_account_days: 180
|
||||
|
||||
# Kubernetes Configuration
|
||||
kubernetes:
|
||||
@@ -479,8 +482,18 @@ kubernetes:
|
||||
]
|
||||
|
||||
|
||||
# Microsoft365 Configuration
|
||||
microsoft365:
|
||||
# Conditional Access Policy
|
||||
# policy.session_controls.sign_in_frequency.frequency in hours
|
||||
sign_in_frequency: 4
|
||||
# M365 Configuration
|
||||
m365:
|
||||
# Entra Conditional Access Policy
|
||||
# m365.entra_admin_users_sign_in_frequency_enabled
|
||||
sign_in_frequency: 4 # 4 hours
|
||||
# Teams Settings
|
||||
# m365.teams_external_file_sharing_restricted
|
||||
allowed_cloud_storage_services:
|
||||
[
|
||||
#"allow_box",
|
||||
#"allow_drop_box",
|
||||
#"allow_egnyte",
|
||||
#"allow_google_drive",
|
||||
#"allow_share_file",
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
### Account, Check and/or Region can be * to apply for all the cases.
|
||||
### Account == Microsoft365 Tenant and Region == Microsoft365 Location
|
||||
### Account == M365 Tenant and Region == M365 Location
|
||||
### Resources and tags are lists that can have either Regex or Keywords.
|
||||
### Tags is an optional list that matches on tuples of 'key=value' and are "ANDed" together.
|
||||
### Use an alternation Regex to match one of multiple tags with "ORed" logic.
|
||||
@@ -543,8 +543,8 @@ class Check_Report_Kubernetes(Check_Report):
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckReportMicrosoft365(Check_Report):
|
||||
"""Contains the Microsoft365 Check's finding information."""
|
||||
class CheckReportM365(Check_Report):
|
||||
"""Contains the M365 Check's finding information."""
|
||||
|
||||
resource_name: str
|
||||
resource_id: str
|
||||
@@ -558,7 +558,7 @@ class CheckReportMicrosoft365(Check_Report):
|
||||
resource_id: str,
|
||||
resource_location: str = "global",
|
||||
) -> None:
|
||||
"""Initialize the Microsoft365 Check's finding information.
|
||||
"""Initialize the M365 Check's finding information.
|
||||
|
||||
Args:
|
||||
metadata: The metadata of the check.
|
||||
|
||||
@@ -26,15 +26,15 @@ class ProwlerArgumentParser:
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog="prowler",
|
||||
formatter_class=RawTextHelpFormatter,
|
||||
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,microsoft365,nhn,dashboard} ...",
|
||||
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,nhn,dashboard} ...",
|
||||
epilog="""
|
||||
Available Cloud Providers:
|
||||
{aws,azure,gcp,kubernetes,microsoft365,nhn}
|
||||
{aws,azure,gcp,kubernetes,m365,nhn}
|
||||
aws AWS Provider
|
||||
azure Azure Provider
|
||||
gcp GCP Provider
|
||||
kubernetes Kubernetes Provider
|
||||
microsoft365 Microsoft 365 Provider
|
||||
m365 Microsoft 365 Provider
|
||||
nhn NHN Provider (Unofficial)
|
||||
|
||||
Available components:
|
||||
@@ -104,6 +104,11 @@ Detailed documentation at https://docs.prowler.com
|
||||
if "-" in sys.argv[1]:
|
||||
sys.argv = self.__set_default_provider__(sys.argv)
|
||||
|
||||
# Provider aliases mapping
|
||||
# Microsoft 365
|
||||
elif sys.argv[1] == "microsoft365":
|
||||
sys.argv[1] = "m365"
|
||||
|
||||
# Parse arguments
|
||||
args = self.parser.parse_args()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.cis.models import Microsoft365CISModel
|
||||
from prowler.lib.outputs.compliance.cis.models import M365CISModel
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class Microsoft365CIS(ComplianceOutput):
|
||||
class M365CIS(ComplianceOutput):
|
||||
"""
|
||||
This class represents the Azure CIS compliance output.
|
||||
|
||||
@@ -39,7 +39,7 @@ class Microsoft365CIS(ComplianceOutput):
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = Microsoft365CISModel(
|
||||
compliance_row = M365CISModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
TenantId=finding.account_uid,
|
||||
@@ -70,7 +70,7 @@ class Microsoft365CIS(ComplianceOutput):
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = Microsoft365CISModel(
|
||||
compliance_row = M365CISModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
TenantId=finding.account_uid,
|
||||
@@ -69,9 +69,9 @@ class AzureCISModel(BaseModel):
|
||||
Muted: bool
|
||||
|
||||
|
||||
class Microsoft365CISModel(BaseModel):
|
||||
class M365CISModel(BaseModel):
|
||||
"""
|
||||
Microsoft365CISModel generates a finding's output in Microsoft365 CIS Compliance format.
|
||||
M365CISModel generates a finding's output in Microsoft 365 CIS Compliance format.
|
||||
"""
|
||||
|
||||
Provider: str
|
||||
|
||||
@@ -245,7 +245,7 @@ class Finding(BaseModel):
|
||||
)
|
||||
output_data["region"] = f"namespace: {check_output.namespace}"
|
||||
|
||||
elif provider.type == "microsoft365":
|
||||
elif provider.type == "m365":
|
||||
output_data["auth_method"] = (
|
||||
f"{provider.identity.identity_type}: {provider.identity.identity_id}"
|
||||
)
|
||||
@@ -284,7 +284,7 @@ class Finding(BaseModel):
|
||||
|
||||
if not output_data["resource_uid"]:
|
||||
logger.error(
|
||||
f"Check {check_output.check_metadata.CheckID} has no resource_id."
|
||||
f"Check {check_output.check_metadata.CheckID} has no resource_uid."
|
||||
)
|
||||
if not output_data["resource_name"]:
|
||||
logger.error(
|
||||
|
||||
@@ -546,9 +546,9 @@ class HTML(Output):
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def get_microsoft365_assessment_summary(provider: Provider) -> str:
|
||||
def get_m365_assessment_summary(provider: Provider) -> str:
|
||||
"""
|
||||
get_microsoft365_assessment_summary gets the HTML assessment summary for the provider
|
||||
get_m365_assessment_summary gets the HTML assessment summary for the provider
|
||||
|
||||
Args:
|
||||
provider (Provider): the provider object
|
||||
@@ -561,11 +561,11 @@ class HTML(Output):
|
||||
<div class="col-md-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Microsoft365 Assessment Summary
|
||||
M365 Assessment Summary
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>Microsoft365 Tenant Domain:</b> {provider.identity.tenant_domain}
|
||||
<b>M365 Tenant Domain:</b> {provider.identity.tenant_domain}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -573,14 +573,14 @@ class HTML(Output):
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Microsoft365 Credentials
|
||||
M365 Credentials
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>Microsoft365 Identity Type:</b> {provider.identity.identity_type}
|
||||
<b>M365 Identity Type:</b> {provider.identity.identity_type}
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>Microsoft365 Identity ID:</b> {provider.identity.identity_id}
|
||||
<b>M365 Identity ID:</b> {provider.identity.identity_id}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ def stdout_report(finding, color, verbose, status, fix):
|
||||
details = finding.location.lower()
|
||||
if finding.check_metadata.Provider == "kubernetes":
|
||||
details = finding.namespace.lower()
|
||||
if finding.check_metadata.Provider == "microsoft365":
|
||||
if finding.check_metadata.Provider == "m365":
|
||||
details = finding.location
|
||||
if finding.check_metadata.Provider == "nhn":
|
||||
details = finding.location
|
||||
|
||||
@@ -40,7 +40,7 @@ def display_summary_table(
|
||||
elif provider.type == "kubernetes":
|
||||
entity_type = "Context"
|
||||
audited_entities = provider.identity.context
|
||||
elif provider.type == "microsoft365":
|
||||
elif provider.type == "m365":
|
||||
entity_type = "Tenant Domain"
|
||||
audited_entities = provider.identity.tenant_domain
|
||||
elif provider.type == "nhn":
|
||||
|
||||
240
prowler/lib/powershell/powershell.py
Normal file
240
prowler/lib/powershell/powershell.py
Normal file
@@ -0,0 +1,240 @@
|
||||
import json
|
||||
import platform
|
||||
import queue
|
||||
import re
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
class PowerShellSession:
|
||||
"""
|
||||
Base class for managing PowerShell sessions.
|
||||
|
||||
This class provides the core functionality for interacting with PowerShell,
|
||||
including command execution, output handling, and session management.
|
||||
It serves as a foundation for more specific PowerShell implementations.
|
||||
|
||||
Features:
|
||||
- Maintains a persistent PowerShell session
|
||||
- Handles command execution and output parsing
|
||||
- Provides secure input sanitization
|
||||
- Manages ANSI escape sequence removal
|
||||
- Supports JSON output parsing
|
||||
- Implements timeout handling for long-running commands
|
||||
|
||||
Attributes:
|
||||
END (str): Marker string used to signal the end of PowerShell command output.
|
||||
process (subprocess.Popen): The underlying PowerShell subprocess with open stdin, stdout, and stderr streams.
|
||||
|
||||
Note:
|
||||
This is an abstract base class that should be extended by specific implementations
|
||||
for different PowerShell use cases.
|
||||
"""
|
||||
|
||||
END = "<END>"
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize a persistent PowerShell session.
|
||||
|
||||
Creates a subprocess running PowerShell with pipes for stdin, stdout, and stderr.
|
||||
The session is configured to run in interactive mode with no exit.
|
||||
|
||||
Note:
|
||||
This is a base implementation that should be extended by subclasses
|
||||
to add specific initialization logic (e.g., credential setup).
|
||||
"""
|
||||
# Determine the appropriate PowerShell command based on the OS
|
||||
if platform.system() == "Windows":
|
||||
powershell_cmd = "powershell"
|
||||
else:
|
||||
powershell_cmd = "pwsh"
|
||||
|
||||
self.process = subprocess.Popen(
|
||||
[powershell_cmd, "-NoExit", "-Command", "-"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
|
||||
def sanitize(self, credential: str) -> str:
|
||||
"""
|
||||
Sanitize input to prevent command injection.
|
||||
|
||||
Filters the input string to allow only letters, numbers, @, periods, underscores,
|
||||
plus signs, and hyphens. This is a security measure to prevent command injection
|
||||
attacks through credential input.
|
||||
|
||||
Args:
|
||||
credential (str): The string to sanitize.
|
||||
|
||||
Returns:
|
||||
str: The sanitized string containing only allowed characters.
|
||||
|
||||
Example:
|
||||
>>> sanitize("user@domain.com!@#$")
|
||||
"user@domain.com"
|
||||
"""
|
||||
return re.sub(r"[^a-zA-Z0-9@._+\-]", "", credential)
|
||||
|
||||
def remove_ansi(self, text: str) -> str:
|
||||
"""
|
||||
Remove ANSI color codes and other escape sequences from PowerShell output.
|
||||
|
||||
PowerShell often includes ANSI escape sequences in its output for terminal
|
||||
coloring and formatting. This method strips these sequences to produce clean,
|
||||
parseable text that can be processed programmatically.
|
||||
|
||||
Args:
|
||||
text (str): Raw text containing ANSI escape sequences from PowerShell output.
|
||||
|
||||
Returns:
|
||||
str: Clean text with all ANSI escape sequences removed, suitable for parsing.
|
||||
|
||||
Example:
|
||||
>>> remove_ansi("\x1b[32mSuccess\x1b[0m")
|
||||
"Success"
|
||||
"""
|
||||
ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
|
||||
return ansi_escape.sub("", text)
|
||||
|
||||
def execute(self, command: str) -> dict:
|
||||
"""
|
||||
Send a command to PowerShell and retrieve its output.
|
||||
|
||||
Executes the given command in the PowerShell session, adds an END marker,
|
||||
and parses the output as JSON if possible. The command is executed
|
||||
asynchronously with a timeout mechanism.
|
||||
|
||||
Args:
|
||||
command (str): PowerShell command to execute.
|
||||
|
||||
Returns:
|
||||
dict: JSON-parsed output if available, otherwise an empty dictionary.
|
||||
|
||||
Example:
|
||||
>>> execute("Get-Process | ConvertTo-Json")
|
||||
{"Name": "process1", "Id": 1234}
|
||||
"""
|
||||
self.process.stdin.write(f"{command}\n")
|
||||
self.process.stdin.write(f"Write-Output '{self.END}'\n")
|
||||
return self.json_parse_output(self.read_output())
|
||||
|
||||
def read_output(self, timeout: int = 10, default: str = "") -> str:
|
||||
"""
|
||||
Read output from a process with timeout functionality.
|
||||
|
||||
This method reads lines from process stdout until it encounters the END marker
|
||||
or the stream ends. If reading takes longer than the timeout period, the method
|
||||
returns a default value while allowing the reading to continue in the background.
|
||||
|
||||
Args:
|
||||
timeout (int, optional): Maximum time in seconds to wait for output.
|
||||
Defaults to 10.
|
||||
default (str, optional): Value to return if timeout occurs.
|
||||
Defaults to empty string.
|
||||
|
||||
Returns:
|
||||
str: Concatenated output lines or default value if timeout occurs.
|
||||
|
||||
Note:
|
||||
This method uses a daemon thread to read the output asynchronously,
|
||||
ensuring that the main thread is not blocked.
|
||||
"""
|
||||
output_lines = []
|
||||
result_queue = queue.Queue()
|
||||
|
||||
def reader_thread():
|
||||
try:
|
||||
while True:
|
||||
line = self.remove_ansi(self.process.stdout.readline().strip())
|
||||
if line == self.END:
|
||||
break
|
||||
output_lines.append(line)
|
||||
result_queue.put("\n".join(output_lines))
|
||||
except Exception as e:
|
||||
result_queue.put(str(e))
|
||||
|
||||
thread = threading.Thread(target=reader_thread)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
try:
|
||||
return result_queue.get(timeout=timeout)
|
||||
except queue.Empty:
|
||||
return default
|
||||
|
||||
def json_parse_output(self, output: str) -> dict:
|
||||
"""
|
||||
Parse command execution output to JSON format.
|
||||
|
||||
Searches for a JSON object in the output string and parses it.
|
||||
The method looks for both object and array JSON structures.
|
||||
|
||||
Args:
|
||||
output (str): The string output from a PowerShell command.
|
||||
|
||||
Returns:
|
||||
dict: Parsed JSON object if found, otherwise an empty dictionary.
|
||||
|
||||
Raises:
|
||||
JSONDecodeError: If the JSON parsing fails.
|
||||
|
||||
Example:
|
||||
>>> json_parse_output('Some text {"key": "value"} more text')
|
||||
{"key": "value"}
|
||||
"""
|
||||
if output == "":
|
||||
return {}
|
||||
|
||||
json_match = re.search(r"(\[.*\]|\{.*\})", output, re.DOTALL)
|
||||
if not json_match:
|
||||
logger.warning(
|
||||
f"Could not parse PowerShell output as JSON.\nOriginal output: {output}",
|
||||
)
|
||||
return {}
|
||||
else:
|
||||
try:
|
||||
return json.loads(json_match.group(1))
|
||||
except json.JSONDecodeError as error:
|
||||
logger.error(
|
||||
f"Error parsing PowerShell output as JSON: {str(error)}\nOriginal output: {output}",
|
||||
)
|
||||
return {}
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
Terminate the PowerShell session.
|
||||
|
||||
Sends an exit command to PowerShell and terminates the subprocess.
|
||||
This method should be called when the session is no longer needed
|
||||
to ensure proper cleanup of resources.
|
||||
|
||||
Note:
|
||||
It's important to call this method when done with the session
|
||||
to prevent resource leaks.
|
||||
"""
|
||||
if self.process:
|
||||
try:
|
||||
# Send exit command
|
||||
self.process.stdin.write("exit\n")
|
||||
self.process.stdin.flush()
|
||||
|
||||
# Terminate the process
|
||||
self.process.terminate()
|
||||
|
||||
# Wait for the process to finish
|
||||
self.process.wait(timeout=5)
|
||||
except Exception:
|
||||
# If process is still running, force kill it
|
||||
self.process.kill()
|
||||
finally:
|
||||
# Close all pipes
|
||||
self.process.stdin.close()
|
||||
self.process.stdout.close()
|
||||
self.process.stderr.close()
|
||||
self.process = None
|
||||
@@ -939,6 +939,7 @@
|
||||
"ap-southeast-3",
|
||||
"ap-southeast-4",
|
||||
"ap-southeast-5",
|
||||
"ap-southeast-7",
|
||||
"ca-central-1",
|
||||
"ca-west-1",
|
||||
"eu-central-1",
|
||||
@@ -952,6 +953,7 @@
|
||||
"il-central-1",
|
||||
"me-central-1",
|
||||
"me-south-1",
|
||||
"mx-central-1",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
@@ -4184,10 +4186,12 @@
|
||||
"entityresolution": {
|
||||
"regions": {
|
||||
"aws": [
|
||||
"af-south-1",
|
||||
"ap-northeast-1",
|
||||
"ap-northeast-2",
|
||||
"ap-southeast-1",
|
||||
"ap-southeast-2",
|
||||
"ca-central-1",
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"eu-west-2",
|
||||
@@ -6005,6 +6009,7 @@
|
||||
"ap-southeast-3",
|
||||
"ap-southeast-4",
|
||||
"ap-southeast-5",
|
||||
"ap-southeast-7",
|
||||
"ca-central-1",
|
||||
"ca-west-1",
|
||||
"eu-central-1",
|
||||
@@ -6018,6 +6023,7 @@
|
||||
"il-central-1",
|
||||
"me-central-1",
|
||||
"me-south-1",
|
||||
"mx-central-1",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
@@ -8392,6 +8398,7 @@
|
||||
"qdeveloper": {
|
||||
"regions": {
|
||||
"aws": [
|
||||
"eu-central-1",
|
||||
"us-east-1"
|
||||
],
|
||||
"aws-cn": [],
|
||||
@@ -8853,6 +8860,8 @@
|
||||
"ap-southeast-2",
|
||||
"ap-southeast-3",
|
||||
"ap-southeast-4",
|
||||
"ap-southeast-5",
|
||||
"ap-southeast-7",
|
||||
"ca-central-1",
|
||||
"ca-west-1",
|
||||
"eu-central-1",
|
||||
@@ -8866,6 +8875,7 @@
|
||||
"il-central-1",
|
||||
"me-central-1",
|
||||
"me-south-1",
|
||||
"mx-central-1",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
@@ -9302,6 +9312,7 @@
|
||||
"regions": {
|
||||
"aws": [
|
||||
"af-south-1",
|
||||
"ap-east-1",
|
||||
"ap-northeast-1",
|
||||
"ap-northeast-2",
|
||||
"ap-northeast-3",
|
||||
@@ -9320,6 +9331,7 @@
|
||||
"eu-west-1",
|
||||
"eu-west-2",
|
||||
"eu-west-3",
|
||||
"il-central-1",
|
||||
"me-central-1",
|
||||
"me-south-1",
|
||||
"sa-east-1",
|
||||
@@ -11158,6 +11170,7 @@
|
||||
"il-central-1",
|
||||
"me-central-1",
|
||||
"me-south-1",
|
||||
"mx-central-1",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
|
||||
@@ -14,7 +14,7 @@ class acm_certificates_with_secure_key_algorithms(Check):
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ACM Certificate {certificate.id} for {certificate.name} uses a secure key algorithm ({certificate.key_algorithm})."
|
||||
if certificate.key_algorithm in acm_client.audit_config.get(
|
||||
"insecure_key_algorithms", ["RSA-1024", "P-192", "SHA-1"]
|
||||
"insecure_key_algorithms", ["RSA-1024", "P-192"]
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"ACM Certificate {certificate.id} for {certificate.name} does not use a secure key algorithm ({certificate.key_algorithm})."
|
||||
|
||||
@@ -286,7 +286,7 @@ class IAM(AWSService):
|
||||
return stored_password_policy
|
||||
|
||||
def _get_users(self):
|
||||
logger.info("IAM - List Users...")
|
||||
logger.info("IAM - Get Users...")
|
||||
try:
|
||||
get_users_paginator = self.client.get_paginator("list_users")
|
||||
users = []
|
||||
@@ -469,7 +469,7 @@ class IAM(AWSService):
|
||||
)
|
||||
|
||||
def _list_attached_role_policies(self):
|
||||
logger.info("IAM - List Attached User Policies...")
|
||||
logger.info("IAM - List Attached Role Policies...")
|
||||
try:
|
||||
if self.roles:
|
||||
for role in self.roles:
|
||||
@@ -712,7 +712,7 @@ class IAM(AWSService):
|
||||
return roles
|
||||
|
||||
def _list_entities_for_policy(self, policy_arn):
|
||||
logger.info("IAM - List Entities Role For Policy...")
|
||||
logger.info("IAM - List Entities For Policy...")
|
||||
try:
|
||||
entities = {
|
||||
"Users": [],
|
||||
|
||||
@@ -161,7 +161,7 @@ class Defender(AzureService):
|
||||
{
|
||||
security_contact_default.name: SecurityContacts(
|
||||
resource_id=security_contact_default.id,
|
||||
name=security_contact_default.name,
|
||||
name=security_contact_default.get("name", "default"),
|
||||
emails=security_contact_default.emails,
|
||||
phone=security_contact_default.phone,
|
||||
alert_notifications_minimal_severity=security_contact_default.alert_notifications.minimal_severity,
|
||||
|
||||
@@ -211,12 +211,13 @@ class Provider(ABC):
|
||||
mutelist_path=arguments.mutelist_file,
|
||||
fixer_config=fixer_config,
|
||||
)
|
||||
elif "microsoft365" in provider_class_name.lower():
|
||||
elif "m365" in provider_class_name.lower():
|
||||
provider_class(
|
||||
region=arguments.region,
|
||||
config_path=arguments.config_file,
|
||||
mutelist_path=arguments.mutelist_file,
|
||||
sp_env_auth=arguments.sp_env_auth,
|
||||
env_auth=arguments.env_auth,
|
||||
az_cli_auth=arguments.az_cli_auth,
|
||||
browser_auth=arguments.browser_auth,
|
||||
tenant_id=arguments.tenant_id,
|
||||
|
||||
@@ -396,14 +396,19 @@ class GcpProvider(Provider):
|
||||
client_secrets_path = os.path.abspath(credentials_file)
|
||||
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = client_secrets_path
|
||||
|
||||
access_token = os.getenv("CLOUDSDK_AUTH_ACCESS_TOKEN")
|
||||
if access_token:
|
||||
logger.info("Using access token from CLOUDSDK_AUTH_ACCESS_TOKEN")
|
||||
credentials = Credentials(token=access_token, scopes=scopes)
|
||||
default_project_id = os.getenv("GOOGLE_CLOUD_PROJECT", "")
|
||||
return credentials, default_project_id
|
||||
|
||||
# Get default credentials
|
||||
credentials, default_project_id = default(scopes=scopes)
|
||||
|
||||
# Refresh the credentials to ensure they are valid
|
||||
credentials.refresh(Request())
|
||||
|
||||
logger.info(f"Initial credentials: {credentials}")
|
||||
|
||||
if service_account:
|
||||
# Create the impersonated credentials
|
||||
credentials = impersonated_credentials.Credentials(
|
||||
@@ -411,7 +416,7 @@ class GcpProvider(Provider):
|
||||
target_principal=service_account,
|
||||
target_scopes=scopes,
|
||||
)
|
||||
logger.info(f"Impersonated credentials: {credentials}")
|
||||
logger.info(f"Impersonating service account: {service_account}")
|
||||
|
||||
return credentials, default_project_id
|
||||
except Exception as error:
|
||||
@@ -628,15 +633,21 @@ class GcpProvider(Provider):
|
||||
.get("labels", {})
|
||||
.items()
|
||||
}
|
||||
project_id = asset["resource"]["data"]["projectId"]
|
||||
project_number = asset["resource"]["data"]["projectNumber"]
|
||||
project_id = (
|
||||
asset["resource"]["data"].get("projectId")
|
||||
if asset["resource"]["data"].get("projectId")
|
||||
else project_number
|
||||
)
|
||||
project_name = (
|
||||
asset["resource"]["data"].get("name")
|
||||
if asset["resource"]["data"].get("name")
|
||||
else project_id
|
||||
)
|
||||
gcp_project = GCPProject(
|
||||
number=asset["resource"]["data"]["projectNumber"],
|
||||
number=project_number,
|
||||
id=project_id,
|
||||
name=(
|
||||
asset["resource"]["data"].get("name")
|
||||
if asset["resource"]["data"].get("name")
|
||||
else project_id
|
||||
),
|
||||
name=project_name,
|
||||
lifecycle_state=asset["resource"]["data"].get(
|
||||
"lifecycleState"
|
||||
),
|
||||
@@ -677,15 +688,22 @@ class GcpProvider(Provider):
|
||||
labels = {
|
||||
k: v for k, v in project.get("labels", {}).items()
|
||||
}
|
||||
project_number = project["projectNumber"]
|
||||
project_id = (
|
||||
project.get("projectId")
|
||||
if project.get("projectId")
|
||||
else project_number
|
||||
)
|
||||
project_name = (
|
||||
project.get("name")
|
||||
if project.get("name")
|
||||
else project_id
|
||||
)
|
||||
project_id = project["projectId"]
|
||||
gcp_project = GCPProject(
|
||||
number=project["projectNumber"],
|
||||
number=project_number,
|
||||
id=project_id,
|
||||
name=(
|
||||
project.get("name")
|
||||
if project.get("name")
|
||||
else project_id
|
||||
),
|
||||
name=project_name,
|
||||
lifecycle_state=project["lifecycleState"],
|
||||
labels=labels,
|
||||
)
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "ServiceAccountKey",
|
||||
"Description": "Ensure That There Are No Dormant Service Account Keys for Each Service Account. A key is considered dormant if it has been inactive for more than 180 days.",
|
||||
"Description": "Ensure That There Are No Unused Service Account Keys for Each Service Account.",
|
||||
"Risk": "Anyone who has access to the keys will be able to access resources through the service account. GCP-managed keys are used by Cloud Platform services such as App Engine and Compute Engine. These keys cannot be downloaded. Google will keep the keys and automatically rotate them on an approximately weekly basis. User-managed keys are created, downloadable, and managed by users.",
|
||||
"RelatedUrl": "https://cloud.google.com/iam/docs/service-account-overview#identify-unused",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/gcp/CloudIAM/delete-user-managed-service-account-keys.html",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
|
||||
@@ -8,6 +8,9 @@ from prowler.providers.gcp.services.monitoring.monitoring_client import (
|
||||
class iam_sa_user_managed_key_unused(Check):
|
||||
def execute(self) -> Check_Report_GCP:
|
||||
findings = []
|
||||
max_unused_days = monitoring_client.audit_config.get(
|
||||
"max_unused_account_days", 180
|
||||
)
|
||||
keys_used = monitoring_client.sa_keys_metrics
|
||||
for account in iam_client.service_accounts:
|
||||
for key in account.keys:
|
||||
@@ -21,10 +24,10 @@ class iam_sa_user_managed_key_unused(Check):
|
||||
)
|
||||
if key.name in keys_used:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"User-managed key {key.name} for Service Account {account.email} was used over the last 180 days."
|
||||
report.status_extended = f"User-managed key {key.name} for Service Account {account.email} was used over the last {max_unused_days} days."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"User-managed key {key.name} for Service Account {account.email} was not used over the last 180 days."
|
||||
report.status_extended = f"User-managed key {key.name} for Service Account {account.email} was not used over the last {max_unused_days} days."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
@@ -35,6 +35,7 @@ class IAM(GCPService):
|
||||
email=account["email"],
|
||||
display_name=account.get("displayName", ""),
|
||||
project_id=project_id,
|
||||
uniqueId=account.get("uniqueId", ""),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -99,6 +100,7 @@ class ServiceAccount(BaseModel):
|
||||
display_name: str
|
||||
keys: list[Key] = []
|
||||
project_id: str
|
||||
uniqueId: str
|
||||
|
||||
|
||||
class AccessApproval(GCPService):
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "gcp",
|
||||
"CheckID": "iam_service_account_unused",
|
||||
"CheckTitle": "Ensure That There Are No Unused Service Accounts",
|
||||
"CheckType": [],
|
||||
"ServiceName": "iam",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "ServiceAccount",
|
||||
"Description": "Ensure That There Are No Unused Service Accounts.",
|
||||
"Risk": "A malicious actor could make use of privilege escalation or impersonation to access an unused Service Account that is over-privileged.",
|
||||
"RelatedUrl": "https://cloud.google.com/iam/docs/service-account-overview#identify-unused",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "It is recommended to disable or remove unused Service Accounts.",
|
||||
"Url": "https://cloud.google.com/iam/docs/service-account-overview#identify-unused"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_GCP
|
||||
from prowler.providers.gcp.services.iam.iam_client import iam_client
|
||||
from prowler.providers.gcp.services.monitoring.monitoring_client import (
|
||||
monitoring_client,
|
||||
)
|
||||
|
||||
|
||||
class iam_service_account_unused(Check):
|
||||
def execute(self) -> Check_Report_GCP:
|
||||
findings = []
|
||||
max_unused_days = monitoring_client.audit_config.get(
|
||||
"max_unused_account_days", 180
|
||||
)
|
||||
sa_ids_used = monitoring_client.sa_api_metrics
|
||||
for account in iam_client.service_accounts:
|
||||
report = Check_Report_GCP(
|
||||
metadata=self.metadata(),
|
||||
resource=account,
|
||||
resource_id=account.email,
|
||||
location=iam_client.region,
|
||||
)
|
||||
if account.uniqueId in sa_ids_used:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Service Account {account.email} was used over the last {max_unused_days} days."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Service Account {account.email} was not used over the last {max_unused_days} days."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -12,10 +12,12 @@ class Monitoring(GCPService):
|
||||
super().__init__(__class__.__name__, provider, api_version="v3")
|
||||
self.alert_policies = []
|
||||
self.sa_keys_metrics = set()
|
||||
self.sa_api_metrics = set()
|
||||
self._get_alert_policies()
|
||||
self._get_sa_keys_metrics(
|
||||
"iam.googleapis.com/service_account/key/authn_events_count"
|
||||
)
|
||||
self._get_sa_api_metrics("serviceruntime.googleapis.com/api/request_count")
|
||||
|
||||
def _get_alert_policies(self):
|
||||
for project_id in self.project_ids:
|
||||
@@ -54,6 +56,7 @@ class Monitoring(GCPService):
|
||||
|
||||
def _get_sa_keys_metrics(self, metric_type):
|
||||
try:
|
||||
max_unused_days = int(self.audit_config.get("max_unused_account_days", 180))
|
||||
end_time = (
|
||||
datetime.datetime.now(datetime.timezone.utc)
|
||||
.replace(microsecond=0)
|
||||
@@ -62,7 +65,7 @@ class Monitoring(GCPService):
|
||||
start_time = (
|
||||
(
|
||||
datetime.datetime.now(datetime.timezone.utc)
|
||||
- datetime.timedelta(days=180)
|
||||
- datetime.timedelta(days=max_unused_days)
|
||||
)
|
||||
.replace(microsecond=0)
|
||||
.isoformat()
|
||||
@@ -96,6 +99,53 @@ class Monitoring(GCPService):
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def _get_sa_api_metrics(self, metric_type):
|
||||
try:
|
||||
max_unused_days = int(self.audit_config.get("max_unused_account_days", 180))
|
||||
end_time = (
|
||||
datetime.datetime.now(datetime.timezone.utc)
|
||||
.replace(microsecond=0)
|
||||
.isoformat()
|
||||
)
|
||||
start_time = (
|
||||
(
|
||||
datetime.datetime.now(datetime.timezone.utc)
|
||||
- datetime.timedelta(days=max_unused_days)
|
||||
)
|
||||
.replace(microsecond=0)
|
||||
.isoformat()
|
||||
)
|
||||
for project_id in self.project_ids:
|
||||
try:
|
||||
request = (
|
||||
self.client.projects()
|
||||
.timeSeries()
|
||||
.list(
|
||||
name=f"projects/{project_id}",
|
||||
filter=f'metric.type = "{metric_type}"',
|
||||
interval_startTime=start_time,
|
||||
interval_endTime=end_time,
|
||||
view="HEADERS",
|
||||
)
|
||||
)
|
||||
response = request.execute()
|
||||
|
||||
for metric in response.get("timeSeries", []):
|
||||
sa_id = metric["resource"]["labels"].get("credential_id")
|
||||
if sa_id and "serviceaccount:" in sa_id:
|
||||
self.sa_api_metrics.add(
|
||||
sa_id.replace("serviceaccount:", "")
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
|
||||
class AlertPolicy(BaseModel):
|
||||
name: str
|
||||
|
||||
@@ -1,103 +1,123 @@
|
||||
from prowler.exceptions.exceptions import ProwlerException
|
||||
|
||||
|
||||
# Exceptions codes from 5000 to 5999 are reserved for Microsoft365 exceptions
|
||||
class Microsoft365BaseException(ProwlerException):
|
||||
"""Base class for Microsoft365 Errors."""
|
||||
# Exceptions codes from 5000 to 5999 are reserved for M365 exceptions
|
||||
class M365BaseException(ProwlerException):
|
||||
"""Base class for M365 Errors."""
|
||||
|
||||
MICROSOFT365_ERROR_CODES = {
|
||||
(6000, "Microsoft365EnvironmentVariableError"): {
|
||||
"message": "Microsoft365 environment variable error",
|
||||
"remediation": "Check the Microsoft365 environment variables and ensure they are properly set.",
|
||||
(6000, "M365EnvironmentVariableError"): {
|
||||
"message": "Microsoft 365 environment variable error",
|
||||
"remediation": "Check the Microsoft 365 environment variables and ensure they are properly set.",
|
||||
},
|
||||
(6001, "Microsoft365ArgumentTypeValidationError"): {
|
||||
"message": "Microsoft365 argument type validation error",
|
||||
"remediation": "Check the provided argument types specific to Microsoft365 and ensure they meet the required format.",
|
||||
(6001, "M365ArgumentTypeValidationError"): {
|
||||
"message": "Microsoft 365 argument type validation error",
|
||||
"remediation": "Check the provided argument types specific to Microsoft 365 and ensure they meet the required format.",
|
||||
},
|
||||
(6002, "Microsoft365SetUpRegionConfigError"): {
|
||||
"message": "Microsoft365 region configuration setup error",
|
||||
"remediation": "Check the Microsoft365 region configuration and ensure it is properly set up.",
|
||||
(6002, "M365SetUpRegionConfigError"): {
|
||||
"message": "Microsoft 365 region configuration setup error",
|
||||
"remediation": "Check the Microsoft 365 region configuration and ensure it is properly set up.",
|
||||
},
|
||||
(6003, "Microsoft365HTTPResponseError"): {
|
||||
"message": "Error in HTTP response from Microsoft365",
|
||||
(6003, "M365HTTPResponseError"): {
|
||||
"message": "Error in HTTP response from Microsoft 365",
|
||||
"remediation": "",
|
||||
},
|
||||
(6004, "Microsoft365CredentialsUnavailableError"): {
|
||||
"message": "Error trying to configure Microsoft365 credentials because they are unavailable",
|
||||
"remediation": "Check the dictionary and ensure it is properly set up for Microsoft365 credentials. TENANT_ID, CLIENT_ID and CLIENT_SECRET are required.",
|
||||
(6004, "M365CredentialsUnavailableError"): {
|
||||
"message": "Error trying to configure Microsoft 365 credentials because they are unavailable",
|
||||
"remediation": "Check the dictionary and ensure it is properly set up for Microsoft 365 credentials. TENANT_ID, CLIENT_ID and CLIENT_SECRET are required.",
|
||||
},
|
||||
(6005, "Microsoft365GetTokenIdentityError"): {
|
||||
"message": "Error trying to get token from Microsoft365 Identity",
|
||||
"remediation": "Check the Microsoft365 Identity and ensure it is properly set up.",
|
||||
(6005, "M365GetTokenIdentityError"): {
|
||||
"message": "Error trying to get token from Microsoft 365 Identity",
|
||||
"remediation": "Check the Microsoft 365 Identity and ensure it is properly set up.",
|
||||
},
|
||||
(6006, "Microsoft365ClientAuthenticationError"): {
|
||||
(6006, "M365ClientAuthenticationError"): {
|
||||
"message": "Error in client authentication",
|
||||
"remediation": "Check the client authentication and ensure it is properly set up.",
|
||||
},
|
||||
(6007, "Microsoft365NotValidTenantIdError"): {
|
||||
(6007, "M365NotValidTenantIdError"): {
|
||||
"message": "The provided tenant ID is not valid",
|
||||
"remediation": "Check the tenant ID and ensure it is a valid ID.",
|
||||
},
|
||||
(6008, "Microsoft365NotValidClientIdError"): {
|
||||
(6008, "M365NotValidClientIdError"): {
|
||||
"message": "The provided client ID is not valid",
|
||||
"remediation": "Check the client ID and ensure it is a valid ID.",
|
||||
},
|
||||
(6009, "Microsoft365NotValidClientSecretError"): {
|
||||
(6009, "M365NotValidClientSecretError"): {
|
||||
"message": "The provided client secret is not valid",
|
||||
"remediation": "Check the client secret and ensure it is a valid secret.",
|
||||
},
|
||||
(6010, "Microsoft365ConfigCredentialsError"): {
|
||||
"message": "Error in configuration of Microsoft365 credentials",
|
||||
"remediation": "Check the configuration of Microsoft365 credentials and ensure it is properly set up.",
|
||||
(6010, "M365ConfigCredentialsError"): {
|
||||
"message": "Error in configuration of Microsoft 365 credentials",
|
||||
"remediation": "Check the configuration of Microsoft 365 credentials and ensure it is properly set up.",
|
||||
},
|
||||
(6011, "Microsoft365ClientIdAndClientSecretNotBelongingToTenantIdError"): {
|
||||
(6011, "M365ClientIdAndClientSecretNotBelongingToTenantIdError"): {
|
||||
"message": "The provided client ID and client secret do not belong to the provided tenant ID",
|
||||
"remediation": "Check the client ID and client secret and ensure they belong to the provided tenant ID.",
|
||||
},
|
||||
(6012, "Microsoft365TenantIdAndClientSecretNotBelongingToClientIdError"): {
|
||||
(6012, "M365TenantIdAndClientSecretNotBelongingToClientIdError"): {
|
||||
"message": "The provided tenant ID and client secret do not belong to the provided client ID",
|
||||
"remediation": "Check the tenant ID and client secret and ensure they belong to the provided client ID.",
|
||||
},
|
||||
(6013, "Microsoft365TenantIdAndClientIdNotBelongingToClientSecretError"): {
|
||||
(6013, "M365TenantIdAndClientIdNotBelongingToClientSecretError"): {
|
||||
"message": "The provided tenant ID and client ID do not belong to the provided client secret",
|
||||
"remediation": "Check the tenant ID and client ID and ensure they belong to the provided client secret.",
|
||||
},
|
||||
(6014, "Microsoft365InvalidProviderIdError"): {
|
||||
(6014, "M365InvalidProviderIdError"): {
|
||||
"message": "The provided provider_id does not match with the available subscriptions",
|
||||
"remediation": "Check the provider_id and ensure it is a valid subscription for the given credentials.",
|
||||
},
|
||||
(6015, "Microsoft365NoAuthenticationMethodError"): {
|
||||
"message": "No Microsoft365 authentication method found",
|
||||
"remediation": "Check that any authentication method is properly set up for Microsoft365.",
|
||||
(6015, "M365NoAuthenticationMethodError"): {
|
||||
"message": "No Microsoft 365 authentication method found",
|
||||
"remediation": "Check that any authentication method is properly set up for Microsoft 365.",
|
||||
},
|
||||
(6016, "Microsoft365SetUpSessionError"): {
|
||||
(6016, "M365SetUpSessionError"): {
|
||||
"message": "Error setting up session",
|
||||
"remediation": "Check the session setup and ensure it is properly set up.",
|
||||
},
|
||||
(6017, "Microsoft365DefaultAzureCredentialError"): {
|
||||
(6017, "M365DefaultAzureCredentialError"): {
|
||||
"message": "Error with DefaultAzureCredential",
|
||||
"remediation": "Ensure DefaultAzureCredential is correctly configured.",
|
||||
},
|
||||
(6018, "Microsoft365InteractiveBrowserCredentialError"): {
|
||||
(6018, "M365InteractiveBrowserCredentialError"): {
|
||||
"message": "Error with InteractiveBrowserCredential",
|
||||
"remediation": "Ensure InteractiveBrowserCredential is correctly configured.",
|
||||
},
|
||||
(6019, "Microsoft365BrowserAuthNoTenantIDError"): {
|
||||
"message": "Microsoft365 Tenant ID (--tenant-id) is required for browser authentication mode",
|
||||
"remediation": "Check the Microsoft365 Tenant ID and ensure it is properly set up.",
|
||||
(6019, "M365BrowserAuthNoTenantIDError"): {
|
||||
"message": "Microsoft 365 Tenant ID (--tenant-id) is required for browser authentication mode",
|
||||
"remediation": "Check the Microsoft 365 Tenant ID and ensure it is properly set up.",
|
||||
},
|
||||
(6020, "Microsoft365BrowserAuthNoFlagError"): {
|
||||
"message": "Microsoft365 tenant ID error: browser authentication flag (--browser-auth) not found",
|
||||
(6020, "M365BrowserAuthNoFlagError"): {
|
||||
"message": "Microsoft 365 tenant ID error: browser authentication flag (--browser-auth) not found",
|
||||
"remediation": "To use browser authentication, ensure the tenant ID is properly set.",
|
||||
},
|
||||
(6021, "Microsoft365NotTenantIdButClientIdAndClientSecretError"): {
|
||||
"message": "Tenant Id is required for Microsoft365 static credentials. Make sure you are using the correct credentials.",
|
||||
"remediation": "Check the Microsoft365 Tenant ID and ensure it is properly set up.",
|
||||
(6021, "M365NotTenantIdButClientIdAndClientSecretError"): {
|
||||
"message": "Tenant Id is required for Microsoft 365 static credentials. Make sure you are using the correct credentials.",
|
||||
"remediation": "Check the Microsoft 365 Tenant ID and ensure it is properly set up.",
|
||||
},
|
||||
(6022, "M365MissingEnvironmentCredentialsError"): {
|
||||
"message": "User and Password environment variables are needed to use Credentials authentication method.",
|
||||
"remediation": "Ensure your environment variables are properly set up.",
|
||||
},
|
||||
(6023, "M365EnvironmentUserCredentialsError"): {
|
||||
"message": "User or Password environment variables are not correct.",
|
||||
"remediation": "Ensure you are using the right credentials.",
|
||||
},
|
||||
(6024, "M365NotValidUserError"): {
|
||||
"message": "The provided M365 User is not valid.",
|
||||
"remediation": "Check the M365 User and ensure it is a valid user.",
|
||||
},
|
||||
(6025, "M365NotValidEncryptedPasswordError"): {
|
||||
"message": "The provided M365 Encrypted Password is not valid.",
|
||||
"remediation": "Check the M365 Encrypted Password and ensure it is a valid password.",
|
||||
},
|
||||
(6026, "M365UserNotBelongingToTenantError"): {
|
||||
"message": "The provided M365 User does not belong to the specified tenant.",
|
||||
"remediation": "Check the M365 User email domain and ensure it belongs to the specified tenant.",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
provider = "Microsoft365"
|
||||
provider = "M365"
|
||||
error_info = self.MICROSOFT365_ERROR_CODES.get((code, self.__class__.__name__))
|
||||
if message:
|
||||
error_info["message"] = message
|
||||
@@ -110,170 +130,197 @@ class Microsoft365BaseException(ProwlerException):
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365CredentialsError(Microsoft365BaseException):
|
||||
"""Base class for Microsoft365 credentials errors."""
|
||||
class M365CredentialsError(M365BaseException):
|
||||
"""Base class for M365 credentials errors."""
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
super().__init__(code, file, original_exception, message)
|
||||
|
||||
|
||||
class Microsoft365EnvironmentVariableError(Microsoft365CredentialsError):
|
||||
class M365EnvironmentVariableError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6000, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365ArgumentTypeValidationError(Microsoft365BaseException):
|
||||
class M365ArgumentTypeValidationError(M365BaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6001, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365SetUpRegionConfigError(Microsoft365BaseException):
|
||||
class M365SetUpRegionConfigError(M365BaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6002, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365HTTPResponseError(Microsoft365BaseException):
|
||||
class M365HTTPResponseError(M365BaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6003, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365CredentialsUnavailableError(Microsoft365CredentialsError):
|
||||
class M365CredentialsUnavailableError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6004, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365GetTokenIdentityError(Microsoft365BaseException):
|
||||
class M365GetTokenIdentityError(M365BaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6005, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365ClientAuthenticationError(Microsoft365CredentialsError):
|
||||
class M365ClientAuthenticationError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6006, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365NotValidTenantIdError(Microsoft365CredentialsError):
|
||||
class M365NotValidTenantIdError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6007, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365NotValidClientIdError(Microsoft365CredentialsError):
|
||||
class M365NotValidClientIdError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6008, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365NotValidClientSecretError(Microsoft365CredentialsError):
|
||||
class M365NotValidClientSecretError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6009, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365ConfigCredentialsError(Microsoft365CredentialsError):
|
||||
class M365ConfigCredentialsError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6010, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365ClientIdAndClientSecretNotBelongingToTenantIdError(
|
||||
Microsoft365CredentialsError
|
||||
):
|
||||
class M365ClientIdAndClientSecretNotBelongingToTenantIdError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6011, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365TenantIdAndClientSecretNotBelongingToClientIdError(
|
||||
Microsoft365CredentialsError
|
||||
):
|
||||
class M365TenantIdAndClientSecretNotBelongingToClientIdError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6012, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365TenantIdAndClientIdNotBelongingToClientSecretError(
|
||||
Microsoft365CredentialsError
|
||||
):
|
||||
class M365TenantIdAndClientIdNotBelongingToClientSecretError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6013, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365InvalidProviderIdError(Microsoft365BaseException):
|
||||
class M365InvalidProviderIdError(M365BaseException):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6014, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365NoAuthenticationMethodError(Microsoft365CredentialsError):
|
||||
class M365NoAuthenticationMethodError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6015, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365SetUpSessionError(Microsoft365CredentialsError):
|
||||
class M365SetUpSessionError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6016, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365DefaultAzureCredentialError(Microsoft365CredentialsError):
|
||||
class M365DefaultAzureCredentialError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6017, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365InteractiveBrowserCredentialError(Microsoft365CredentialsError):
|
||||
class M365InteractiveBrowserCredentialError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6018, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365BrowserAuthNoTenantIDError(Microsoft365CredentialsError):
|
||||
class M365BrowserAuthNoTenantIDError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6019, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365BrowserAuthNoFlagError(Microsoft365CredentialsError):
|
||||
class M365BrowserAuthNoFlagError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6020, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class Microsoft365NotTenantIdButClientIdAndClientSecretError(
|
||||
Microsoft365CredentialsError
|
||||
):
|
||||
class M365NotTenantIdButClientIdAndClientSecretError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6021, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class M365MissingEnvironmentCredentialsError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6022, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class M365EnvironmentUserCredentialsError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6023, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class M365NotValidUserError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6024, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class M365NotValidEncryptedPasswordError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6025, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class M365UserNotBelongingToTenantError(M365CredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
6026, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
61
prowler/providers/m365/lib/arguments/arguments.py
Normal file
61
prowler/providers/m365/lib/arguments/arguments.py
Normal file
@@ -0,0 +1,61 @@
|
||||
def init_parser(self):
|
||||
"""Init the M365 Provider CLI parser"""
|
||||
m365_parser = self.subparsers.add_parser(
|
||||
"m365",
|
||||
parents=[self.common_providers_parser],
|
||||
help="M365 Provider",
|
||||
)
|
||||
# Authentication Modes
|
||||
m365_auth_subparser = m365_parser.add_argument_group("Authentication Modes")
|
||||
m365_auth_modes_group = m365_auth_subparser.add_mutually_exclusive_group()
|
||||
m365_auth_modes_group.add_argument(
|
||||
"--az-cli-auth",
|
||||
action="store_true",
|
||||
help="Use Azure CLI authentication to log in against Microsoft 365",
|
||||
)
|
||||
m365_auth_modes_group.add_argument(
|
||||
"--env-auth",
|
||||
action="store_true",
|
||||
help="Use User and Password environment variables authentication to log in against Microsoft 365",
|
||||
)
|
||||
m365_auth_modes_group.add_argument(
|
||||
"--sp-env-auth",
|
||||
action="store_true",
|
||||
help="Use Azure Service Principal environment variables authentication to log in against Microsoft 365",
|
||||
)
|
||||
m365_auth_modes_group.add_argument(
|
||||
"--browser-auth",
|
||||
action="store_true",
|
||||
help="Use Azure interactive browser authentication to log in against Microsoft 365",
|
||||
)
|
||||
m365_parser.add_argument(
|
||||
"--tenant-id",
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Microsoft 365 Tenant ID to be used with --browser-auth option",
|
||||
)
|
||||
m365_parser.add_argument(
|
||||
"--user",
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Microsoft 365 user email",
|
||||
)
|
||||
m365_parser.add_argument(
|
||||
"--encypted-password",
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Microsoft 365 encrypted password",
|
||||
)
|
||||
# Regions
|
||||
m365_regions_subparser = m365_parser.add_argument_group("Regions")
|
||||
m365_regions_subparser.add_argument(
|
||||
"--region",
|
||||
nargs="?",
|
||||
default="M365Global",
|
||||
choices=[
|
||||
"M365Global",
|
||||
"M365GlobalChina",
|
||||
"M365USGovernment",
|
||||
],
|
||||
help="Microsoft 365 region to be used, default is M365Global",
|
||||
)
|
||||
@@ -1,12 +1,12 @@
|
||||
from prowler.lib.check.models import CheckReportMicrosoft365
|
||||
from prowler.lib.check.models import CheckReportM365
|
||||
from prowler.lib.mutelist.mutelist import Mutelist
|
||||
from prowler.lib.outputs.utils import unroll_dict, unroll_tags
|
||||
|
||||
|
||||
class Microsoft365Mutelist(Mutelist):
|
||||
class M365Mutelist(Mutelist):
|
||||
def is_finding_muted(
|
||||
self,
|
||||
finding: CheckReportMicrosoft365,
|
||||
finding: CheckReportM365,
|
||||
) -> bool:
|
||||
return self.is_muted(
|
||||
finding.tenant_id,
|
||||
289
prowler/providers/m365/lib/powershell/m365_powershell.py
Normal file
289
prowler/providers/m365/lib/powershell/m365_powershell.py
Normal file
@@ -0,0 +1,289 @@
|
||||
import os
|
||||
|
||||
import msal
|
||||
|
||||
from prowler.lib.powershell.powershell import PowerShellSession
|
||||
from prowler.providers.m365.exceptions.exceptions import (
|
||||
M365UserNotBelongingToTenantError,
|
||||
)
|
||||
from prowler.providers.m365.models import M365Credentials
|
||||
|
||||
|
||||
class M365PowerShell(PowerShellSession):
|
||||
"""
|
||||
Microsoft 365 specific PowerShell session management implementation.
|
||||
|
||||
This class extends the base PowerShellSession to provide Microsoft 365 specific
|
||||
functionality, including authentication, Teams management, and Exchange Online
|
||||
operations.
|
||||
|
||||
Features:
|
||||
- Microsoft 365 credential management
|
||||
- Teams client configuration
|
||||
- Exchange Online connectivity
|
||||
- Audit log configuration
|
||||
- Secure credential handling
|
||||
|
||||
Attributes:
|
||||
credentials (M365Credentials): The Microsoft 365 credentials used for authentication.
|
||||
|
||||
Note:
|
||||
This class requires the Microsoft Teams and Exchange Online PowerShell modules
|
||||
to be installed and available in the PowerShell environment.
|
||||
"""
|
||||
|
||||
def __init__(self, credentials: M365Credentials):
|
||||
"""
|
||||
Initialize a Microsoft 365 PowerShell session.
|
||||
|
||||
Sets up the PowerShell session and initializes the provided credentials
|
||||
for Microsoft 365 authentication.
|
||||
|
||||
Args:
|
||||
credentials (M365Credentials): The Microsoft 365 credentials to use
|
||||
for authentication.
|
||||
"""
|
||||
super().__init__()
|
||||
self.init_credential(credentials)
|
||||
|
||||
def init_credential(self, credentials: M365Credentials) -> None:
|
||||
"""
|
||||
Initialize PowerShell credential object for Microsoft 365 authentication.
|
||||
|
||||
Sanitizes the username and password, then creates a PSCredential object
|
||||
in the PowerShell session for use with Microsoft 365 cmdlets.
|
||||
|
||||
Args:
|
||||
credentials (M365Credentials): The credentials object containing
|
||||
username and password.
|
||||
|
||||
Note:
|
||||
The credentials are sanitized to prevent command injection and
|
||||
stored securely in the PowerShell session.
|
||||
"""
|
||||
# Sanitize user and password
|
||||
user = self.sanitize(credentials.user)
|
||||
passwd = self.sanitize(credentials.passwd)
|
||||
|
||||
# Securely convert encrypted password to SecureString
|
||||
self.execute(f'$user = "{user}"')
|
||||
self.execute(f'$secureString = "{passwd}" | ConvertTo-SecureString')
|
||||
self.execute(
|
||||
"$credential = New-Object System.Management.Automation.PSCredential ($user, $secureString)"
|
||||
)
|
||||
|
||||
def test_credentials(self, credentials: M365Credentials) -> bool:
|
||||
"""
|
||||
Test Microsoft 365 credentials by attempting to authenticate against Entra ID.
|
||||
|
||||
Args:
|
||||
credentials (M365Credentials): The credentials object containing
|
||||
username and password to test.
|
||||
|
||||
Returns:
|
||||
bool: True if credentials are valid and authentication succeeds, False otherwise.
|
||||
"""
|
||||
self.execute(
|
||||
f'$securePassword = "{credentials.passwd}" | ConvertTo-SecureString\n'
|
||||
)
|
||||
self.execute(
|
||||
f'$credential = New-Object System.Management.Automation.PSCredential("{credentials.user}", $securePassword)\n'
|
||||
)
|
||||
self.process.stdin.write(
|
||||
'Write-Output "$($credential.GetNetworkCredential().Password)"\n'
|
||||
)
|
||||
self.process.stdin.write(f"Write-Output '{self.END}'\n")
|
||||
decrypted_password = self.read_output()
|
||||
|
||||
app = msal.ConfidentialClientApplication(
|
||||
client_id=credentials.client_id,
|
||||
client_credential=credentials.client_secret,
|
||||
authority=f"https://login.microsoftonline.com/{credentials.tenant_id}",
|
||||
)
|
||||
|
||||
result = app.acquire_token_by_username_password(
|
||||
username=credentials.user,
|
||||
password=decrypted_password, # Needs to be in plain text
|
||||
scopes=["https://graph.microsoft.com/.default"],
|
||||
)
|
||||
|
||||
if result is None:
|
||||
return False
|
||||
|
||||
if "access_token" not in result:
|
||||
return False
|
||||
|
||||
# Validate user credentials belong to tenant
|
||||
user_domain = credentials.user.split("@")[1]
|
||||
if not credentials.provider_id.endswith(user_domain):
|
||||
raise M365UserNotBelongingToTenantError(
|
||||
file=os.path.basename(__file__),
|
||||
message="The provided M365 User does not belong to the specified tenant.",
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def connect_microsoft_teams(self) -> dict:
|
||||
"""
|
||||
Connect to Microsoft Teams Module PowerShell Module.
|
||||
|
||||
Establishes a connection to Microsoft Teams using the initialized credentials.
|
||||
|
||||
Returns:
|
||||
dict: Connection status information in JSON format.
|
||||
|
||||
Note:
|
||||
This method requires the Microsoft Teams PowerShell module to be installed.
|
||||
"""
|
||||
return self.execute("Connect-MicrosoftTeams -Credential $credential")
|
||||
|
||||
def get_teams_settings(self) -> dict:
|
||||
"""
|
||||
Get Teams Client Settings.
|
||||
|
||||
Retrieves the current Microsoft Teams client configuration settings.
|
||||
|
||||
Returns:
|
||||
dict: Teams client configuration settings in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_teams_settings()
|
||||
{
|
||||
"AllowBox": true,
|
||||
"AllowDropBox": true,
|
||||
"AllowGoogleDrive": true
|
||||
}
|
||||
"""
|
||||
return self.execute("Get-CsTeamsClientConfiguration | ConvertTo-Json")
|
||||
|
||||
def connect_exchange_online(self) -> dict:
|
||||
"""
|
||||
Connect to Exchange Online PowerShell Module.
|
||||
|
||||
Establishes a connection to Exchange Online using the initialized credentials.
|
||||
|
||||
Returns:
|
||||
dict: Connection status information in JSON format.
|
||||
|
||||
Note:
|
||||
This method requires the Exchange Online PowerShell module to be installed.
|
||||
"""
|
||||
return self.execute("Connect-ExchangeOnline -Credential $credential")
|
||||
|
||||
def get_audit_log_config(self) -> dict:
|
||||
"""
|
||||
Get Purview Admin Audit Log Settings.
|
||||
|
||||
Retrieves the current audit log configuration settings for Microsoft Purview.
|
||||
|
||||
Returns:
|
||||
dict: Audit log configuration settings in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_audit_log_config()
|
||||
{
|
||||
"UnifiedAuditLogIngestionEnabled": true
|
||||
}
|
||||
"""
|
||||
return self.execute(
|
||||
"Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled | ConvertTo-Json"
|
||||
)
|
||||
|
||||
def get_malware_filter_policy(self) -> dict:
|
||||
"""
|
||||
Get Defender Malware Filter Policy.
|
||||
|
||||
Retrieves the current Defender anti-malware filter policy settings.
|
||||
|
||||
Returns:
|
||||
dict: Malware filter policy settings in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_malware_filter_policy()
|
||||
{
|
||||
"EnableFileFilter": true,
|
||||
"Identity": "Default"
|
||||
}
|
||||
"""
|
||||
return self.execute("Get-MalwareFilterPolicy | ConvertTo-Json")
|
||||
|
||||
def get_antiphishing_policy(self) -> dict:
|
||||
"""
|
||||
Get Defender Antiphishing Policy.
|
||||
|
||||
Retrieves the current Defender anti-phishing policy settings.
|
||||
|
||||
Returns:
|
||||
dict: Antiphishing policy settings in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_antiphishing_policy()
|
||||
{
|
||||
"EnableSpoofIntelligence": true,
|
||||
"AuthenticationFailAction": "Quarantine",
|
||||
"DmarcRejectAction": "Quarantine",
|
||||
"DmarcQuarantineAction": "Quarantine",
|
||||
"EnableFirstContactSafetyTips": true,
|
||||
"EnableUnauthenticatedSender": true,
|
||||
"EnableViaTag": true,
|
||||
"HonorDmarcPolicy": true,
|
||||
"IsDefault": false
|
||||
}
|
||||
"""
|
||||
return self.execute("Get-AntiPhishPolicy | ConvertTo-Json")
|
||||
|
||||
def get_antiphishing_rules(self) -> dict:
|
||||
"""
|
||||
Get Defender Antiphishing Rules.
|
||||
|
||||
Retrieves the current Defender anti-phishing rules.
|
||||
|
||||
Returns:
|
||||
dict: Antiphishing rules in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_antiphishing_rules()
|
||||
{
|
||||
"Name": "Rule1",
|
||||
"State": Enabled,
|
||||
}
|
||||
"""
|
||||
return self.execute("Get-AntiPhishRule | ConvertTo-Json")
|
||||
|
||||
def get_organization_config(self) -> dict:
|
||||
"""
|
||||
Get Exchange Online Organization Configuration.
|
||||
|
||||
Retrieves the current Exchange Online organization configuration settings.
|
||||
|
||||
Returns:
|
||||
dict: Organization configuration settings in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_organization_config()
|
||||
{
|
||||
"Name": "MyOrganization",
|
||||
"Guid": "12345678-1234-1234-1234-123456789012"
|
||||
"AuditDisabled": false
|
||||
}
|
||||
"""
|
||||
return self.execute("Get-OrganizationConfig | ConvertTo-Json")
|
||||
|
||||
def get_mailbox_audit_config(self) -> dict:
|
||||
"""
|
||||
Get Exchange Online Mailbox Audit Configuration.
|
||||
|
||||
Retrieves the current mailbox audit configuration settings for Exchange Online.
|
||||
|
||||
Returns:
|
||||
dict: Mailbox audit configuration settings in JSON format.
|
||||
|
||||
Example:
|
||||
>>> get_mailbox_audit_config()
|
||||
{
|
||||
"Name": "MyMailbox",
|
||||
"Id": "12345678-1234-1234-1234-123456789012",
|
||||
"AuditBypassEnabled": false
|
||||
}
|
||||
"""
|
||||
return self.execute("Get-MailboxAuditBypassAssociation | ConvertTo-Json")
|
||||
@@ -8,17 +8,17 @@ MICROSOFT365_GENERIC_CLOUD = "https://graph.microsoft.com"
|
||||
|
||||
def get_regions_config(region):
|
||||
allowed_regions = {
|
||||
"Microsoft365Global": {
|
||||
"M365Global": {
|
||||
"authority": None,
|
||||
"base_url": MICROSOFT365_GENERIC_CLOUD,
|
||||
"credential_scopes": [MICROSOFT365_GENERIC_CLOUD + "/.default"],
|
||||
},
|
||||
"Microsoft365China": {
|
||||
"M365China": {
|
||||
"authority": AzureAuthorityHosts.AZURE_CHINA,
|
||||
"base_url": MICROSOFT365_CHINA_CLOUD,
|
||||
"credential_scopes": [MICROSOFT365_CHINA_CLOUD + "/.default"],
|
||||
},
|
||||
"Microsoft365USGovernment": {
|
||||
"M365USGovernment": {
|
||||
"authority": AzureAuthorityHosts.AZURE_GOVERNMENT,
|
||||
"base_url": MICROSOFT365_US_GOV_CLOUD,
|
||||
"credential_scopes": [MICROSOFT365_US_GOV_CLOUD + "/.default"],
|
||||
17
prowler/providers/m365/lib/service/service.py
Normal file
17
prowler/providers/m365/lib/service/service.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from msgraph import GraphServiceClient
|
||||
|
||||
from prowler.providers.m365.lib.powershell.m365_powershell import M365PowerShell
|
||||
from prowler.providers.m365.m365_provider import M365Provider
|
||||
|
||||
|
||||
class M365Service:
|
||||
def __init__(
|
||||
self,
|
||||
provider: M365Provider,
|
||||
):
|
||||
self.client = GraphServiceClient(credentials=provider.session)
|
||||
self.audit_config = provider.audit_config
|
||||
self.fixer_config = provider.fixer_config
|
||||
|
||||
if provider.credentials:
|
||||
self.powershell = M365PowerShell(provider.credentials)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ from prowler.config.config import output_file_timestamp
|
||||
from prowler.providers.common.models import ProviderOutputOptions
|
||||
|
||||
|
||||
class Microsoft365IdentityInfo(BaseModel):
|
||||
class M365IdentityInfo(BaseModel):
|
||||
identity_id: str = ""
|
||||
identity_type: str = ""
|
||||
tenant_id: str = ""
|
||||
@@ -12,14 +12,23 @@ class Microsoft365IdentityInfo(BaseModel):
|
||||
location: str = ""
|
||||
|
||||
|
||||
class Microsoft365RegionConfig(BaseModel):
|
||||
class M365RegionConfig(BaseModel):
|
||||
name: str = ""
|
||||
authority: str = None
|
||||
base_url: str = ""
|
||||
credential_scopes: list = []
|
||||
|
||||
|
||||
class Microsoft365OutputOptions(ProviderOutputOptions):
|
||||
class M365Credentials(BaseModel):
|
||||
user: str = ""
|
||||
passwd: str = ""
|
||||
client_id: str = ""
|
||||
client_secret: str = ""
|
||||
tenant_id: str = ""
|
||||
provider_id: str = ""
|
||||
|
||||
|
||||
class M365OutputOptions(ProviderOutputOptions):
|
||||
def __init__(self, arguments, bulk_checks_metadata, identity):
|
||||
# First call Provider_Output_Options init
|
||||
super().__init__(arguments, bulk_checks_metadata)
|
||||
@@ -1,6 +1,4 @@
|
||||
from prowler.providers.common.provider import Provider
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_service import (
|
||||
AdminCenter,
|
||||
)
|
||||
from prowler.providers.m365.services.admincenter.admincenter_service import AdminCenter
|
||||
|
||||
admincenter_client = AdminCenter(Provider.get_global_provider())
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Provider": "microsoft365",
|
||||
"Provider": "m365",
|
||||
"CheckID": "admincenter_groups_not_public_visibility",
|
||||
"CheckTitle": "Ensure that only organizationally managed/approved public groups exist",
|
||||
"CheckType": [],
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportMicrosoft365
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_client import (
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.admincenter.admincenter_client import (
|
||||
admincenter_client,
|
||||
)
|
||||
|
||||
@@ -16,18 +16,18 @@ class admincenter_groups_not_public_visibility(Check):
|
||||
metadata: Metadata associated with the check (inherited from Check).
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportMicrosoft365]:
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""Execute the check for groups with public visibility.
|
||||
|
||||
This method iterates through all groups in Microsoft Admin Center and checks
|
||||
if any group has 'Public' visibility. If so, the check fails for that group.
|
||||
|
||||
Returns:
|
||||
List[CheckReportMicrosoft365]: A list containing the results of the check for each group.
|
||||
List[CheckReportM365]: A list containing the results of the check for each group.
|
||||
"""
|
||||
findings = []
|
||||
for group in admincenter_client.groups.values():
|
||||
report = CheckReportMicrosoft365(
|
||||
report = CheckReportM365(
|
||||
metadata=self.metadata(),
|
||||
resource=group,
|
||||
resource_name=group.name,
|
||||
@@ -1,16 +1,15 @@
|
||||
from asyncio import gather, get_event_loop
|
||||
from typing import List, Optional
|
||||
|
||||
from msgraph.generated.models.o_data_errors.o_data_error import ODataError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.microsoft365.lib.service.service import Microsoft365Service
|
||||
from prowler.providers.microsoft365.microsoft365_provider import Microsoft365Provider
|
||||
from prowler.providers.m365.lib.service.service import M365Service
|
||||
from prowler.providers.m365.m365_provider import M365Provider
|
||||
|
||||
|
||||
class AdminCenter(Microsoft365Service):
|
||||
def __init__(self, provider: Microsoft365Provider):
|
||||
class AdminCenter(M365Service):
|
||||
def __init__(self, provider: M365Provider):
|
||||
super().__init__(provider)
|
||||
|
||||
loop = get_event_loop()
|
||||
@@ -31,7 +30,7 @@ class AdminCenter(Microsoft365Service):
|
||||
self.domains = attributes[2]
|
||||
|
||||
async def _get_users(self):
|
||||
logger.info("Microsoft365 - Getting users...")
|
||||
logger.info("M365 - Getting users...")
|
||||
users = {}
|
||||
try:
|
||||
users_list = await self.client.users.get()
|
||||
@@ -40,20 +39,6 @@ class AdminCenter(Microsoft365Service):
|
||||
license_details = await self.client.users.by_user_id(
|
||||
user.id
|
||||
).license_details.get()
|
||||
try:
|
||||
mailbox_settings = await self.client.users.by_user_id(
|
||||
user.id
|
||||
).mailbox_settings.get()
|
||||
mailbox_settings.user_purpose
|
||||
except ODataError as error:
|
||||
if error.error.code == "MailboxNotEnabledForRESTAPI":
|
||||
logger.warning(
|
||||
f"MailboxNotEnabledForRESTAPI for user {user.id}"
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
users.update(
|
||||
{
|
||||
user.id: User(
|
||||
@@ -75,7 +60,7 @@ class AdminCenter(Microsoft365Service):
|
||||
return users
|
||||
|
||||
async def _get_directory_roles(self):
|
||||
logger.info("Microsoft365 - Getting directory roles...")
|
||||
logger.info("M365 - Getting directory roles...")
|
||||
directory_roles_with_members = {}
|
||||
try:
|
||||
directory_roles_with_members.update({})
|
||||
@@ -110,7 +95,7 @@ class AdminCenter(Microsoft365Service):
|
||||
return directory_roles_with_members
|
||||
|
||||
async def _get_groups(self):
|
||||
logger.info("Microsoft365 - Getting groups...")
|
||||
logger.info("M365 - Getting groups...")
|
||||
groups = {}
|
||||
try:
|
||||
groups_list = await self.client.groups.get()
|
||||
@@ -133,7 +118,7 @@ class AdminCenter(Microsoft365Service):
|
||||
return groups
|
||||
|
||||
async def _get_domains(self):
|
||||
logger.info("Microsoft365 - Getting domains...")
|
||||
logger.info("M365 - Getting domains...")
|
||||
domains = {}
|
||||
try:
|
||||
domains_list = await self.client.domains.get()
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Provider": "microsoft365",
|
||||
"Provider": "m365",
|
||||
"CheckID": "admincenter_settings_password_never_expire",
|
||||
"CheckTitle": "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire (recommended)'",
|
||||
"CheckType": [],
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportMicrosoft365
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_client import (
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.admincenter.admincenter_client import (
|
||||
admincenter_client,
|
||||
)
|
||||
|
||||
@@ -17,19 +17,19 @@ class admincenter_settings_password_never_expire(Check):
|
||||
metadata: Metadata associated with the check (inherited from Check).
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportMicrosoft365]:
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""Execute the check for password never expires policy.
|
||||
|
||||
This method iterates over all domains and checks if the password validity period is set
|
||||
to `2147483647`, indicating that passwords for users in the domain never expire.
|
||||
|
||||
Returns:
|
||||
List[CheckReportMicrosoft365]: A list of reports indicating whether the domain's password
|
||||
List[CheckReportM365]: A list of reports indicating whether the domain's password
|
||||
policy is set to never expire.
|
||||
"""
|
||||
findings = []
|
||||
for domain in admincenter_client.domains.values():
|
||||
report = CheckReportMicrosoft365(
|
||||
report = CheckReportM365(
|
||||
self.metadata(),
|
||||
resource=domain,
|
||||
resource_name=domain.id,
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Provider": "microsoft365",
|
||||
"Provider": "m365",
|
||||
"CheckID": "admincenter_users_admins_reduced_license_footprint",
|
||||
"CheckTitle": "Ensure administrative accounts use licenses with a reduced application footprint",
|
||||
"CheckType": [],
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportMicrosoft365
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_client import (
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.admincenter.admincenter_client import (
|
||||
admincenter_client,
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ class admincenter_users_admins_reduced_license_footprint(Check):
|
||||
metadata: Metadata associated with the check (inherited from Check).
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportMicrosoft365]:
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""Execute the check for users with administrative roles and their licenses.
|
||||
|
||||
This method iterates over all users and checks if those with administrative roles
|
||||
@@ -25,7 +25,7 @@ class admincenter_users_admins_reduced_license_footprint(Check):
|
||||
the check passes; otherwise, it fails.
|
||||
|
||||
Returns:
|
||||
List[CheckReportMicrosoft365]: A list containing the result of the check for each user.
|
||||
List[CheckReportM365]: A list containing the result of the check for each user.
|
||||
"""
|
||||
findings = []
|
||||
allowed_licenses = ["AAD_PREMIUM", "AAD_PREMIUM_P2"]
|
||||
@@ -39,7 +39,7 @@ class admincenter_users_admins_reduced_license_footprint(Check):
|
||||
)
|
||||
|
||||
if admin_roles:
|
||||
report = CheckReportMicrosoft365(
|
||||
report = CheckReportM365(
|
||||
metadata=self.metadata(),
|
||||
resource=user,
|
||||
resource_name=user.name,
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Provider": "microsoft365",
|
||||
"Provider": "m365",
|
||||
"CheckID": "admincenter_users_between_two_and_four_global_admins",
|
||||
"CheckTitle": "Ensure that between two and four global admins are designated",
|
||||
"CheckType": [],
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportMicrosoft365
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_client import (
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.admincenter.admincenter_client import (
|
||||
admincenter_client,
|
||||
)
|
||||
|
||||
@@ -16,21 +16,21 @@ class admincenter_users_between_two_and_four_global_admins(Check):
|
||||
metadata: Metadata associated with the check (inherited from Check).
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportMicrosoft365]:
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""Execute the check for the number of Global Administrators.
|
||||
|
||||
This method checks if the number of users with the 'Global Administrator' role
|
||||
is between two and four. If the condition is met, the check passes; otherwise, it fails.
|
||||
|
||||
Returns:
|
||||
List[CheckReportMicrosoft365]: A list containing the result of the check for the Global Administrators.
|
||||
List[CheckReportM365]: A list containing the result of the check for the Global Administrators.
|
||||
"""
|
||||
findings = []
|
||||
directory_roles = admincenter_client.directory_roles
|
||||
global_admin_role = directory_roles.get("Global Administrator", {})
|
||||
|
||||
if global_admin_role:
|
||||
report = CheckReportMicrosoft365(
|
||||
report = CheckReportM365(
|
||||
metadata=self.metadata(),
|
||||
resource=global_admin_role,
|
||||
resource_name=global_admin_role.name,
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "m365",
|
||||
"CheckID": "defender_antiphishing_policy_configured",
|
||||
"CheckTitle": "Ensure anti-phishing policies are properly configured and active.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "defender",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "low",
|
||||
"ResourceType": "Defender Anti-Phishing Policy",
|
||||
"Description": "Ensure that anti-phishing policies are created and configured for specific users, groups, or domains, taking precedence over the default policy. This check verifies the existence of rules within policies and validates specific policy settings such as spoof intelligence, DMARC actions, safety tips, and unauthenticated sender actions.",
|
||||
"Risk": "Without anti-phishing policies, organizations may rely solely on default settings, which might not adequately protect against phishing attacks targeted at specific users, groups, or domains. This increases the risk of successful phishing attempts and potential data breaches.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/microsoft-365/security/office-365-security/set-up-anti-phishing-policies?view=o365-worldwide",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "$params = @{Name='<policy_name>';PhishThresholdLevel=3;EnableTargetedUserProtection=$true;EnableOrganizationDomainsProtection=$true;EnableMailboxIntelligence=$true;EnableMailboxIntelligenceProtection=$true;EnableSpoofIntelligence=$true;TargetedUserProtectionAction='Quarantine';TargetedDomainProtectionAction='Quarantine';MailboxIntelligenceProtectionAction='Quarantine';TargetedUserQuarantineTag='DefaultFullAccessWithNotificationPolicy';MailboxIntelligenceQuarantineTag='DefaultFullAccessWithNotificationPolicy';TargetedDomainQuarantineTag='DefaultFullAccessWithNotificationPolicy';EnableFirstContactSafetyTips=$true;EnableSimilarUsersSafetyTips=$true;EnableSimilarDomainsSafetyTips=$true;EnableUnusualCharactersSafetyTips=$true;HonorDmarcPolicy=$true}; New-AntiPhishPolicy @params; New-AntiPhishRule -Name $params.Name -AntiPhishPolicy $params.Name -RecipientDomainIs (Get-AcceptedDomain).Name -Priority 0",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to Microsoft 365 Defender https://security.microsoft.com. 2. Click to expand Email & collaboration and select Policies & rules. 3. On the Policies & rules page select Threat policies. 4. Under Policies, select Anti-phishing 5. Ensure policies have rules with the state set to 'on' and validate settings: spoof intelligence enabled, spoof intelligence action set to 'Quarantine', DMARC reject and quarantine actions, safety tips enabled, unauthenticated sender action enabled, show tag enabled, and honor DMARC policy enabled. If not, modify them to be as recommended.",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Create and configure anti-phishing policies for specific users, groups, or domains to enhance protection against phishing attacks.",
|
||||
"Url": "https://learn.microsoft.com/en-us/microsoft-365/security/office-365-security/set-up-anti-phishing-policies?view=o365-worldwide"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user