@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.
Files changed (114) hide show
  1. package/.github/CODEOWNERS +1 -0
  2. package/.github/workflows/npm-release.yml +77 -0
  3. package/.github/workflows/sca_scan.yml +10 -0
  4. package/.github/workflows/test.yml +48 -0
  5. package/AUTHORS +5 -0
  6. package/LICENSE +203 -0
  7. package/README.md +166 -0
  8. package/THIRD-PARTY-NOTICES +226 -0
  9. package/analyzer/lib/actions/checkActionsHardCodedValues.js +151 -0
  10. package/analyzer/lib/actions/checkActionsRuntime.js +105 -0
  11. package/analyzer/lib/actions/checkDependencies.js +111 -0
  12. package/analyzer/lib/attack_protection/checkBotDetectionSetting.js +76 -0
  13. package/analyzer/lib/attack_protection/checkBreachedPassword.js +140 -0
  14. package/analyzer/lib/attack_protection/checkBruteForce.js +89 -0
  15. package/analyzer/lib/attack_protection/checkSuspiciousIPThrottling.js +89 -0
  16. package/analyzer/lib/canonical_domain/checkCanonicalDomain.js +63 -0
  17. package/analyzer/lib/clients/checkAllowedCallbacks.js +122 -0
  18. package/analyzer/lib/clients/checkAllowedLogoutUrl.js +124 -0
  19. package/analyzer/lib/clients/checkApplicationLoginUri.js +125 -0
  20. package/analyzer/lib/clients/checkCrossOriginAuthentication.js +91 -0
  21. package/analyzer/lib/clients/checkGrantTypes.js +138 -0
  22. package/analyzer/lib/clients/checkJWTSignAlg.js +118 -0
  23. package/analyzer/lib/clients/checkRefreshToken.js +108 -0
  24. package/analyzer/lib/clients/checkWebOrigins.js +55 -0
  25. package/analyzer/lib/constants.js +63 -0
  26. package/analyzer/lib/custom_domain/checkCustomDomain.js +53 -0
  27. package/analyzer/lib/databases/checkAuthenticationMethods.js +98 -0
  28. package/analyzer/lib/databases/checkDASHardCodedValues.js +163 -0
  29. package/analyzer/lib/databases/checkEmailAttributeVerification.js +114 -0
  30. package/analyzer/lib/databases/checkEnabledDatabaseCustomization.js +83 -0
  31. package/analyzer/lib/databases/checkPasswordComplexity.js +100 -0
  32. package/analyzer/lib/databases/checkPasswordHistory.js +92 -0
  33. package/analyzer/lib/databases/checkPasswordNoPersonalInfo.js +91 -0
  34. package/analyzer/lib/databases/checkPasswordPolicy.js +95 -0
  35. package/analyzer/lib/databases/checkPromotedDBConnection.js +96 -0
  36. package/analyzer/lib/email_provider/checkEmailProvider.js +37 -0
  37. package/analyzer/lib/email_templates/checkEmailTemplates.js +71 -0
  38. package/analyzer/lib/error_page_template/checkErrorPageTemplate.js +153 -0
  39. package/analyzer/lib/event_streams/checkEventStreams.js +71 -0
  40. package/analyzer/lib/executeCheck.js +12 -0
  41. package/analyzer/lib/hooks/checkHooks.js +43 -0
  42. package/analyzer/lib/listOfAnalyser.js +24 -0
  43. package/analyzer/lib/log_streams/checkLogStream.js +60 -0
  44. package/analyzer/lib/logger.js +16 -0
  45. package/analyzer/lib/multifactor/checkGuardianFactors.js +72 -0
  46. package/analyzer/lib/multifactor/checkGuardianPolicy.js +40 -0
  47. package/analyzer/lib/network_acl/checkNetworkACL.js +35 -0
  48. package/analyzer/lib/rules/checkRules.js +102 -0
  49. package/analyzer/lib/tenant_settings/checkDefaultAudience.js +53 -0
  50. package/analyzer/lib/tenant_settings/checkDefaultDirectory.js +48 -0
  51. package/analyzer/lib/tenant_settings/checkEnabledDynamicClientRegistration.js +60 -0
  52. package/analyzer/lib/tenant_settings/checkSandboxVersion.js +37 -0
  53. package/analyzer/lib/tenant_settings/checkSessionLifetime.js +95 -0
  54. package/analyzer/lib/tenant_settings/checkSupportEmail.js +61 -0
  55. package/analyzer/lib/tenant_settings/checkSupportUrl.js +61 -0
  56. package/analyzer/lib/tenant_settings/checkTenantLoginUrl.js +71 -0
  57. package/analyzer/lib/tenant_settings/checkTenantLogoutUrl.js +60 -0
  58. package/analyzer/report.js +404 -0
  59. package/analyzer/tools/auth0.js +443 -0
  60. package/analyzer/tools/helpers.js +71 -0
  61. package/analyzer/tools/summary.js +84 -0
  62. package/analyzer/tools/utils.js +72 -0
  63. package/bin/index.js +393 -0
  64. package/eslint.config.mjs +16 -0
  65. package/images/auth0.png +0 -0
  66. package/images/okta.png +0 -0
  67. package/locales/en.json +1417 -0
  68. package/package.json +66 -0
  69. package/tests/actions/checkActionsHardCodedValues.test.js +106 -0
  70. package/tests/actions/checkActionsRuntime.test.js +102 -0
  71. package/tests/actions/checkDependencies.test.js +131 -0
  72. package/tests/attack_protection/checkBreachedPassword.test.js +253 -0
  73. package/tests/attack_protection/checkBruteForce.test.js +181 -0
  74. package/tests/attack_protection/checkSuspiciousIPThrottling.test.js +222 -0
  75. package/tests/canonical_domain/checkCanonicalDomain.test.js +94 -0
  76. package/tests/clients/checkAllowedCallbacks.test.js +149 -0
  77. package/tests/clients/checkAllowedLogoutUrl.test.js +149 -0
  78. package/tests/clients/checkApplicationLoginUri.test.js +180 -0
  79. package/tests/clients/checkCrossOriginAuthentication.test.js +99 -0
  80. package/tests/clients/checkGrantTypes.test.js +154 -0
  81. package/tests/clients/checkJWTSignAlg.test.js +121 -0
  82. package/tests/clients/checkRefreshToken.test.js +63 -0
  83. package/tests/clients/checkWebOrigins.test.js +140 -0
  84. package/tests/custom_domain/checkCustomDomain.test.js +73 -0
  85. package/tests/databases/checkAuthenticationMethods.test.js +124 -0
  86. package/tests/databases/checkDASHardCodedValues.test.js +77 -0
  87. package/tests/databases/checkEmailAttributeVerification.test.js +79 -0
  88. package/tests/databases/checkEnabledDatabaseCustomization.test.js +68 -0
  89. package/tests/databases/checkPasswordComplexity.test.js +127 -0
  90. package/tests/databases/checkPasswordHistory.test.js +100 -0
  91. package/tests/databases/checkPasswordNoPersonalInfo.test.js +94 -0
  92. package/tests/databases/checkPasswordPolicy.test.js +161 -0
  93. package/tests/databases/checkPromotedDBConnection.test.js +62 -0
  94. package/tests/email_provider/checkEmailProvider.test.js +58 -0
  95. package/tests/email_templates/checkEmailTemplates.test.js +120 -0
  96. package/tests/error_page_template/checkErrorPageTemplate.test.js +315 -0
  97. package/tests/event_streams/checkEventStreams.test.js +118 -0
  98. package/tests/hooks/checkHooks.test.js +112 -0
  99. package/tests/log_streams/checkLogStream.test.js +140 -0
  100. package/tests/multifactor/checkGuardianFactors.test.js +94 -0
  101. package/tests/multifactor/checkGuardianPolicy.test.js +49 -0
  102. package/tests/rules/checkRules.test.js +102 -0
  103. package/tests/tenant_settings/checkDefaultAudience.test.js +62 -0
  104. package/tests/tenant_settings/checkDefaultDirectory.test.js +62 -0
  105. package/tests/tenant_settings/checkEnabledDynamicClientRegistration.test.js +97 -0
  106. package/tests/tenant_settings/checkSandboxVersion.test.js +50 -0
  107. package/tests/tenant_settings/checkSessionLifetime.test.js +108 -0
  108. package/tests/tenant_settings/checkSupportEmail.test.js +77 -0
  109. package/tests/tenant_settings/checkSupportUrl.test.js +77 -0
  110. package/tests/tenant_settings/checkTenantLoginUri.test.js +82 -0
  111. package/tests/tenant_settings/checkTenantLogoutUrl.test.js +108 -0
  112. package/tests/tools/auth0.test.js +833 -0
  113. package/tests/tools/helpers.test.js +692 -0
  114. 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
+ };