@auth0/auth0-checkmate 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/CODEOWNERS +1 -0
- package/.github/workflows/npm-release.yml +77 -0
- package/.github/workflows/sca_scan.yml +10 -0
- package/.github/workflows/test.yml +48 -0
- package/AUTHORS +5 -0
- package/LICENSE +203 -0
- package/README.md +166 -0
- package/THIRD-PARTY-NOTICES +226 -0
- package/analyzer/lib/actions/checkActionsHardCodedValues.js +151 -0
- package/analyzer/lib/actions/checkActionsRuntime.js +105 -0
- package/analyzer/lib/actions/checkDependencies.js +111 -0
- package/analyzer/lib/attack_protection/checkBotDetectionSetting.js +76 -0
- package/analyzer/lib/attack_protection/checkBreachedPassword.js +140 -0
- package/analyzer/lib/attack_protection/checkBruteForce.js +89 -0
- package/analyzer/lib/attack_protection/checkSuspiciousIPThrottling.js +89 -0
- package/analyzer/lib/canonical_domain/checkCanonicalDomain.js +63 -0
- package/analyzer/lib/clients/checkAllowedCallbacks.js +122 -0
- package/analyzer/lib/clients/checkAllowedLogoutUrl.js +124 -0
- package/analyzer/lib/clients/checkApplicationLoginUri.js +125 -0
- package/analyzer/lib/clients/checkCrossOriginAuthentication.js +91 -0
- package/analyzer/lib/clients/checkGrantTypes.js +138 -0
- package/analyzer/lib/clients/checkJWTSignAlg.js +118 -0
- package/analyzer/lib/clients/checkRefreshToken.js +108 -0
- package/analyzer/lib/clients/checkWebOrigins.js +55 -0
- package/analyzer/lib/constants.js +63 -0
- package/analyzer/lib/custom_domain/checkCustomDomain.js +53 -0
- package/analyzer/lib/databases/checkAuthenticationMethods.js +98 -0
- package/analyzer/lib/databases/checkDASHardCodedValues.js +163 -0
- package/analyzer/lib/databases/checkEmailAttributeVerification.js +114 -0
- package/analyzer/lib/databases/checkEnabledDatabaseCustomization.js +83 -0
- package/analyzer/lib/databases/checkPasswordComplexity.js +100 -0
- package/analyzer/lib/databases/checkPasswordHistory.js +92 -0
- package/analyzer/lib/databases/checkPasswordNoPersonalInfo.js +91 -0
- package/analyzer/lib/databases/checkPasswordPolicy.js +95 -0
- package/analyzer/lib/databases/checkPromotedDBConnection.js +96 -0
- package/analyzer/lib/email_provider/checkEmailProvider.js +37 -0
- package/analyzer/lib/email_templates/checkEmailTemplates.js +71 -0
- package/analyzer/lib/error_page_template/checkErrorPageTemplate.js +153 -0
- package/analyzer/lib/event_streams/checkEventStreams.js +71 -0
- package/analyzer/lib/executeCheck.js +12 -0
- package/analyzer/lib/hooks/checkHooks.js +43 -0
- package/analyzer/lib/listOfAnalyser.js +24 -0
- package/analyzer/lib/log_streams/checkLogStream.js +60 -0
- package/analyzer/lib/logger.js +16 -0
- package/analyzer/lib/multifactor/checkGuardianFactors.js +72 -0
- package/analyzer/lib/multifactor/checkGuardianPolicy.js +40 -0
- package/analyzer/lib/network_acl/checkNetworkACL.js +35 -0
- package/analyzer/lib/rules/checkRules.js +102 -0
- package/analyzer/lib/tenant_settings/checkDefaultAudience.js +53 -0
- package/analyzer/lib/tenant_settings/checkDefaultDirectory.js +48 -0
- package/analyzer/lib/tenant_settings/checkEnabledDynamicClientRegistration.js +60 -0
- package/analyzer/lib/tenant_settings/checkSandboxVersion.js +37 -0
- package/analyzer/lib/tenant_settings/checkSessionLifetime.js +95 -0
- package/analyzer/lib/tenant_settings/checkSupportEmail.js +61 -0
- package/analyzer/lib/tenant_settings/checkSupportUrl.js +61 -0
- package/analyzer/lib/tenant_settings/checkTenantLoginUrl.js +71 -0
- package/analyzer/lib/tenant_settings/checkTenantLogoutUrl.js +60 -0
- package/analyzer/report.js +404 -0
- package/analyzer/tools/auth0.js +443 -0
- package/analyzer/tools/helpers.js +71 -0
- package/analyzer/tools/summary.js +84 -0
- package/analyzer/tools/utils.js +72 -0
- package/bin/index.js +393 -0
- package/eslint.config.mjs +16 -0
- package/images/auth0.png +0 -0
- package/images/okta.png +0 -0
- package/locales/en.json +1417 -0
- package/package.json +66 -0
- package/tests/actions/checkActionsHardCodedValues.test.js +106 -0
- package/tests/actions/checkActionsRuntime.test.js +102 -0
- package/tests/actions/checkDependencies.test.js +131 -0
- package/tests/attack_protection/checkBreachedPassword.test.js +253 -0
- package/tests/attack_protection/checkBruteForce.test.js +181 -0
- package/tests/attack_protection/checkSuspiciousIPThrottling.test.js +222 -0
- package/tests/canonical_domain/checkCanonicalDomain.test.js +94 -0
- package/tests/clients/checkAllowedCallbacks.test.js +149 -0
- package/tests/clients/checkAllowedLogoutUrl.test.js +149 -0
- package/tests/clients/checkApplicationLoginUri.test.js +180 -0
- package/tests/clients/checkCrossOriginAuthentication.test.js +99 -0
- package/tests/clients/checkGrantTypes.test.js +154 -0
- package/tests/clients/checkJWTSignAlg.test.js +121 -0
- package/tests/clients/checkRefreshToken.test.js +63 -0
- package/tests/clients/checkWebOrigins.test.js +140 -0
- package/tests/custom_domain/checkCustomDomain.test.js +73 -0
- package/tests/databases/checkAuthenticationMethods.test.js +124 -0
- package/tests/databases/checkDASHardCodedValues.test.js +77 -0
- package/tests/databases/checkEmailAttributeVerification.test.js +79 -0
- package/tests/databases/checkEnabledDatabaseCustomization.test.js +68 -0
- package/tests/databases/checkPasswordComplexity.test.js +127 -0
- package/tests/databases/checkPasswordHistory.test.js +100 -0
- package/tests/databases/checkPasswordNoPersonalInfo.test.js +94 -0
- package/tests/databases/checkPasswordPolicy.test.js +161 -0
- package/tests/databases/checkPromotedDBConnection.test.js +62 -0
- package/tests/email_provider/checkEmailProvider.test.js +58 -0
- package/tests/email_templates/checkEmailTemplates.test.js +120 -0
- package/tests/error_page_template/checkErrorPageTemplate.test.js +315 -0
- package/tests/event_streams/checkEventStreams.test.js +118 -0
- package/tests/hooks/checkHooks.test.js +112 -0
- package/tests/log_streams/checkLogStream.test.js +140 -0
- package/tests/multifactor/checkGuardianFactors.test.js +94 -0
- package/tests/multifactor/checkGuardianPolicy.test.js +49 -0
- package/tests/rules/checkRules.test.js +102 -0
- package/tests/tenant_settings/checkDefaultAudience.test.js +62 -0
- package/tests/tenant_settings/checkDefaultDirectory.test.js +62 -0
- package/tests/tenant_settings/checkEnabledDynamicClientRegistration.test.js +97 -0
- package/tests/tenant_settings/checkSandboxVersion.test.js +50 -0
- package/tests/tenant_settings/checkSessionLifetime.test.js +108 -0
- package/tests/tenant_settings/checkSupportEmail.test.js +77 -0
- package/tests/tenant_settings/checkSupportUrl.test.js +77 -0
- package/tests/tenant_settings/checkTenantLoginUri.test.js +82 -0
- package/tests/tenant_settings/checkTenantLogoutUrl.test.js +108 -0
- package/tests/tools/auth0.test.js +833 -0
- package/tests/tools/helpers.test.js +692 -0
- package/views/pdf_cli_report.handlebars +571 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const CONSTANTS = require("../lib/constants");
|
|
3
|
+
const logger = require("../lib/logger");
|
|
4
|
+
const {
|
|
5
|
+
version: packageVersion,
|
|
6
|
+
name: packageName,
|
|
7
|
+
} = require("../../package.json");
|
|
8
|
+
const PER_PAGE = 100;
|
|
9
|
+
//axios default config
|
|
10
|
+
axios.defaults.headers.common["User-Agent"] =
|
|
11
|
+
`${packageName}/${packageVersion}`;
|
|
12
|
+
|
|
13
|
+
async function getAccessToken(domain, client_id, client_secret, access_token) {
|
|
14
|
+
if (access_token) {
|
|
15
|
+
return access_token;
|
|
16
|
+
}
|
|
17
|
+
logger.log("info", `Getting an access token for client_id ${client_id}`);
|
|
18
|
+
logger.log("info", `Requesting scopes ${CONSTANTS.REQUIRED_SCOPES}`);
|
|
19
|
+
const tokenUrl = `https://${domain}/oauth/token`;
|
|
20
|
+
const headers = {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
};
|
|
23
|
+
const body = {
|
|
24
|
+
grant_type: "client_credentials",
|
|
25
|
+
client_id: client_id,
|
|
26
|
+
client_secret: client_secret,
|
|
27
|
+
audience: `https://${domain}/api/v2/`,
|
|
28
|
+
scopes: CONSTANTS.REQUIRED_SCOPES,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const response = await axios.post(tokenUrl, body, { headers });
|
|
33
|
+
return response.data.access_token;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error("Error getting access token: %s", error.message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function getCustomDomains(domain, accessToken) {
|
|
41
|
+
const url = `https://${domain}/api/v2/custom-domains`;
|
|
42
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
43
|
+
logger.log("info", `Getting custom domains`);
|
|
44
|
+
try {
|
|
45
|
+
const response = await axios.get(url, { headers });
|
|
46
|
+
return response.data;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.log("error", `Failed to get custom domains ${error.message}`);
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function fetchClients(url, accessToken, page) {
|
|
54
|
+
try {
|
|
55
|
+
const response = await axios.get(url, {
|
|
56
|
+
params: {
|
|
57
|
+
per_page: PER_PAGE,
|
|
58
|
+
page: page,
|
|
59
|
+
is_global: false,
|
|
60
|
+
//include_fields: false,
|
|
61
|
+
//fields: `client_secret,signing_keys,encryption_key,client_authentication_methods`
|
|
62
|
+
},
|
|
63
|
+
headers: {
|
|
64
|
+
Authorization: `Bearer ${accessToken}`,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return response.data; // Array of clients
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("Error fetching clients:", error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function getActions(domain, accessToken) {
|
|
75
|
+
const url = `https://${domain}/api/v2/actions/actions?installed=false`;
|
|
76
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
77
|
+
logger.log("info", `Getting Actions`);
|
|
78
|
+
try {
|
|
79
|
+
const response = await axios.get(url, { headers });
|
|
80
|
+
return response.data;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
logger.log("error", `Failed to get actions ${error.message}`);
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function getApplications(domain, accessToken) {
|
|
88
|
+
const url = `https://${domain}/api/v2/clients`;
|
|
89
|
+
let allClients = [],
|
|
90
|
+
page = 0;
|
|
91
|
+
logger.log("info", `Getting Applications`);
|
|
92
|
+
try {
|
|
93
|
+
let hasMore = true;
|
|
94
|
+
|
|
95
|
+
while (hasMore) {
|
|
96
|
+
const clients = await fetchClients(url, accessToken, page);
|
|
97
|
+
|
|
98
|
+
if (clients.length < PER_PAGE) {
|
|
99
|
+
hasMore = false; // No more data to fetch
|
|
100
|
+
} else {
|
|
101
|
+
page++; // Move to the next page
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
allClients = allClients.concat(clients); // Add the current page's clients to the list
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return allClients;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
logger.log("error", `Failed to get clients ${error.message}`);
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function getConnections(domain, accessToken) {
|
|
115
|
+
const url = `https://${domain}/api/v2/connections?strategy=auth0`;
|
|
116
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
117
|
+
logger.log("info", `Getting database connections`);
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const response = await axios.get(url, { headers });
|
|
121
|
+
return response.data;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
logger.log("error", `Failed to get database connections ${error.message}`);
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function getEmailProvider(domain, accessToken) {
|
|
129
|
+
const url = `https://${domain}/api/v2/emails/provider`;
|
|
130
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
131
|
+
logger.log("info", `Getting email provider`);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const response = await axios.get(url, { headers });
|
|
135
|
+
return response.data;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
logger.log("error", `Failed to get email provider ${error.message}`);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function getLogStreams(domain, accessToken) {
|
|
143
|
+
const url = `https://${domain}/api/v2/log-streams`;
|
|
144
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
145
|
+
logger.log("info", `Getting log streams`);
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const response = await axios.get(url, { headers });
|
|
149
|
+
return response.data;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
logger.log("error", `Failed to get log streams ${error.message}`);
|
|
152
|
+
return [error.response.data];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function getBruteForceProtectionSetting(domain, accessToken) {
|
|
157
|
+
const url = `https://${domain}/api/v2/attack-protection/brute-force-protection`;
|
|
158
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
159
|
+
logger.log("info", `Getting brute force setting`);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const response = await axios.get(url, { headers });
|
|
163
|
+
return response.data;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logger.log("error", `Failed to get brute force settings ${error.message}`);
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function getSuspiciousIpSetting(domain, accessToken) {
|
|
170
|
+
const url = `https://${domain}/api/v2/attack-protection/suspicious-ip-throttling`;
|
|
171
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
172
|
+
logger.log("info", `Getting suspicious ip throttling`);
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const response = await axios.get(url, { headers });
|
|
176
|
+
return response.data;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
logger.log(
|
|
179
|
+
"error",
|
|
180
|
+
`Failed to get suspicious IP settings ${error.message}`,
|
|
181
|
+
);
|
|
182
|
+
return {};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function getBreachedPasswordSetting(domain, accessToken) {
|
|
186
|
+
const url = `https://${domain}/api/v2/attack-protection/breached-password-detection`;
|
|
187
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
188
|
+
logger.log("info", `Getting breached password setting`);
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const response = await axios.get(url, { headers });
|
|
192
|
+
return response.data;
|
|
193
|
+
} catch (error) {
|
|
194
|
+
logger.log(
|
|
195
|
+
"error",
|
|
196
|
+
`Failed to get breached password settings ${error.message}`,
|
|
197
|
+
);
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function getBotDetectionSetting(domain, accessToken) {
|
|
203
|
+
const url = `https://${domain}/api/v2/anomaly/captchas`;
|
|
204
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
205
|
+
logger.log("info", `Getting bot detection setting`);
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const response = await axios.get(url, { headers });
|
|
209
|
+
return response.data;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
logger.log(
|
|
212
|
+
"error",
|
|
213
|
+
`Failed to get bot detection settings ${error.message}`,
|
|
214
|
+
);
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function getAttackProtection(domain, accessToken) {
|
|
219
|
+
try {
|
|
220
|
+
var attackProtection = {};
|
|
221
|
+
const [
|
|
222
|
+
breachedPasswordDetection,
|
|
223
|
+
bruteForceProtection,
|
|
224
|
+
suspiciousIpThrottling,
|
|
225
|
+
botDetection,
|
|
226
|
+
] = await Promise.all([
|
|
227
|
+
getBreachedPasswordSetting(domain, accessToken),
|
|
228
|
+
getBruteForceProtectionSetting(domain, accessToken),
|
|
229
|
+
getSuspiciousIpSetting(domain, accessToken),
|
|
230
|
+
getBotDetectionSetting(domain, accessToken),
|
|
231
|
+
]);
|
|
232
|
+
attackProtection = {
|
|
233
|
+
breachedPasswordDetection,
|
|
234
|
+
bruteForceProtection,
|
|
235
|
+
suspiciousIpThrottling,
|
|
236
|
+
botDetection,
|
|
237
|
+
};
|
|
238
|
+
return attackProtection;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
logger.log(
|
|
241
|
+
"error",
|
|
242
|
+
`Failed to get attack protection settings ${error.message}`,
|
|
243
|
+
);
|
|
244
|
+
return {};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function getEmailTemplate(domain, accessToken, templateName) {
|
|
249
|
+
try {
|
|
250
|
+
const response = await axios.get(
|
|
251
|
+
`https://${domain}/api/v2/email-templates/${templateName}`,
|
|
252
|
+
{
|
|
253
|
+
headers: {
|
|
254
|
+
Authorization: `Bearer ${accessToken}`,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
return response.data;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
logger.log(
|
|
261
|
+
"error",
|
|
262
|
+
`Failed to get email template because it is not configured ${templateName} - ${error.message}`,
|
|
263
|
+
);
|
|
264
|
+
return null; // Return null for templates that may not exist
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function getErrorPageTemplate(domain, accessToken) {
|
|
269
|
+
try {
|
|
270
|
+
const response = await axios.get(
|
|
271
|
+
`https://${domain}/api/v2/tenants/settings`,
|
|
272
|
+
{
|
|
273
|
+
headers: {
|
|
274
|
+
Authorization: `Bearer ${accessToken}`,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
const payload = response.data.error_page.html;
|
|
279
|
+
return payload;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
logger.log(
|
|
282
|
+
"error",
|
|
283
|
+
`Failed to get error page template because it is not configured - ${error.message}`,
|
|
284
|
+
);
|
|
285
|
+
return null; // Return null for templates that may not exist
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function getEmailTemplates(domain, accessToken) {
|
|
290
|
+
logger.log("info", `Getting email templates`);
|
|
291
|
+
const emailTemplates = await Promise.all(
|
|
292
|
+
CONSTANTS.EMAIL_TEMPLATES_TYPES.map(async (templateName) => {
|
|
293
|
+
const template = await getEmailTemplate(
|
|
294
|
+
domain,
|
|
295
|
+
accessToken,
|
|
296
|
+
templateName,
|
|
297
|
+
);
|
|
298
|
+
return { name: CONSTANTS.EMAIL_TEMPLATES_NAMES[templateName], template };
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
//const nonEmptyTemplates = emailTemplates.filter((template) => !!template);
|
|
302
|
+
return emailTemplates;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function getTenantSettings(domain, accessToken) {
|
|
306
|
+
const url = `https://${domain}/api/v2/tenants/settings`;
|
|
307
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
308
|
+
logger.log("info", `Getting tenant setting`);
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const response = await axios.get(url, { headers });
|
|
312
|
+
return response.data;
|
|
313
|
+
} catch (error) {
|
|
314
|
+
logger.log("error", `Failed to get tenant settings ${error.message}`);
|
|
315
|
+
return {};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function getGuardianFactors(domain, accessToken) {
|
|
320
|
+
const url = `https://${domain}/api/v2/guardian/factors`;
|
|
321
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
322
|
+
logger.log("info", `Getting MFA factors`);
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
const response = await axios.get(url, { headers });
|
|
326
|
+
return response.data;
|
|
327
|
+
} catch (error) {
|
|
328
|
+
logger.log("error", `Failed to get MFA factors ${error.message}`);
|
|
329
|
+
return {};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// policies
|
|
333
|
+
async function getGuardianPolicies(domain, accessToken) {
|
|
334
|
+
const url = `https://${domain}/api/v2/guardian/policies`;
|
|
335
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
336
|
+
logger.log("info", `Getting MFA policy`);
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const response = await axios.get(url, { headers });
|
|
340
|
+
return response.data;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
logger.log("error", `Failed to get MFA policies ${error.message}`);
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// legacy rules and hooks
|
|
347
|
+
async function getRules(domain, accessToken) {
|
|
348
|
+
const url = `https://${domain}/api/v2/rules?enabled=true`;
|
|
349
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
350
|
+
logger.log("warn", `Getting rules`);
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const response = await axios.get(url, { headers });
|
|
354
|
+
return response.data;
|
|
355
|
+
} catch (error) {
|
|
356
|
+
logger.log("error", `Failed to get rules ${error.message}`);
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function getHooks(domain, accessToken) {
|
|
362
|
+
const url = `https://${domain}/api/v2/hooks?enabled=true`;
|
|
363
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
364
|
+
logger.log("warn", `Getting hooks`);
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
const response = await axios.get(url, { headers });
|
|
368
|
+
return response.data;
|
|
369
|
+
} catch (error) {
|
|
370
|
+
logger.log("error", `Failed to get hooks ${error.message}`);
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function getLogs(domain, accessToken) {
|
|
376
|
+
const logTypes = CONSTANTS.LOG_TYPES;
|
|
377
|
+
const fields = "type,hostname";
|
|
378
|
+
const per_page = 1;
|
|
379
|
+
const query = `type: (${logTypes.join(" ")}) AND hostname: ${domain}`;
|
|
380
|
+
const url = `https://${domain}/api/v2/logs?per_page=${per_page}&fields=${fields}&q=${query}`;
|
|
381
|
+
const encodedUrl = encodeURI(url);
|
|
382
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
383
|
+
logger.log("info", `Getting logs`);
|
|
384
|
+
try {
|
|
385
|
+
const response = await axios.get(encodedUrl, { headers });
|
|
386
|
+
return { log_query: query, logs: response.data };
|
|
387
|
+
} catch (error) {
|
|
388
|
+
logger.log("error", `Failed to get logs ${error.message}`);
|
|
389
|
+
return { log_query: query, logs: [] };
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Early Access
|
|
393
|
+
async function getNetworkACL(domain, accessToken) {
|
|
394
|
+
const url = `https://${domain}/api/v2/network-acls`;
|
|
395
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
396
|
+
logger.log("info", `Getting Network ACL`);
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
const response = await axios.get(url, { headers });
|
|
400
|
+
return response.data;
|
|
401
|
+
} catch (error) {
|
|
402
|
+
logger.log("error", `Failed to get network ACL ${error.response.data.message}`);
|
|
403
|
+
return [error.response.data];
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
//Early Access
|
|
407
|
+
async function getEventStreams(domain, accessToken) {
|
|
408
|
+
const url = `https://${domain}/api/v2/event-streams`;
|
|
409
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
410
|
+
logger.log("info", `Getting event streams`);
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
const response = await axios.get(url, { headers });
|
|
414
|
+
return response.data.eventStreams;
|
|
415
|
+
} catch (error) {
|
|
416
|
+
logger.log("error", `Failed to get event streams ${error.message}`);
|
|
417
|
+
return [error.response.data];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
module.exports = {
|
|
421
|
+
getAccessToken,
|
|
422
|
+
getCustomDomains,
|
|
423
|
+
getApplications,
|
|
424
|
+
getConnections,
|
|
425
|
+
getEmailProvider,
|
|
426
|
+
getEmailTemplates,
|
|
427
|
+
getErrorPageTemplate,
|
|
428
|
+
getBruteForceProtectionSetting,
|
|
429
|
+
getSuspiciousIpSetting,
|
|
430
|
+
getBreachedPasswordSetting,
|
|
431
|
+
getLogStreams,
|
|
432
|
+
getAttackProtection,
|
|
433
|
+
getTenantSettings,
|
|
434
|
+
getGuardianFactors,
|
|
435
|
+
getGuardianPolicies,
|
|
436
|
+
getBotDetectionSetting,
|
|
437
|
+
getRules,
|
|
438
|
+
getHooks,
|
|
439
|
+
getActions,
|
|
440
|
+
getLogs,
|
|
441
|
+
getNetworkACL,
|
|
442
|
+
getEventStreams,
|
|
443
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const semver = require("semver");
|
|
3
|
+
const logger = require("../lib/logger");
|
|
4
|
+
async function checkVulnerableVersion(currentVersion, advisoryData) {
|
|
5
|
+
const vulnerabilitiesAdvisory = [];
|
|
6
|
+
advisoryData.forEach((advisory) => {
|
|
7
|
+
advisory.vulnerabilities.forEach((vulnerability) => {
|
|
8
|
+
const vulnerableVersionRange = vulnerability.vulnerable_version_range;
|
|
9
|
+
const isVulnerable = semver.satisfies(
|
|
10
|
+
currentVersion,
|
|
11
|
+
vulnerableVersionRange,
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
if (isVulnerable) {
|
|
15
|
+
vulnerabilitiesAdvisory.push({
|
|
16
|
+
description: `Vulnerable to ${advisory.cve_id} (range: ${vulnerableVersionRange})`,
|
|
17
|
+
advisory_url: advisory.html_url,
|
|
18
|
+
advisory_summary: advisory.summary,
|
|
19
|
+
severity: advisory.severity,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
return vulnerabilitiesAdvisory;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function checkGitHubAdvisories(packageName, version) {
|
|
28
|
+
try {
|
|
29
|
+
const headers = {
|
|
30
|
+
Accept: "application/vnd.github.v3+json",
|
|
31
|
+
};
|
|
32
|
+
const url = `https://api.github.com/advisories?affects=${packageName}@${version}`;
|
|
33
|
+
const response = await axios.get(url, { headers });
|
|
34
|
+
const vulnFindings = await checkVulnerableVersion(version, response.data);
|
|
35
|
+
return vulnFindings;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
logger.log(
|
|
38
|
+
"error",
|
|
39
|
+
`Failed to get github advisory skipping - ${error.message}`,
|
|
40
|
+
);
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function getActionDependencies(actionsList) {
|
|
46
|
+
var vulnDependencyList = [];
|
|
47
|
+
for (let i = 0; i < actionsList.length; i++) {
|
|
48
|
+
for (const dependency of actionsList[i].dependencies) {
|
|
49
|
+
var vulnFindings = await checkGitHubAdvisories(
|
|
50
|
+
dependency.name,
|
|
51
|
+
dependency.version,
|
|
52
|
+
);
|
|
53
|
+
if (vulnFindings.length > 0) {
|
|
54
|
+
vulnDependencyList.push({
|
|
55
|
+
name: dependency.name,
|
|
56
|
+
actionName: actionsList[i].name,
|
|
57
|
+
version: dependency.version,
|
|
58
|
+
vulnFindings: vulnFindings,
|
|
59
|
+
trigger: actionsList[i].supported_triggers[0].id,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return vulnDependencyList;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
checkVulnerableVersion,
|
|
69
|
+
checkGitHubAdvisories,
|
|
70
|
+
getActionDependencies,
|
|
71
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const _ = require("lodash");
|
|
2
|
+
module.exports.getSummaryReport = async (data) => {
|
|
3
|
+
const filteredData = _.map(data, (item) => {
|
|
4
|
+
const filteredDetails = _.flatMap(item.details, (detail) => {
|
|
5
|
+
if (_.isArray(detail.report)) {
|
|
6
|
+
// Process if the detail has a 'report' array
|
|
7
|
+
return _.filter(detail.report, (report) =>
|
|
8
|
+
["red", "yellow"].includes(report.status),
|
|
9
|
+
).map((report) =>
|
|
10
|
+
_.pick(report, [
|
|
11
|
+
"name",
|
|
12
|
+
"status",
|
|
13
|
+
"field",
|
|
14
|
+
"value",
|
|
15
|
+
"message",
|
|
16
|
+
"vulnFindings",
|
|
17
|
+
"pre_requisites",
|
|
18
|
+
]),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
// Process other fields if no 'report' array exists
|
|
22
|
+
return _.filter([detail], (detailItem) =>
|
|
23
|
+
["red", "yellow"].includes(detailItem.status),
|
|
24
|
+
).map((detailItem) =>
|
|
25
|
+
_.pick(detailItem, [
|
|
26
|
+
"name",
|
|
27
|
+
"title",
|
|
28
|
+
"description",
|
|
29
|
+
"docsPath",
|
|
30
|
+
"field",
|
|
31
|
+
"value",
|
|
32
|
+
"status",
|
|
33
|
+
"message",
|
|
34
|
+
"vulnFindings",
|
|
35
|
+
"pre_requisites",
|
|
36
|
+
]),
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
// Only return items with 'red' or 'yellow' status after filtering
|
|
40
|
+
const detailsLength = _.uniqBy(filteredDetails, "name").length;
|
|
41
|
+
return {
|
|
42
|
+
name: item.name,
|
|
43
|
+
title: item.title,
|
|
44
|
+
description: item.description,
|
|
45
|
+
status: item.status,
|
|
46
|
+
disclaimer: item.disclaimer || null,
|
|
47
|
+
advisory: item.advisory || null,
|
|
48
|
+
pre_requisites: item.pre_requisites || null,
|
|
49
|
+
vulnFindings: item.vulnFindings || null,
|
|
50
|
+
severity: item.severity,
|
|
51
|
+
severity_message: item.severity_message,
|
|
52
|
+
docsPath: item.docsPath,
|
|
53
|
+
details: filteredDetails,
|
|
54
|
+
detailsLength: detailsLength > 0 ? detailsLength : filteredDetails.length,
|
|
55
|
+
};
|
|
56
|
+
}).filter((item) => item.details.length > 0); // Ensure that at least one detail exists
|
|
57
|
+
|
|
58
|
+
const transformedFindings = filteredData.map((finding) => ({
|
|
59
|
+
name: finding.name,
|
|
60
|
+
title: finding.title, // Set title from name
|
|
61
|
+
description: finding.description, // Set description
|
|
62
|
+
status: finding.status, // Set status
|
|
63
|
+
disclaimer: finding.disclaimer || null,
|
|
64
|
+
advisory: finding.advisory || null,
|
|
65
|
+
vulnFindings: finding.vulnFindings || [],
|
|
66
|
+
pre_requisites: finding.pre_requisites || null,
|
|
67
|
+
severity: finding.severity,
|
|
68
|
+
severity_message: finding.severity_message.replace(
|
|
69
|
+
"%s",
|
|
70
|
+
finding.details.length,
|
|
71
|
+
),
|
|
72
|
+
docsPath: finding.docsPath,
|
|
73
|
+
details: finding.details,
|
|
74
|
+
detailsLength: finding.detailsLength, // Get the length of the details array
|
|
75
|
+
}));
|
|
76
|
+
const severityOrder = ["red", "yellow", "green", "blue", "violet"];
|
|
77
|
+
|
|
78
|
+
// Sort the array using the custom order
|
|
79
|
+
const sortedFindings = _.sortBy(transformedFindings, (item) =>
|
|
80
|
+
severityOrder.indexOf(item.status),
|
|
81
|
+
);
|
|
82
|
+
//console.log(JSON.stringify(sortedFindings, null, 2))
|
|
83
|
+
return sortedFindings;
|
|
84
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const moment = require("moment");
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
function getFormattedDateTime() {
|
|
4
|
+
return moment().format("YYYY-MM-DD_HH:mm:ss").replace(/:/g, "_");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async function getToday(locale) {
|
|
8
|
+
const date = new Date();
|
|
9
|
+
const formattedDate = date.toLocaleDateString(locale, {
|
|
10
|
+
month: "long", // Full month name (e.g., 'January')
|
|
11
|
+
day: "numeric", // Day of the month (e.g., '20')
|
|
12
|
+
year: "numeric", // Full year (e.g., '2025')
|
|
13
|
+
});
|
|
14
|
+
return formattedDate;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function convertToTitleCase(str) {
|
|
18
|
+
// Insert space before each uppercase letter and capitalize the first letter
|
|
19
|
+
return str
|
|
20
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2') // Add space between lowercase and uppercase letters
|
|
21
|
+
.replace(/^./, (match) => match.toUpperCase()); // Capitalize the first letter
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function tranformReport(grouped) {
|
|
25
|
+
const report = _.flatMap(grouped, (values, name) => {
|
|
26
|
+
const firstPartyValues = [];
|
|
27
|
+
const thirdPartyValues = [];
|
|
28
|
+
values.forEach((detail) => {
|
|
29
|
+
const firstReports = detail.report.filter(r => r.is_first_party === true);
|
|
30
|
+
const thirdReports = detail.report.filter(r => r.is_first_party === false);
|
|
31
|
+
|
|
32
|
+
if (firstReports.length) {
|
|
33
|
+
firstPartyValues.push({
|
|
34
|
+
...detail,
|
|
35
|
+
report: firstReports
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (thirdReports.length) {
|
|
40
|
+
thirdPartyValues.push({
|
|
41
|
+
...detail,
|
|
42
|
+
report: thirdReports
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const result = [];
|
|
48
|
+
|
|
49
|
+
if (firstPartyValues.length) {
|
|
50
|
+
result.push({
|
|
51
|
+
name: `${name} (First-Party Application)`,
|
|
52
|
+
values: firstPartyValues
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (thirdPartyValues.length) {
|
|
57
|
+
result.push({
|
|
58
|
+
name: `${name} (Third-Party Application)`,
|
|
59
|
+
values: thirdPartyValues
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
});
|
|
65
|
+
return report;
|
|
66
|
+
}
|
|
67
|
+
module.exports = {
|
|
68
|
+
getFormattedDateTime,
|
|
69
|
+
getToday,
|
|
70
|
+
convertToTitleCase,
|
|
71
|
+
tranformReport
|
|
72
|
+
};
|