@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,692 @@
1
+ const { expect } = require("chai");
2
+ const axios = require("axios");
3
+ const semver = require("semver");
4
+ const {
5
+ checkVulnerableVersion,
6
+ checkGitHubAdvisories,
7
+ getActionDependencies
8
+ } = require("../../analyzer/tools/helpers");
9
+ const logger = require("../../analyzer/lib/logger");
10
+
11
+ describe("helpers.js", function() {
12
+ let originalAxiosGet;
13
+ let originalLoggerLog;
14
+ let originalSemverSatisfies;
15
+
16
+ beforeEach(function() {
17
+ // Store original functions
18
+ originalAxiosGet = axios.get;
19
+ originalLoggerLog = logger.log;
20
+ originalSemverSatisfies = semver.satisfies;
21
+
22
+ // Mock logger.log to capture log messages
23
+ logger.log = function() {};
24
+ });
25
+
26
+ afterEach(function() {
27
+ // Restore original functions
28
+ axios.get = originalAxiosGet;
29
+ logger.log = originalLoggerLog;
30
+ semver.satisfies = originalSemverSatisfies;
31
+ });
32
+
33
+ describe("checkVulnerableVersion", function() {
34
+ it("should return empty array when no vulnerabilities are found", async function() {
35
+ const currentVersion = "1.0.0";
36
+ const advisoryData = [
37
+ {
38
+ cve_id: "CVE-2023-1234",
39
+ html_url: "https://github.com/advisories/CVE-2023-1234",
40
+ summary: "Test vulnerability",
41
+ severity: "high",
42
+ vulnerabilities: [
43
+ {
44
+ vulnerable_version_range: "< 0.5.0"
45
+ }
46
+ ]
47
+ }
48
+ ];
49
+
50
+ semver.satisfies = function() {
51
+ return false; // Not vulnerable
52
+ };
53
+
54
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
55
+
56
+ expect(result).to.be.an("array");
57
+ expect(result).to.have.length(0);
58
+ });
59
+
60
+ it("should return vulnerability details when version is vulnerable", async function() {
61
+ const currentVersion = "0.4.0";
62
+ const advisoryData = [
63
+ {
64
+ cve_id: "CVE-2023-1234",
65
+ html_url: "https://github.com/advisories/CVE-2023-1234",
66
+ summary: "Critical security vulnerability",
67
+ severity: "critical",
68
+ vulnerabilities: [
69
+ {
70
+ vulnerable_version_range: "< 0.5.0"
71
+ }
72
+ ]
73
+ }
74
+ ];
75
+
76
+ semver.satisfies = function(version, range) {
77
+ return version === "0.4.0" && range === "< 0.5.0";
78
+ };
79
+
80
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
81
+
82
+ expect(result).to.be.an("array");
83
+ expect(result).to.have.length(1);
84
+ expect(result[0]).to.deep.equal({
85
+ description: "Vulnerable to CVE-2023-1234 (range: < 0.5.0)",
86
+ advisory_url: "https://github.com/advisories/CVE-2023-1234",
87
+ advisory_summary: "Critical security vulnerability",
88
+ severity: "critical"
89
+ });
90
+ });
91
+
92
+ it("should handle multiple vulnerabilities in a single advisory", async function() {
93
+ const currentVersion = "1.5.0";
94
+ const advisoryData = [
95
+ {
96
+ cve_id: "CVE-2023-5678",
97
+ html_url: "https://github.com/advisories/CVE-2023-5678",
98
+ summary: "Multiple vulnerabilities",
99
+ severity: "high",
100
+ vulnerabilities: [
101
+ {
102
+ vulnerable_version_range: "< 2.0.0"
103
+ },
104
+ {
105
+ vulnerable_version_range: ">= 1.0.0 < 1.8.0"
106
+ }
107
+ ]
108
+ }
109
+ ];
110
+
111
+ semver.satisfies = function(version, range) {
112
+ if (version === "1.5.0") {
113
+ return range === "< 2.0.0" || range === ">= 1.0.0 < 1.8.0";
114
+ }
115
+ return false;
116
+ };
117
+
118
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
119
+
120
+ expect(result).to.be.an("array");
121
+ expect(result).to.have.length(2);
122
+ expect(result[0].description).to.contain("CVE-2023-5678");
123
+ expect(result[1].description).to.contain("CVE-2023-5678");
124
+ });
125
+
126
+ it("should handle multiple advisories", async function() {
127
+ const currentVersion = "1.0.0";
128
+ const advisoryData = [
129
+ {
130
+ cve_id: "CVE-2023-1111",
131
+ html_url: "https://github.com/advisories/CVE-2023-1111",
132
+ summary: "First vulnerability",
133
+ severity: "medium",
134
+ vulnerabilities: [
135
+ {
136
+ vulnerable_version_range: "<= 1.0.0"
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ cve_id: "CVE-2023-2222",
142
+ html_url: "https://github.com/advisories/CVE-2023-2222",
143
+ summary: "Second vulnerability",
144
+ severity: "low",
145
+ vulnerabilities: [
146
+ {
147
+ vulnerable_version_range: "= 1.0.0"
148
+ }
149
+ ]
150
+ }
151
+ ];
152
+
153
+ semver.satisfies = function(version, range) {
154
+ if (version === "1.0.0") {
155
+ return range === "<= 1.0.0" || range === "= 1.0.0";
156
+ }
157
+ return false;
158
+ };
159
+
160
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
161
+
162
+ expect(result).to.be.an("array");
163
+ expect(result).to.have.length(2);
164
+ expect(result[0].description).to.contain("CVE-2023-1111");
165
+ expect(result[1].description).to.contain("CVE-2023-2222");
166
+ });
167
+
168
+ it("should handle empty advisory data", async function() {
169
+ const currentVersion = "1.0.0";
170
+ const advisoryData = [];
171
+
172
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
173
+
174
+ expect(result).to.be.an("array");
175
+ expect(result).to.have.length(0);
176
+ });
177
+
178
+ it("should handle advisory with no vulnerabilities", async function() {
179
+ const currentVersion = "1.0.0";
180
+ const advisoryData = [
181
+ {
182
+ cve_id: "CVE-2023-0000",
183
+ html_url: "https://github.com/advisories/CVE-2023-0000",
184
+ summary: "No vulnerabilities",
185
+ severity: "info",
186
+ vulnerabilities: []
187
+ }
188
+ ];
189
+
190
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
191
+
192
+ expect(result).to.be.an("array");
193
+ expect(result).to.have.length(0);
194
+ });
195
+ });
196
+
197
+ describe("checkGitHubAdvisories", function() {
198
+ it("should return vulnerability findings on successful API call", async function() {
199
+ const mockAdvisoryData = [
200
+ {
201
+ cve_id: "CVE-2023-9999",
202
+ html_url: "https://github.com/advisories/CVE-2023-9999",
203
+ summary: "Test vulnerability from GitHub API",
204
+ severity: "high",
205
+ vulnerabilities: [
206
+ {
207
+ vulnerable_version_range: "< 2.0.0"
208
+ }
209
+ ]
210
+ }
211
+ ];
212
+
213
+ axios.get = async function(url, options) {
214
+ expect(url).to.equal("https://api.github.com/advisories?affects=lodash@1.0.0");
215
+ expect(options.headers.Accept).to.equal("application/vnd.github.v3+json");
216
+ return { data: mockAdvisoryData };
217
+ };
218
+
219
+ semver.satisfies = function(version, range) {
220
+ return version === "1.0.0" && range === "< 2.0.0";
221
+ };
222
+
223
+ const result = await checkGitHubAdvisories("lodash", "1.0.0");
224
+
225
+ expect(result).to.be.an("array");
226
+ expect(result).to.have.length(1);
227
+ expect(result[0].description).to.contain("CVE-2023-9999");
228
+ });
229
+
230
+ it("should return empty array when no vulnerabilities are found", async function() {
231
+ const mockAdvisoryData = [
232
+ {
233
+ cve_id: "CVE-2023-0001",
234
+ html_url: "https://github.com/advisories/CVE-2023-0001",
235
+ summary: "Non-applicable vulnerability",
236
+ severity: "medium",
237
+ vulnerabilities: [
238
+ {
239
+ vulnerable_version_range: "< 0.5.0"
240
+ }
241
+ ]
242
+ }
243
+ ];
244
+
245
+ axios.get = async function() {
246
+ return { data: mockAdvisoryData };
247
+ };
248
+
249
+ semver.satisfies = function() {
250
+ return false; // Not vulnerable
251
+ };
252
+
253
+ const result = await checkGitHubAdvisories("express", "4.18.0");
254
+
255
+ expect(result).to.be.an("array");
256
+ expect(result).to.have.length(0);
257
+ });
258
+
259
+ it("should handle API errors gracefully and return empty array", async function() {
260
+ let loggedError = null;
261
+ logger.log = function(level, message) {
262
+ if (level === "error") {
263
+ loggedError = message;
264
+ }
265
+ };
266
+
267
+ axios.get = async function() {
268
+ const error = new Error("GitHub API rate limit exceeded");
269
+ error.response = { status: 403 };
270
+ throw error;
271
+ };
272
+
273
+ const result = await checkGitHubAdvisories("react", "18.0.0");
274
+
275
+ expect(result).to.be.an("array");
276
+ expect(result).to.have.length(0);
277
+ expect(loggedError).to.contain("Failed to get github advisory skipping");
278
+ expect(loggedError).to.contain("GitHub API rate limit exceeded");
279
+ });
280
+
281
+ it("should handle network timeout errors", async function() {
282
+ let loggedError = null;
283
+ logger.log = function(level, message) {
284
+ if (level === "error") {
285
+ loggedError = message;
286
+ }
287
+ };
288
+
289
+ axios.get = async function() {
290
+ const error = new Error("timeout of 5000ms exceeded");
291
+ error.code = "ECONNABORTED";
292
+ throw error;
293
+ };
294
+
295
+ const result = await checkGitHubAdvisories("moment", "2.29.0");
296
+
297
+ expect(result).to.be.an("array");
298
+ expect(result).to.have.length(0);
299
+ expect(loggedError).to.contain("timeout of 5000ms exceeded");
300
+ });
301
+
302
+ it("should construct correct API URL with package name and version", async function() {
303
+ let capturedUrl = null;
304
+
305
+ axios.get = async function(url) {
306
+ capturedUrl = url;
307
+ return { data: [] };
308
+ };
309
+
310
+ await checkGitHubAdvisories("@types/node", "16.11.0");
311
+
312
+ expect(capturedUrl).to.equal("https://api.github.com/advisories?affects=@types/node@16.11.0");
313
+ });
314
+
315
+ it("should include correct headers in API request", async function() {
316
+ let capturedHeaders = null;
317
+
318
+ axios.get = async function(url, options) {
319
+ capturedHeaders = options.headers;
320
+ return { data: [] };
321
+ };
322
+
323
+ await checkGitHubAdvisories("chalk", "4.1.2");
324
+
325
+ expect(capturedHeaders).to.deep.equal({
326
+ Accept: "application/vnd.github.v3+json"
327
+ });
328
+ });
329
+ });
330
+
331
+ describe("getActionDependencies", function() {
332
+ it("should return empty array when no actions are provided", async function() {
333
+ const result = await getActionDependencies([]);
334
+
335
+ expect(result).to.be.an("array");
336
+ expect(result).to.have.length(0);
337
+ });
338
+
339
+ it("should return empty array when actions have no vulnerable dependencies", async function() {
340
+ const actionsList = [
341
+ {
342
+ name: "Test Action",
343
+ dependencies: [
344
+ { name: "safe-package", version: "1.0.0" }
345
+ ],
346
+ supported_triggers: [{ id: "post-login" }]
347
+ }
348
+ ];
349
+
350
+ axios.get = async function() {
351
+ return { data: [] }; // No vulnerabilities
352
+ };
353
+
354
+ const result = await getActionDependencies(actionsList);
355
+
356
+ expect(result).to.be.an("array");
357
+ expect(result).to.have.length(0);
358
+ });
359
+
360
+ it("should return vulnerability details for actions with vulnerable dependencies", async function() {
361
+ const actionsList = [
362
+ {
363
+ name: "Auth Action",
364
+ dependencies: [
365
+ { name: "vulnerable-package", version: "1.0.0" },
366
+ { name: "safe-package", version: "2.0.0" }
367
+ ],
368
+ supported_triggers: [{ id: "post-login" }]
369
+ }
370
+ ];
371
+
372
+ let callCount = 0;
373
+ axios.get = async function(url) {
374
+ callCount++;
375
+ if (url.includes("vulnerable-package")) {
376
+ return {
377
+ data: [
378
+ {
379
+ cve_id: "CVE-2023-VULN",
380
+ html_url: "https://github.com/advisories/CVE-2023-VULN",
381
+ summary: "Critical vulnerability",
382
+ severity: "critical",
383
+ vulnerabilities: [
384
+ { vulnerable_version_range: "<= 1.0.0" }
385
+ ]
386
+ }
387
+ ]
388
+ };
389
+ } else {
390
+ return { data: [] }; // No vulnerabilities for safe-package
391
+ }
392
+ };
393
+
394
+ semver.satisfies = function(version, range) {
395
+ return version === "1.0.0" && range === "<= 1.0.0";
396
+ };
397
+
398
+ const result = await getActionDependencies(actionsList);
399
+
400
+ expect(result).to.be.an("array");
401
+ expect(result).to.have.length(1);
402
+ expect(result[0]).to.deep.include({
403
+ name: "vulnerable-package",
404
+ actionName: "Auth Action",
405
+ version: "1.0.0",
406
+ trigger: "post-login"
407
+ });
408
+ expect(result[0].vulnFindings).to.have.length(1);
409
+ expect(result[0].vulnFindings[0].description).to.contain("CVE-2023-VULN");
410
+ expect(callCount).to.equal(2); // Called for both dependencies
411
+ });
412
+
413
+ it("should handle multiple actions with multiple vulnerable dependencies", async function() {
414
+ const actionsList = [
415
+ {
416
+ name: "Action 1",
417
+ dependencies: [
418
+ { name: "vuln-pkg-1", version: "0.5.0" }
419
+ ],
420
+ supported_triggers: [{ id: "post-login" }]
421
+ },
422
+ {
423
+ name: "Action 2",
424
+ dependencies: [
425
+ { name: "vuln-pkg-2", version: "1.2.0" }
426
+ ],
427
+ supported_triggers: [{ id: "pre-user-registration" }]
428
+ }
429
+ ];
430
+
431
+ axios.get = async function(url) {
432
+ if (url.includes("vuln-pkg-1")) {
433
+ return {
434
+ data: [
435
+ {
436
+ cve_id: "CVE-2023-0001",
437
+ html_url: "https://github.com/advisories/CVE-2023-0001",
438
+ summary: "First vulnerability",
439
+ severity: "high",
440
+ vulnerabilities: [
441
+ { vulnerable_version_range: "< 1.0.0" }
442
+ ]
443
+ }
444
+ ]
445
+ };
446
+ } else if (url.includes("vuln-pkg-2")) {
447
+ return {
448
+ data: [
449
+ {
450
+ cve_id: "CVE-2023-0002",
451
+ html_url: "https://github.com/advisories/CVE-2023-0002",
452
+ summary: "Second vulnerability",
453
+ severity: "medium",
454
+ vulnerabilities: [
455
+ { vulnerable_version_range: ">= 1.0.0 < 1.5.0" }
456
+ ]
457
+ }
458
+ ]
459
+ };
460
+ }
461
+ return { data: [] };
462
+ };
463
+
464
+ semver.satisfies = function(version, range) {
465
+ return (version === "0.5.0" && range === "< 1.0.0") ||
466
+ (version === "1.2.0" && range === ">= 1.0.0 < 1.5.0");
467
+ };
468
+
469
+ const result = await getActionDependencies(actionsList);
470
+
471
+ expect(result).to.be.an("array");
472
+ expect(result).to.have.length(2);
473
+
474
+ expect(result[0].name).to.equal("vuln-pkg-1");
475
+ expect(result[0].actionName).to.equal("Action 1");
476
+ expect(result[0].trigger).to.equal("post-login");
477
+
478
+ expect(result[1].name).to.equal("vuln-pkg-2");
479
+ expect(result[1].actionName).to.equal("Action 2");
480
+ expect(result[1].trigger).to.equal("pre-user-registration");
481
+ });
482
+
483
+ it("should handle actions with no dependencies", async function() {
484
+ const actionsList = [
485
+ {
486
+ name: "Simple Action",
487
+ dependencies: [],
488
+ supported_triggers: [{ id: "post-login" }]
489
+ }
490
+ ];
491
+
492
+ const result = await getActionDependencies(actionsList);
493
+
494
+ expect(result).to.be.an("array");
495
+ expect(result).to.have.length(0);
496
+ });
497
+
498
+ it("should handle API errors during dependency checking", async function() {
499
+ const actionsList = [
500
+ {
501
+ name: "Error Action",
502
+ dependencies: [
503
+ { name: "error-package", version: "1.0.0" }
504
+ ],
505
+ supported_triggers: [{ id: "post-login" }]
506
+ }
507
+ ];
508
+
509
+ let loggedErrors = [];
510
+ logger.log = function(level, message) {
511
+ if (level === "error") {
512
+ loggedErrors.push(message);
513
+ }
514
+ };
515
+
516
+ axios.get = async function() {
517
+ throw new Error("API request failed");
518
+ };
519
+
520
+ const result = await getActionDependencies(actionsList);
521
+
522
+ expect(result).to.be.an("array");
523
+ expect(result).to.have.length(0);
524
+ expect(loggedErrors).to.have.length(1);
525
+ expect(loggedErrors[0]).to.contain("Failed to get github advisory skipping");
526
+ });
527
+
528
+ it("should handle actions with mixed vulnerable and safe dependencies", async function() {
529
+ const actionsList = [
530
+ {
531
+ name: "Mixed Action",
532
+ dependencies: [
533
+ { name: "safe-package", version: "2.0.0" },
534
+ { name: "vulnerable-package", version: "0.1.0" },
535
+ { name: "another-safe-package", version: "3.0.0" }
536
+ ],
537
+ supported_triggers: [{ id: "post-change-password" }]
538
+ }
539
+ ];
540
+
541
+ axios.get = async function(url) {
542
+ if (url.includes("vulnerable-package")) {
543
+ return {
544
+ data: [
545
+ {
546
+ cve_id: "CVE-2023-MIXED",
547
+ html_url: "https://github.com/advisories/CVE-2023-MIXED",
548
+ summary: "Mixed action vulnerability",
549
+ severity: "high",
550
+ vulnerabilities: [
551
+ { vulnerable_version_range: "< 1.0.0" }
552
+ ]
553
+ }
554
+ ]
555
+ };
556
+ }
557
+ return { data: [] }; // Safe packages have no vulnerabilities
558
+ };
559
+
560
+ semver.satisfies = function(version, range) {
561
+ return version === "0.1.0" && range === "< 1.0.0";
562
+ };
563
+
564
+ const result = await getActionDependencies(actionsList);
565
+
566
+ expect(result).to.be.an("array");
567
+ expect(result).to.have.length(1); // Only the vulnerable package
568
+ expect(result[0].name).to.equal("vulnerable-package");
569
+ expect(result[0].actionName).to.equal("Mixed Action");
570
+ expect(result[0].trigger).to.equal("post-change-password");
571
+ });
572
+
573
+ it("should handle actions with multiple supported triggers", async function() {
574
+ const actionsList = [
575
+ {
576
+ name: "Multi-Trigger Action",
577
+ dependencies: [
578
+ { name: "vuln-package", version: "1.0.0" }
579
+ ],
580
+ supported_triggers: [
581
+ { id: "post-login" },
582
+ { id: "pre-user-registration" }
583
+ ]
584
+ }
585
+ ];
586
+
587
+ axios.get = async function() {
588
+ return {
589
+ data: [
590
+ {
591
+ cve_id: "CVE-2023-MULTI",
592
+ html_url: "https://github.com/advisories/CVE-2023-MULTI",
593
+ summary: "Multi-trigger vulnerability",
594
+ severity: "medium",
595
+ vulnerabilities: [
596
+ { vulnerable_version_range: "= 1.0.0" }
597
+ ]
598
+ }
599
+ ]
600
+ };
601
+ };
602
+
603
+ semver.satisfies = function(version, range) {
604
+ return version === "1.0.0" && range === "= 1.0.0";
605
+ };
606
+
607
+ const result = await getActionDependencies(actionsList);
608
+
609
+ expect(result).to.be.an("array");
610
+ expect(result).to.have.length(1);
611
+ expect(result[0].trigger).to.equal("post-login"); // Should use first trigger
612
+ });
613
+ });
614
+
615
+ describe("Integration and Edge Cases", function() {
616
+ it("should handle semver edge cases", async function() {
617
+ const currentVersion = "1.0.0-beta.1";
618
+ const advisoryData = [
619
+ {
620
+ cve_id: "CVE-2023-BETA",
621
+ html_url: "https://github.com/advisories/CVE-2023-BETA",
622
+ summary: "Beta version vulnerability",
623
+ severity: "low",
624
+ vulnerabilities: [
625
+ {
626
+ vulnerable_version_range: ">=1.0.0-alpha <1.0.0"
627
+ }
628
+ ]
629
+ }
630
+ ];
631
+
632
+ // Use actual semver function to test real behavior
633
+ semver.satisfies = originalSemverSatisfies;
634
+
635
+ const result = await checkVulnerableVersion(currentVersion, advisoryData);
636
+
637
+ expect(result).to.be.an("array");
638
+ // Beta versions like 1.0.0-beta.1 should satisfy ">=1.0.0-alpha <1.0.0"
639
+ expect(result).to.have.length(1);
640
+ expect(result[0].description).to.contain("CVE-2023-BETA");
641
+ });
642
+
643
+ it("should handle malformed advisory data gracefully", async function() {
644
+ const currentVersion = "1.0.0";
645
+ const malformedAdvisoryData = [
646
+ {
647
+ // Missing required fields
648
+ vulnerabilities: [
649
+ {
650
+ vulnerable_version_range: "< 1.0.0"
651
+ }
652
+ ]
653
+ },
654
+ {
655
+ cve_id: "CVE-2023-GOOD",
656
+ html_url: "https://github.com/advisories/CVE-2023-GOOD",
657
+ summary: "Good advisory",
658
+ severity: "medium",
659
+ vulnerabilities: [
660
+ {
661
+ vulnerable_version_range: "= 1.0.0"
662
+ }
663
+ ]
664
+ }
665
+ ];
666
+
667
+ semver.satisfies = function(version, range) {
668
+ return version === "1.0.0" && range === "= 1.0.0";
669
+ };
670
+
671
+ const result = await checkVulnerableVersion(currentVersion, malformedAdvisoryData);
672
+
673
+ expect(result).to.be.an("array");
674
+ expect(result).to.have.length(1); // Should only process the well-formed advisory
675
+ expect(result[0].description).to.contain("CVE-2023-GOOD");
676
+ });
677
+
678
+ it("should handle undefined/null values gracefully", async function() {
679
+ const currentVersion = "1.0.0";
680
+ const advisoryData = null;
681
+
682
+ let threwError = false;
683
+ try {
684
+ await checkVulnerableVersion(currentVersion, advisoryData);
685
+ } catch {
686
+ threwError = true;
687
+ }
688
+
689
+ expect(threwError).to.be.true; // Should handle gracefully or throw appropriately
690
+ });
691
+ });
692
+ });