@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,92 @@
1
+ /*
2
+ {
3
+ databases: [
4
+ {
5
+ "id": "con_JBv3Nu3wcKQni7Vv",
6
+ "options": {
7
+ "import_mode": false,
8
+ "configuration": {},
9
+ "disable_signup": true,
10
+ "passwordPolicy": "good",
11
+ "passkey_options": {
12
+ "challenge_ui": "both",
13
+ "local_enrollment_enabled": true,
14
+ "progressive_enrollment_enabled": true
15
+ },
16
+ "password_history": {
17
+ "size": 5,
18
+ "enable": false
19
+ },
20
+ "strategy_version": 2,
21
+ "password_dictionary": {
22
+ "enable": false,
23
+ "dictionary": []
24
+ },
25
+ "authentication_methods": {
26
+ "passkey": {
27
+ "enabled": false
28
+ },
29
+ "password": {
30
+ "enabled": true
31
+ }
32
+ },
33
+ "brute_force_protection": true,
34
+ "password_no_personal_info": {
35
+ "enable": false
36
+ },
37
+ "password_complexity_options": {
38
+ "min_length": 8
39
+ },
40
+ "enabledDatabaseCustomization": false
41
+ },
42
+ "strategy": "auth0",
43
+ "name": "Username-Password-Authentication",
44
+ "is_domain_connection": false,
45
+ "realms": [
46
+ "Username-Password-Authentication"
47
+ ],
48
+ "enabled_clients": [
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ */
54
+ const _ = require("lodash");
55
+ const executeCheck = require("../executeCheck");
56
+ const CONSTANTS = require("../constants");
57
+
58
+ function checkPasswordHistory(options) {
59
+ const { databases } = options || [];
60
+ return executeCheck("checkPasswordHistory", (callback) => {
61
+ const report = [];
62
+ if (_.isEmpty(databases)) {
63
+ report.push({
64
+ field: "no_database_connections_found",
65
+ status: CONSTANTS.FAIL,
66
+ });
67
+ return callback(report);
68
+ }
69
+ databases.forEach((connection) => {
70
+ //Check Password History
71
+ if (connection.options.password_history) {
72
+ if (!connection.options.password_history.enable) {
73
+ report.push({
74
+ name: connection.name,
75
+ status: CONSTANTS.FAIL,
76
+ field: "password_history_disabled",
77
+ });
78
+ } else {
79
+ report.push({
80
+ name: connection.name,
81
+ field: "password_history_enabled",
82
+ status: CONSTANTS.SUCCESS,
83
+ value: connection.options.password_history.size,
84
+ });
85
+ }
86
+ }
87
+ });
88
+ return callback(report);
89
+ });
90
+ }
91
+
92
+ module.exports = checkPasswordHistory;
@@ -0,0 +1,91 @@
1
+ /*
2
+ {
3
+ databases: [
4
+ {
5
+ "id": "con_JBv3Nu3wcKQni7Vv",
6
+ "options": {
7
+ "import_mode": false,
8
+ "configuration": {},
9
+ "disable_signup": true,
10
+ "passwordPolicy": "good",
11
+ "passkey_options": {
12
+ "challenge_ui": "both",
13
+ "local_enrollment_enabled": true,
14
+ "progressive_enrollment_enabled": true
15
+ },
16
+ "password_history": {
17
+ "size": 5,
18
+ "enable": false
19
+ },
20
+ "strategy_version": 2,
21
+ "password_dictionary": {
22
+ "enable": false,
23
+ "dictionary": []
24
+ },
25
+ "authentication_methods": {
26
+ "passkey": {
27
+ "enabled": false
28
+ },
29
+ "password": {
30
+ "enabled": true
31
+ }
32
+ },
33
+ "brute_force_protection": true,
34
+ "password_no_personal_info": {
35
+ "enable": false
36
+ },
37
+ "password_complexity_options": {
38
+ "min_length": 8
39
+ },
40
+ "enabledDatabaseCustomization": false
41
+ },
42
+ "strategy": "auth0",
43
+ "name": "Username-Password-Authentication",
44
+ "is_domain_connection": false,
45
+ "realms": [
46
+ "Username-Password-Authentication"
47
+ ],
48
+ "enabled_clients": [
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ */
54
+ const _ = require("lodash");
55
+ const executeCheck = require("../executeCheck");
56
+ const CONSTANTS = require("../constants");
57
+
58
+ function checkPasswordNoPersonalInfo(options) {
59
+ const { databases } = options || [];
60
+ return executeCheck("checkPasswordNoPersonalInfo", (callback) => {
61
+ const report = [];
62
+ if (_.isEmpty(databases)) {
63
+ report.push({
64
+ field: "no_database_connections_found",
65
+ status: CONSTANTS.FAIL,
66
+ });
67
+ return callback(report);
68
+ }
69
+ databases.forEach((connection) => {
70
+ //Disallow Personal Data
71
+ if (connection.options.password_no_personal_info) {
72
+ if (connection.options.password_no_personal_info.enable) {
73
+ report.push({
74
+ name: connection.name,
75
+ status: CONSTANTS.SUCCESS,
76
+ field: "password_no_personal_info_enable",
77
+ });
78
+ } else {
79
+ report.push({
80
+ name: connection.name,
81
+ status: CONSTANTS.FAIL,
82
+ field: "password_no_personal_info_disabled",
83
+ });
84
+ }
85
+ }
86
+ });
87
+ return callback(report);
88
+ });
89
+ }
90
+
91
+ module.exports = checkPasswordNoPersonalInfo;
@@ -0,0 +1,95 @@
1
+ /*
2
+ {
3
+ databases: [
4
+ {
5
+ "id": "con_JBv3Nu3wcKQni7Vv",
6
+ "options": {
7
+ "import_mode": false,
8
+ "configuration": {},
9
+ "disable_signup": true,
10
+ "passwordPolicy": "good",
11
+ "passkey_options": {
12
+ "challenge_ui": "both",
13
+ "local_enrollment_enabled": true,
14
+ "progressive_enrollment_enabled": true
15
+ },
16
+ "password_history": {
17
+ "size": 5,
18
+ "enable": false
19
+ },
20
+ "strategy_version": 2,
21
+ "password_dictionary": {
22
+ "enable": false,
23
+ "dictionary": []
24
+ },
25
+ "authentication_methods": {
26
+ "passkey": {
27
+ "enabled": false
28
+ },
29
+ "password": {
30
+ "enabled": true
31
+ }
32
+ },
33
+ "brute_force_protection": true,
34
+ "password_no_personal_info": {
35
+ "enable": false
36
+ },
37
+ "password_complexity_options": {
38
+ "min_length": 8
39
+ },
40
+ "enabledDatabaseCustomization": false
41
+ },
42
+ "strategy": "auth0",
43
+ "name": "Username-Password-Authentication",
44
+ "is_domain_connection": false,
45
+ "realms": [
46
+ "Username-Password-Authentication"
47
+ ],
48
+ "enabled_clients": [
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ */
54
+ const _ = require("lodash");
55
+ const executeCheck = require("../executeCheck");
56
+ const CONSTANTS = require("../constants");
57
+
58
+ function checkPasswordPolicy(options) {
59
+ const { databases } = options || [];
60
+ return executeCheck("checkPasswordPolicy", (callback) => {
61
+ const report = [];
62
+ if (_.isEmpty(databases)) {
63
+ report.push({
64
+ field: "no_database_connections_found",
65
+ status: CONSTANTS.FAIL,
66
+ });
67
+ return callback(report);
68
+ }
69
+ databases.forEach((connection) => {
70
+ var status = CONSTANTS.FAIL;
71
+ switch (connection.options.passwordPolicy) {
72
+ case "none":
73
+ case "low":
74
+ case "fair":
75
+ status = CONSTANTS.FAIL;
76
+ break;
77
+ case "good":
78
+ case "excellent":
79
+ status = CONSTANTS.SUCCESS;
80
+ break;
81
+ }
82
+ report.push({
83
+ name: connection.name,
84
+ field: connection.options.passwordPolicy
85
+ ? "password_policy"
86
+ : "missing_password_policy",
87
+ status: status,
88
+ value: connection.options.passwordPolicy,
89
+ });
90
+ });
91
+ return callback(report);
92
+ });
93
+ }
94
+
95
+ module.exports = checkPasswordPolicy;
@@ -0,0 +1,96 @@
1
+ /*
2
+ {
3
+ databases: [
4
+ {
5
+ "id": "con_JBv3Nu3wcKQni7Vv",
6
+ "options": {
7
+ "import_mode": false,
8
+ "configuration": {},
9
+ "disable_signup": true,
10
+ "passwordPolicy": "good",
11
+ "passkey_options": {
12
+ "challenge_ui": "both",
13
+ "local_enrollment_enabled": true,
14
+ "progressive_enrollment_enabled": true
15
+ },
16
+ "password_history": {
17
+ "size": 5,
18
+ "enable": false
19
+ },
20
+ "strategy_version": 2,
21
+ "password_dictionary": {
22
+ "enable": false,
23
+ "dictionary": []
24
+ },
25
+ "authentication_methods": {
26
+ "passkey": {
27
+ "enabled": false
28
+ },
29
+ "password": {
30
+ "enabled": true
31
+ }
32
+ },
33
+ "brute_force_protection": true,
34
+ "password_no_personal_info": {
35
+ "enable": false
36
+ },
37
+ "password_complexity_options": {
38
+ "min_length": 8
39
+ },
40
+ "enabledDatabaseCustomization": false
41
+ },
42
+ "strategy": "auth0",
43
+ "name": "Username-Password-Authentication",
44
+ "is_domain_connection": false,
45
+ "realms": [
46
+ "Username-Password-Authentication"
47
+ ],
48
+ "enabled_clients": [
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ */
54
+ const _ = require("lodash");
55
+ const executeCheck = require("../executeCheck");
56
+ const CONSTANTS = require("../constants");
57
+
58
+ function checkPromotedDBConnection(options) {
59
+
60
+ const { databases } = options || [];
61
+ return executeCheck("checkPromotedDBConnection", (callback) => {
62
+ const report = [];
63
+ if (_.isEmpty(databases)) {
64
+ report.push({
65
+ field: "no_database_connections_found",
66
+ status: CONSTANTS.FAIL,
67
+ });
68
+ return callback(report);
69
+ }
70
+ let promoted_domain_connection = null;
71
+ databases.forEach((connection) => {
72
+ connection.is_domain_connection;
73
+ if (connection.is_domain_connection === true) {
74
+ promoted_domain_connection = connection.name;
75
+ return;
76
+ }
77
+ });
78
+ if (promoted_domain_connection === null) {
79
+ report.push({
80
+ name: "NO_PRMOTED_DOMAIN_CONNECTION",
81
+ status: CONSTANTS.FAIL,
82
+ field: "no_database_connections_found",
83
+ });
84
+ }
85
+ else {
86
+ report.push({
87
+ name: promoted_domain_connection,
88
+ status: CONSTANTS.FAIL,
89
+ field: "with_promoted_database_connections",
90
+ });
91
+ }
92
+ return callback(report);
93
+ });
94
+ }
95
+
96
+ module.exports = checkPromotedDBConnection;
@@ -0,0 +1,37 @@
1
+ /*
2
+ {
3
+ "name": "sendgrid",
4
+ "enabled": true
5
+ }
6
+ */
7
+ const _ = require("lodash");
8
+ const executeCheck = require("../executeCheck");
9
+ const CONSTANTS = require("../constants");
10
+
11
+ function checkEmailProvider(options) {
12
+ const { emailProvider } = options || {};
13
+ return executeCheck("checkEmailProvider", (callback) => {
14
+ const report = [];
15
+ if (_.isEmpty(emailProvider)) {
16
+ report.push({
17
+ field: "email_provider_not_configured",
18
+ status: CONSTANTS.FAIL,
19
+ });
20
+ } else if (emailProvider.enabled) {
21
+ report.push({
22
+ field: "email_provider_enabled",
23
+ status: CONSTANTS.SUCCESS,
24
+ value: emailProvider.name,
25
+ });
26
+ } else {
27
+ report.push({
28
+ field: "email_provider_disabled",
29
+ status: CONSTANTS.FAIL,
30
+ value: emailProvider.name,
31
+ });
32
+ }
33
+ return callback(report);
34
+ });
35
+ }
36
+
37
+ module.exports = checkEmailProvider;
@@ -0,0 +1,71 @@
1
+ /*
2
+ [
3
+ {
4
+ name: 'Verification Email (Link)',
5
+ template: {
6
+ template: 'verify_email',
7
+ syntax: 'liquid',
8
+ body: ''
9
+ from: '',
10
+ subject: '',
11
+ urlLifetimeInSeconds: 432000,
12
+ enabled: true
13
+ }
14
+ },
15
+ { name: 'Verification Email (Code)', template: null },
16
+ { name: 'Welcome Email', template: null },
17
+ { name: 'Enroll in Multifactor Authentication', template: null },
18
+ { name: 'Change Password (Link)', template: null },
19
+ { name: 'Change Password (Code)', template: null },
20
+ { name: 'Blocked Account Email', template: null },
21
+ { name: 'Password Breach Alert', template: null },
22
+ { name: 'Verification Code for Email MFA', template: null },
23
+ { name: 'User Invitation', template: null }
24
+ ]
25
+ */
26
+ const _ = require("lodash");
27
+ const executeCheck = require("../executeCheck");
28
+ const CONSTANTS = require("../constants");
29
+
30
+ function checkEmailTemplates(options) {
31
+ const { emailTemplates } = options || [];
32
+ return executeCheck("checkEmailTemplates", (callback) => {
33
+ const report = [];
34
+ if (_.isEmpty(emailTemplates)) {
35
+ report.push({
36
+ field: "email_templates_not_configured",
37
+ status: CONSTANTS.FAIL,
38
+ });
39
+ return callback(report);
40
+ }
41
+ emailTemplates.forEach((t) => {
42
+ if (_.isEmpty(t.template)) {
43
+ report.push({
44
+ field: "email_template_not_configured",
45
+ status: CONSTANTS.FAIL,
46
+ attr: t.name,
47
+ value: t.name,
48
+ });
49
+ return;
50
+ }
51
+ if (t.template.enabled) {
52
+ report.push({
53
+ field: "email_template_enabled",
54
+ status: CONSTANTS.SUCCESS,
55
+ attr: t.template.template,
56
+ value: t.template.template,
57
+ });
58
+ } else {
59
+ report.push({
60
+ field: "email_template_not_enabled",
61
+ status: CONSTANTS.FAIL,
62
+ attr: t.name,
63
+ value: t.name,
64
+ });
65
+ }
66
+ });
67
+ return callback(report);
68
+ });
69
+ }
70
+
71
+ module.exports = checkEmailTemplates;
@@ -0,0 +1,153 @@
1
+ /*
2
+ {
3
+ "enabled_locales":[
4
+ "en"
5
+ ],
6
+ "error_page":{
7
+ "html":"<html>",
8
+ "show_log_link":false,
9
+ "url":""
10
+ },
11
+ "flags":{
12
+ "allow_changing_enable_sso":false,
13
+ "allow_legacy_delegation_grant_types":true,
14
+ "allow_legacy_ro_grant_types":true,
15
+ "allow_other_legacy_grant_types":true,
16
+ "disable_impersonation":true,
17
+ "enable_sso":true,
18
+ "universal_login":true,
19
+ "revoke_refresh_token_grant":false,
20
+ "disable_clickjack_protection_headers":false
21
+ },
22
+ "sandbox_version":"22",
23
+ "oidc_logout":{
24
+ "rp_logout_end_session_endpoint_discovery":true
25
+ },
26
+ "sandbox_versions_available":[
27
+ "22",
28
+ "18",
29
+ "16"
30
+ ]
31
+ }
32
+ */
33
+
34
+ const _ = require("lodash");
35
+ const executeCheck = require("../executeCheck");
36
+ const CONSTANTS = require("../constants");
37
+ const logger = require("../logger");
38
+
39
+ /**
40
+ * Patterns that indicate potential XSS vulnerabilities in LiquidJS templates
41
+ */
42
+ const XSS_PATTERNS = {
43
+ // Unescaped variable output - {{ variable }} without proper escaping
44
+ UNESCAPED_OUTPUT: /\{\{\s*([^}|]+?)(?:\s*\|\s*(?!escape|escape_once|h|html_escape)([^}]*))?\s*\}\}/g,
45
+
46
+ // Raw/unescaped filters that bypass HTML escaping
47
+ RAW_FILTERS: /\{\{\s*[^}]*\|\s*(raw|unescaped|safe)\s*\}\}/g,
48
+
49
+ // Direct HTML construction with variables
50
+ HTML_CONSTRUCTION: /<[^>]*\{\{[^}]*\}\}[^>]*>/g,
51
+
52
+ // JavaScript context insertions (dangerous)
53
+ SCRIPT_CONTEXT: /<script[^>]*>[\s\S]*?\{\{[^}]*\}\}[\s\S]*?<\/script>/gi,
54
+
55
+ // Event handler attributes with Liquid variables
56
+ EVENT_HANDLERS: /on\w+\s*=\s*["'][^"']*\{\{[^}]*\}\}[^"']*["']/gi,
57
+
58
+ // URL/href context without proper encoding
59
+ URL_CONTEXT: /href\s*=\s*["'][^"']*\{\{[^}]*\}\}[^"']*["']/gi,
60
+
61
+ // Style attribute context
62
+ STYLE_CONTEXT: /style\s*=\s*["'][^"']*\{\{[^}]*\}\}[^"']*["']/gi,
63
+
64
+ // Potentially dangerous user-controlled variables
65
+ USER_VARIABLES: /\{\{\s*(user\.|client\.|application\.)/g,
66
+ };
67
+
68
+ /**
69
+ * Safe filters that provide XSS protection
70
+ */
71
+ const SAFE_FILTERS = [
72
+ 'escape',
73
+ 'escape_once',
74
+ 'h',
75
+ 'html_escape',
76
+ 'url_encode',
77
+ 'uri_escape',
78
+ 'cgi_escape'
79
+ ];
80
+
81
+ async function checkErrorPageTemplate(options) {
82
+ const { errorPageTemplate } = options || {};
83
+ return executeCheck("checkErrorPageTemplate", (callback) => {
84
+ const report = [];
85
+ logger.log("info", `Checking error page templates... ${errorPageTemplate}`);
86
+
87
+ if (_.isEmpty(errorPageTemplate)) {
88
+ report.push({
89
+ field: "liquidjs_no_templates_to_analyze",
90
+ status: CONSTANTS.SUCCESS,
91
+ value: "No custom error page templates found to analyze for XSS"
92
+ });
93
+ return callback(report);
94
+ }
95
+
96
+ // Check for potential matches first
97
+ const potentialMatches = errorPageTemplate.match(XSS_PATTERNS.UNESCAPED_OUTPUT);
98
+ logger.log("info", `Checking error page templates... ${potentialMatches}`);
99
+
100
+ // Check for raw filters first (before checking unescaped)
101
+ const raw = errorPageTemplate.match(XSS_PATTERNS.RAW_FILTERS);
102
+ if (raw) {
103
+ logger.log("info", `logging error page template raw: ${raw}`);
104
+ report.push({
105
+ field: "liquidjs_raw_filter_usage",
106
+ status: CONSTANTS.FAIL,
107
+ value: JSON.stringify(raw),
108
+ });
109
+ } else {
110
+ report.push({
111
+ field: "liquidjs_raw_filter_usage",
112
+ status: CONSTANTS.SUCCESS,
113
+ value: JSON.stringify(raw) || "null",
114
+ });
115
+ }
116
+
117
+ // Now check for unescaped output, filtering out properly escaped variables
118
+ const unescaped = errorPageTemplate.match(XSS_PATTERNS.UNESCAPED_OUTPUT);
119
+ if (unescaped) {
120
+ // Filter out variables that actually have safe filters
121
+ const actuallyUnescaped = unescaped.filter(match => {
122
+ // Check if this match contains any safe filter
123
+ return !SAFE_FILTERS.some(filter =>
124
+ match.includes(`| ${filter}`) || match.includes(`|${filter}`)
125
+ );
126
+ });
127
+
128
+ if (actuallyUnescaped.length > 0) {
129
+ report.push({
130
+ field: "liquidjs_unescaped_output",
131
+ status: CONSTANTS.FAIL,
132
+ value: JSON.stringify(actuallyUnescaped),
133
+ });
134
+ } else {
135
+ report.push({
136
+ field: "liquidjs_unescaped_output",
137
+ status: CONSTANTS.SUCCESS,
138
+ value: "All variables properly escaped",
139
+ });
140
+ }
141
+ } else {
142
+ report.push({
143
+ field: "liquidjs_unescaped_output",
144
+ status: CONSTANTS.SUCCESS,
145
+ value: "No Liquid variables found",
146
+ });
147
+ }
148
+
149
+ return callback(report);
150
+ });
151
+ }
152
+
153
+ module.exports = checkErrorPageTemplate;
@@ -0,0 +1,71 @@
1
+ /*
2
+
3
+ "eventStreams": [
4
+ {
5
+ "id": "est_jtCx6JaM4zvimx9N7enZ7p",
6
+ "status": "enabled|disabled",
7
+ "name": "Test Event Streams",
8
+ "subscriptions": [
9
+ {
10
+ "event_type": "user.created"
11
+ },
12
+ {
13
+ "event_type": "user.updated"
14
+ },
15
+ {
16
+ "event_type": "user.deleted"
17
+ }
18
+ ],
19
+ "created_at": "2025-05-30T04:08:17.775Z",
20
+ "updated_at": "2025-05-30T04:08:56.303Z",
21
+ "destination": {
22
+ "type": "webhook",
23
+ "configuration": {
24
+ "webhook_endpoint": "https://localhost/webhook",
25
+ "webhook_authorization": {
26
+ "method": "bearer"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ ]
32
+ }
33
+ */
34
+
35
+ const _ = require("lodash");
36
+ const executeCheck = require("../executeCheck");
37
+ const CONSTANTS = require("../constants");
38
+
39
+ function checkEventStreams(options) {
40
+ const { eventStreams } = options;
41
+ return executeCheck("checkEventStreams", (callback) => {
42
+ const report = [];
43
+ const hasInsufficientScope = _.some(eventStreams, {
44
+ errorCode: "insufficient_scope",
45
+ });
46
+ if (hasInsufficientScope) {
47
+ return callback(report);
48
+ }
49
+ if (_.isEmpty(eventStreams)) {
50
+ report.push({
51
+ field: "event_stream_not_configured",
52
+ status: CONSTANTS.FAIL,
53
+ });
54
+ } else {
55
+ eventStreams.forEach((stream) => {
56
+ if (stream.status !== "enabled") {
57
+ report.push({
58
+ field: "event_stream_disabled",
59
+ name: stream.name,
60
+ type: stream?.destination.type,
61
+ stream_status: stream.status,
62
+ status: CONSTANTS.FAIL,
63
+ });
64
+ }
65
+ });
66
+ }
67
+ return callback(report);
68
+ });
69
+ }
70
+
71
+ module.exports = checkEventStreams;