@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,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;
|