@tryghost/domain-validator 2.0.0 → 3.0.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/lib/caa.js +37 -0
- package/lib/validate.js +42 -9
- package/package.json +3 -3
package/lib/caa.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const dns = require('@tryghost/dns');
|
|
2
|
+
const debug = require('@tryghost/debug')('dv:caa');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if the domain's CAA records allow the given issuer.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} customdomain
|
|
8
|
+
* @param {string} issuer - CA identity to check (e.g. 'certainly.com')
|
|
9
|
+
* @returns {Promise<{isCaaValid: boolean, caaSource: string|null, caaAllowedIssuers: string[], caaError: string|null}>}
|
|
10
|
+
*/
|
|
11
|
+
module.exports.validate = (customdomain, issuer) => {
|
|
12
|
+
debug('Checking CAA for ' + customdomain + ' against ' + issuer);
|
|
13
|
+
return dns.caa(customdomain, {issuer}).then((result) => {
|
|
14
|
+
return {
|
|
15
|
+
isCaaValid: result.allowed,
|
|
16
|
+
caaSource: result.source,
|
|
17
|
+
caaAllowedIssuers: result.allowedIssuers,
|
|
18
|
+
caaError: result.error
|
|
19
|
+
};
|
|
20
|
+
}).catch((err) => {
|
|
21
|
+
// Fail open: dns.caa fails open internally on DNS errors, but guard
|
|
22
|
+
// against unexpected throws (bad arguments, future regressions) so
|
|
23
|
+
// CAA can never take down the rest of the validity report. Tolerate
|
|
24
|
+
// non-Error rejections (null/undefined/strings) so this handler
|
|
25
|
+
// itself can never throw.
|
|
26
|
+
const message = (err && typeof err.message === 'string')
|
|
27
|
+
? err.message
|
|
28
|
+
: String(err || 'UNKNOWN');
|
|
29
|
+
debug('Unexpected CAA error: ' + message);
|
|
30
|
+
return {
|
|
31
|
+
isCaaValid: true,
|
|
32
|
+
caaSource: null,
|
|
33
|
+
caaAllowedIssuers: [],
|
|
34
|
+
caaError: message
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
};
|
package/lib/validate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const dns = require('./dns');
|
|
3
3
|
const pro = require('./pro');
|
|
4
|
+
const caa = require('./caa');
|
|
4
5
|
const debug = require('@tryghost/debug')('dv:validate');
|
|
5
6
|
|
|
6
7
|
const _private = {};
|
|
@@ -22,20 +23,28 @@ const validityReportObject = {
|
|
|
22
23
|
dnsRating: null,
|
|
23
24
|
isDnsValidForSSL: false,
|
|
24
25
|
sslRating: null,
|
|
26
|
+
isCaaValid: true,
|
|
27
|
+
caaSource: null,
|
|
28
|
+
caaAllowedIssuers: [],
|
|
29
|
+
caaError: null,
|
|
25
30
|
message: ''
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
_private.doChecks = (subdomain, customdomain, subdirectory, prodomain) => {
|
|
33
|
+
_private.doChecks = (subdomain, customdomain, subdirectory, prodomain, caaIssuer) => {
|
|
29
34
|
const checkResult = {};
|
|
30
35
|
debug('starting pro');
|
|
31
36
|
return pro.validate('https', subdomain, customdomain, subdirectory, prodomain)
|
|
32
37
|
.then((proResult) => {
|
|
33
38
|
checkResult.pro = proResult;
|
|
34
39
|
debug('got pro, starting dns');
|
|
35
|
-
return
|
|
40
|
+
return Promise.all([
|
|
41
|
+
dns.validate(subdomain, customdomain, checkResult.pro.isGhostPro, prodomain),
|
|
42
|
+
caaIssuer ? caa.validate(customdomain, caaIssuer) : Promise.resolve(null)
|
|
43
|
+
]);
|
|
36
44
|
})
|
|
37
|
-
.then((dnsResult) => {
|
|
45
|
+
.then(([dnsResult, caaResult]) => {
|
|
38
46
|
checkResult.dns = dnsResult;
|
|
47
|
+
checkResult.caa = caaResult;
|
|
39
48
|
debug('got dns, returning');
|
|
40
49
|
return checkResult;
|
|
41
50
|
});
|
|
@@ -43,16 +52,23 @@ _private.doChecks = (subdomain, customdomain, subdirectory, prodomain) => {
|
|
|
43
52
|
|
|
44
53
|
/**
|
|
45
54
|
* check health
|
|
46
|
-
* @param {Object} blog
|
|
47
55
|
*
|
|
48
56
|
* - returns a validity report
|
|
49
57
|
* - does NOT error unless something goes wrong
|
|
58
|
+
*
|
|
59
|
+
* @param {string} subdomain
|
|
60
|
+
* @param {string} customdomain
|
|
61
|
+
* @param {string} subdirectory
|
|
62
|
+
* @param {string} [prodomain=ghost.io]
|
|
63
|
+
* @param {string} [caaIssuer] - CA identity to check CAA records against (e.g. 'certainly.com').
|
|
64
|
+
* When omitted, CAA is not checked and isCaaValid defaults to true.
|
|
50
65
|
*/
|
|
51
|
-
module.exports.validate = (subdomain, customdomain, subdirectory, prodomain = 'ghost.io') => _private
|
|
52
|
-
.doChecks(subdomain, customdomain, subdirectory, prodomain)
|
|
66
|
+
module.exports.validate = (subdomain, customdomain, subdirectory, prodomain = 'ghost.io', caaIssuer) => _private
|
|
67
|
+
.doChecks(subdomain, customdomain, subdirectory, prodomain, caaIssuer)
|
|
53
68
|
.then(function (result) {
|
|
54
|
-
//
|
|
55
|
-
|
|
69
|
+
// Deep clone so nested mutable fields (input, caaAllowedIssuers)
|
|
70
|
+
// can't leak across validation calls via the shared template.
|
|
71
|
+
const validityReport = _.cloneDeep(validityReportObject);
|
|
56
72
|
|
|
57
73
|
// Store the inputted details
|
|
58
74
|
validityReport.input.subdomain = subdomain;
|
|
@@ -64,8 +80,25 @@ module.exports.validate = (subdomain, customdomain, subdirectory, prodomain = 'g
|
|
|
64
80
|
// Do a few last DNS-related checks, and then output validity values
|
|
65
81
|
_.merge(validityReport, _.omit(result.dns, 'raw'));
|
|
66
82
|
|
|
83
|
+
if (result.caa) {
|
|
84
|
+
_.merge(validityReport, result.caa);
|
|
85
|
+
|
|
86
|
+
// CAA blocking issuance means SSL can't be provisioned going forward,
|
|
87
|
+
// even if DNS currently points at Ghost — treat it like a DNS failure.
|
|
88
|
+
if (!validityReport.isCaaValid) {
|
|
89
|
+
validityReport.isDnsValidForSSL = false;
|
|
90
|
+
validityReport.sslRating = 'D';
|
|
91
|
+
validityReport.message = 'CAA record does not allow ' + caaIssuer
|
|
92
|
+
+ ' (allowed: ' + (validityReport.caaAllowedIssuers.join(', ') || 'none')
|
|
93
|
+
+ ', source: ' + validityReport.caaSource + ')';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
if (validityReport.isProxy) {
|
|
68
|
-
|
|
98
|
+
const proxyMessage = validityReport.isProxyValid ? 'Found a valid proxy' : 'Found invalid proxy config';
|
|
99
|
+
validityReport.message = validityReport.message
|
|
100
|
+
? validityReport.message + '; ' + proxyMessage
|
|
101
|
+
: proxyMessage;
|
|
69
102
|
}
|
|
70
103
|
|
|
71
104
|
validityReport.raw = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/domain-validator",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "ssh://git@github.com/TryGhost/Pro",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@tryghost/debug": "2.0.3",
|
|
34
|
-
"@tryghost/dns": "^0.
|
|
34
|
+
"@tryghost/dns": "^0.7.0",
|
|
35
35
|
"@tryghost/errors": "3.0.3",
|
|
36
36
|
"@tryghost/pretty-cli": "3.0.3",
|
|
37
37
|
"bluebird": "3.7.2",
|
|
38
38
|
"got": "11.8.6",
|
|
39
39
|
"lodash": "4.17.23"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "c5d1ebb864969de89c1e8a8dcac3f577bceee1dd"
|
|
42
42
|
}
|