@jsenv/https-local 1.0.11 → 2.1.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/README.md +6 -8
- package/package.json +17 -19
- package/src/certificate_authority.js +6 -8
- package/src/certificate_for_localhost.js +34 -38
- package/src/hosts_file_verif.js +1 -2
- package/src/internal/authority_file_infos.js +4 -6
- package/src/internal/certificate_authority_file_urls.js +20 -22
- package/src/internal/certificate_generator.js +2 -4
- package/src/internal/forge.js +1 -3
- package/src/internal/linux/chrome_linux.js +5 -6
- package/src/internal/linux/firefox_linux.js +4 -5
- package/src/internal/linux/linux_trust_store.js +11 -6
- package/src/internal/linux/nss_linux.js +2 -2
- package/src/internal/mac/chrome_mac.js +1 -1
- package/src/internal/mac/firefox_mac.js +3 -3
- package/src/internal/mac/mac_keychain.js +48 -34
- package/src/internal/mac/nss_mac.js +7 -10
- package/src/internal/nssdb_browser.js +21 -26
- package/src/internal/trust_query.js +1 -2
- package/src/internal/windows/chrome_windows.js +1 -1
- package/src/internal/windows/firefox_windows.js +1 -1
- package/src/internal/windows/windows_certutil.js +10 -6
- package/{main.js → src/main.js} +4 -4
- package/src/validity_duration.js +4 -9
package/README.md
CHANGED
|
@@ -67,13 +67,12 @@ node ./install_certificate_authority.mjs
|
|
|
67
67
|
import { createServer } from "node:https"
|
|
68
68
|
import { requestCertificateForLocalhost } from "@jsenv/https-local"
|
|
69
69
|
|
|
70
|
-
const {
|
|
71
|
-
await requestCertificateForLocalhost()
|
|
70
|
+
const { certificate, privateKey } = requestCertificateForLocalhost()
|
|
72
71
|
|
|
73
72
|
const server = createServer(
|
|
74
73
|
{
|
|
75
|
-
cert:
|
|
76
|
-
key:
|
|
74
|
+
cert: certificate,
|
|
75
|
+
key: privateKey,
|
|
77
76
|
},
|
|
78
77
|
(request, response) => {
|
|
79
78
|
const body = "Hello world"
|
|
@@ -361,10 +360,9 @@ _requestCertificateForLocalhost_ function returns a certificate and private key
|
|
|
361
360
|
import { createServer } from "node:https"
|
|
362
361
|
import { requestCertificateForLocalhost } from "@jsenv/https-local"
|
|
363
362
|
|
|
364
|
-
const {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
})
|
|
363
|
+
const { certificate, privateKey } = requestCertificateForLocalhost({
|
|
364
|
+
altNames: ["localhost", "local.example"],
|
|
365
|
+
})
|
|
368
366
|
```
|
|
369
367
|
|
|
370
368
|
[installCertificateAuthority](#installCertificateAuthority) must be called before this function.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/https-local",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A programmatic way to generate locally trusted certificates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"url": "https://github.com/jsenv/https-local"
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
|
-
"access": "public"
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org"
|
|
17
18
|
},
|
|
18
19
|
"engines": {
|
|
19
20
|
"node": ">=16.13.0"
|
|
@@ -21,14 +22,13 @@
|
|
|
21
22
|
"type": "module",
|
|
22
23
|
"exports": {
|
|
23
24
|
".": {
|
|
24
|
-
"import": "./main.js"
|
|
25
|
+
"import": "./src/main.js"
|
|
25
26
|
},
|
|
26
27
|
"./*": "./*"
|
|
27
28
|
},
|
|
28
|
-
"main": "./main.js",
|
|
29
|
+
"main": "./src/main.js",
|
|
29
30
|
"files": [
|
|
30
|
-
"/src/"
|
|
31
|
-
"/main.js"
|
|
31
|
+
"/src/"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
34
|
"eslint": "npx eslint . --ext=.js,.mjs,.cjs",
|
|
@@ -48,27 +48,25 @@
|
|
|
48
48
|
"playwright-install": "npx playwright install-deps && npx playwright install"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@jsenv/
|
|
52
|
-
"@jsenv/
|
|
53
|
-
"@jsenv/
|
|
54
|
-
"@jsenv/logger": "4.0.1",
|
|
51
|
+
"@jsenv/filesystem": "4.1.0",
|
|
52
|
+
"@jsenv/log": "2.0.1",
|
|
53
|
+
"@jsenv/urls": "1.2.6",
|
|
55
54
|
"command-exists": "1.2.9",
|
|
56
55
|
"node-forge": "1.3.1",
|
|
57
56
|
"sudo-prompt": "9.2.1",
|
|
58
57
|
"which": "2.0.2"
|
|
59
58
|
},
|
|
60
59
|
"devDependencies": {
|
|
61
|
-
"@jsenv/assert": "2.
|
|
62
|
-
"@jsenv/core": "27.0.0-alpha.
|
|
60
|
+
"@jsenv/assert": "2.6.0",
|
|
61
|
+
"@jsenv/core": "27.0.0-alpha.92",
|
|
63
62
|
"@jsenv/eslint-config": "16.0.9",
|
|
63
|
+
"@jsenv/eslint-import-resolver": "0.3.0",
|
|
64
64
|
"@jsenv/github-release-package": "1.4.0",
|
|
65
|
-
"@jsenv/
|
|
66
|
-
"@jsenv/
|
|
67
|
-
"
|
|
68
|
-
"@jsenv/performance-impact": "2.2.11",
|
|
69
|
-
"eslint": "8.16.0",
|
|
65
|
+
"@jsenv/package-publish": "1.7.5",
|
|
66
|
+
"@jsenv/performance-impact": "2.3.0",
|
|
67
|
+
"eslint": "8.18.0",
|
|
70
68
|
"eslint-plugin-import": "2.26.0",
|
|
71
69
|
"playwright": "1.22.2",
|
|
72
|
-
"prettier": "2.
|
|
70
|
+
"prettier": "2.7.1"
|
|
73
71
|
}
|
|
74
|
-
}
|
|
72
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { readFile, writeFile, removeEntry } from "@jsenv/filesystem"
|
|
2
|
-
import { createLogger, createDetailedMessage } from "@jsenv/
|
|
3
|
-
import { UNICODE } from "@jsenv/log"
|
|
2
|
+
import { UNICODE, createLogger, createDetailedMessage } from "@jsenv/log"
|
|
4
3
|
|
|
4
|
+
import { forge } from "./internal/forge.js"
|
|
5
5
|
import { getAuthorityFileInfos } from "./internal/authority_file_infos.js"
|
|
6
6
|
import { attributeDescriptionFromAttributeArray } from "./internal/certificate_data_converter.js"
|
|
7
7
|
import {
|
|
8
8
|
formatTimeDelta,
|
|
9
9
|
formatDuration,
|
|
10
10
|
} from "./internal/validity_formatting.js"
|
|
11
|
-
import { importNodeForge } from "./internal/forge.js"
|
|
12
11
|
import { createAuthorityRootCertificate } from "./internal/certificate_generator.js"
|
|
13
12
|
import { importPlatformMethods } from "./internal/platform.js"
|
|
14
13
|
import { jsenvParameters } from "./jsenvParameters.js"
|
|
@@ -79,7 +78,7 @@ export const installCertificateAuthority = async ({
|
|
|
79
78
|
serialNumber: 0,
|
|
80
79
|
})
|
|
81
80
|
|
|
82
|
-
const { pki } =
|
|
81
|
+
const { pki } = forge
|
|
83
82
|
const rootCertificate = pemAsFileContent(
|
|
84
83
|
pki.certificateToPem(rootCertificateForgeObject),
|
|
85
84
|
)
|
|
@@ -173,7 +172,7 @@ export const installCertificateAuthority = async ({
|
|
|
173
172
|
const rootCertificate = await readFile(rootCertificateFileInfo.path, {
|
|
174
173
|
as: "string",
|
|
175
174
|
})
|
|
176
|
-
const { pki } =
|
|
175
|
+
const { pki } = forge
|
|
177
176
|
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
178
177
|
|
|
179
178
|
logger.info(`Checking certificate validity...`)
|
|
@@ -232,13 +231,12 @@ export const installCertificateAuthority = async ({
|
|
|
232
231
|
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
233
232
|
rootCertificatePrivateKey,
|
|
234
233
|
)
|
|
235
|
-
|
|
236
234
|
const trustInfo = await platformMethods.executeTrustQuery({
|
|
237
235
|
logger,
|
|
238
236
|
certificateCommonName,
|
|
239
237
|
certificateFileUrl: rootCertificateFileInfo.url,
|
|
240
238
|
certificate: rootCertificate,
|
|
241
|
-
verb: tryToTrust ? "
|
|
239
|
+
verb: tryToTrust ? "ENSURE_TRUST" : "CHECK_TRUST",
|
|
242
240
|
NSSDynamicInstall,
|
|
243
241
|
})
|
|
244
242
|
|
|
@@ -323,7 +321,7 @@ export const uninstallCertificateAuthority = async ({
|
|
|
323
321
|
const rootCertificate = await readFile(rootCertificateFileInfo.url, {
|
|
324
322
|
as: "string",
|
|
325
323
|
})
|
|
326
|
-
const { pki } =
|
|
324
|
+
const { pki } = forge
|
|
327
325
|
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
328
326
|
const rootCertificateCommonName = attributeDescriptionFromAttributeArray(
|
|
329
327
|
rootCertificateForgeObject.subject.attributes,
|
|
@@ -1,39 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { UNICODE } from "@jsenv/log"
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import { writeFileSync } from "@jsenv/filesystem"
|
|
3
|
+
import { UNICODE, createLogger, createDetailedMessage } from "@jsenv/log"
|
|
4
4
|
|
|
5
|
+
import { forge } from "./internal/forge.js"
|
|
5
6
|
import {
|
|
6
7
|
createValidityDurationOfXDays,
|
|
7
8
|
verifyServerCertificateValidityDuration,
|
|
8
9
|
} from "./validity_duration.js"
|
|
9
10
|
import { getAuthorityFileInfos } from "./internal/authority_file_infos.js"
|
|
10
|
-
import { importNodeForge } from "./internal/forge.js"
|
|
11
11
|
import { requestCertificateFromAuthority } from "./internal/certificate_generator.js"
|
|
12
12
|
import { formatDuration } from "./internal/validity_formatting.js"
|
|
13
13
|
|
|
14
|
-
export const requestCertificateForLocalhost =
|
|
14
|
+
export const requestCertificateForLocalhost = ({
|
|
15
15
|
logLevel,
|
|
16
16
|
logger = createLogger({ logLevel }), // to be able to catch logs during unit tests
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
altNames = ["localhost"],
|
|
19
|
+
commonName = "https local server certificate",
|
|
20
|
+
validityDurationInMs = createValidityDurationOfXDays(396),
|
|
21
21
|
} = {}) => {
|
|
22
|
-
if (typeof
|
|
22
|
+
if (typeof validityDurationInMs !== "number") {
|
|
23
23
|
throw new TypeError(
|
|
24
|
-
`
|
|
24
|
+
`validityDurationInMs must be a number but received ${validityDurationInMs}`,
|
|
25
25
|
)
|
|
26
26
|
}
|
|
27
|
-
if (
|
|
27
|
+
if (validityDurationInMs < 1) {
|
|
28
28
|
throw new TypeError(
|
|
29
|
-
`
|
|
29
|
+
`validityDurationInMs must be > 0 but received ${validityDurationInMs}`,
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
|
-
const validityDurationInfo =
|
|
33
|
-
|
|
34
|
-
)
|
|
32
|
+
const validityDurationInfo =
|
|
33
|
+
verifyServerCertificateValidityDuration(validityDurationInMs)
|
|
35
34
|
if (!validityDurationInfo.ok) {
|
|
36
|
-
|
|
35
|
+
validityDurationInMs = validityDurationInfo.maxAllowedValue
|
|
37
36
|
logger.warn(
|
|
38
37
|
createDetailedMessage(validityDurationInfo.message, {
|
|
39
38
|
details: validityDurationInfo.details,
|
|
@@ -59,19 +58,16 @@ export const requestCertificateForLocalhost = async ({
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
logger.debug(`Restoring certificate authority from filesystem...`)
|
|
62
|
-
const { pki } =
|
|
63
|
-
const rootCertificate =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const rootCertificatePrivateKey =
|
|
67
|
-
rootCertificatePrivateKeyFileInfo.url,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const { pki } = forge
|
|
62
|
+
const rootCertificate = String(
|
|
63
|
+
readFileSync(new URL(rootCertificateFileInfo.url)),
|
|
64
|
+
)
|
|
65
|
+
const rootCertificatePrivateKey = String(
|
|
66
|
+
readFileSync(new URL(rootCertificatePrivateKeyFileInfo.url)),
|
|
67
|
+
)
|
|
68
|
+
const certificateAuthorityData = JSON.parse(
|
|
69
|
+
String(readFileSync(new URL(authorityJsonFileInfo.url))),
|
|
71
70
|
)
|
|
72
|
-
const certificateAuthorityData = await readFile(authorityJsonFileInfo.url, {
|
|
73
|
-
as: "json",
|
|
74
|
-
})
|
|
75
71
|
const rootCertificateForgeObject = pki.certificateFromPem(rootCertificate)
|
|
76
72
|
const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
|
|
77
73
|
rootCertificatePrivateKey,
|
|
@@ -80,26 +76,26 @@ export const requestCertificateForLocalhost = async ({
|
|
|
80
76
|
|
|
81
77
|
const serverCertificateSerialNumber =
|
|
82
78
|
certificateAuthorityData.serialNumber + 1
|
|
83
|
-
|
|
79
|
+
writeFileSync(
|
|
84
80
|
authorityJsonFileInfo.url,
|
|
85
81
|
JSON.stringify({ serialNumber: serverCertificateSerialNumber }, null, " "),
|
|
86
82
|
)
|
|
87
83
|
|
|
88
|
-
if (!
|
|
89
|
-
|
|
84
|
+
if (!altNames.includes("localhost")) {
|
|
85
|
+
altNames.push("localhost")
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
logger.debug(`Generating server certificate...`)
|
|
93
89
|
const { certificateForgeObject, certificatePrivateKeyForgeObject } =
|
|
94
|
-
|
|
90
|
+
requestCertificateFromAuthority({
|
|
95
91
|
logger,
|
|
96
92
|
authorityCertificateForgeObject: rootCertificateForgeObject,
|
|
97
93
|
auhtorityCertificatePrivateKeyForgeObject:
|
|
98
94
|
rootCertificatePrivateKeyForgeObject,
|
|
99
95
|
serialNumber: serverCertificateSerialNumber,
|
|
100
|
-
altNames
|
|
101
|
-
commonName
|
|
102
|
-
validityDurationInMs
|
|
96
|
+
altNames,
|
|
97
|
+
commonName,
|
|
98
|
+
validityDurationInMs,
|
|
103
99
|
})
|
|
104
100
|
const serverCertificate = pki.certificateToPem(certificateForgeObject)
|
|
105
101
|
const serverCertificatePrivateKey = pki.privateKeyToPem(
|
|
@@ -109,13 +105,13 @@ export const requestCertificateForLocalhost = async ({
|
|
|
109
105
|
`${
|
|
110
106
|
UNICODE.OK
|
|
111
107
|
} server certificate generated, it will be valid for ${formatDuration(
|
|
112
|
-
|
|
108
|
+
validityDurationInMs,
|
|
113
109
|
)}`,
|
|
114
110
|
)
|
|
115
111
|
|
|
116
112
|
return {
|
|
117
|
-
serverCertificate,
|
|
118
|
-
serverCertificatePrivateKey,
|
|
113
|
+
certificate: serverCertificate,
|
|
114
|
+
privateKey: serverCertificatePrivateKey,
|
|
119
115
|
rootCertificateFilePath: rootCertificateFileInfo.path,
|
|
120
116
|
}
|
|
121
117
|
}
|
package/src/hosts_file_verif.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
|
-
import {
|
|
2
|
+
import { fileURLToPath } from "node:url"
|
|
3
3
|
|
|
4
4
|
import { getCertificateAuthorityFileUrls } from "./certificate_authority_file_urls.js"
|
|
5
5
|
|
|
@@ -10,15 +10,13 @@ export const getAuthorityFileInfos = () => {
|
|
|
10
10
|
rootCertificatePrivateKeyFileUrl,
|
|
11
11
|
} = getCertificateAuthorityFileUrls()
|
|
12
12
|
|
|
13
|
-
const authorityJsonFilePath =
|
|
14
|
-
certificateAuthorityJsonFileUrl,
|
|
15
|
-
)
|
|
13
|
+
const authorityJsonFilePath = fileURLToPath(certificateAuthorityJsonFileUrl)
|
|
16
14
|
const authorityJsonFileDetected = existsSync(authorityJsonFilePath)
|
|
17
15
|
|
|
18
|
-
const rootCertificateFilePath =
|
|
16
|
+
const rootCertificateFilePath = fileURLToPath(rootCertificateFileUrl)
|
|
19
17
|
const rootCertificateFileDetected = existsSync(rootCertificateFilePath)
|
|
20
18
|
|
|
21
|
-
const rootCertificatePrivateKeyFilePath =
|
|
19
|
+
const rootCertificatePrivateKeyFilePath = fileURLToPath(
|
|
22
20
|
rootCertificatePrivateKeyFileUrl,
|
|
23
21
|
)
|
|
24
22
|
const rootCertificatePrivateKeyFileDetected = existsSync(
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
resolveUrl,
|
|
4
|
-
urlToFilename,
|
|
5
|
-
} from "@jsenv/filesystem"
|
|
1
|
+
import { urlToFilename } from "@jsenv/urls"
|
|
2
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
6
3
|
|
|
7
4
|
export const getCertificateAuthorityFileUrls = () => {
|
|
8
5
|
// we need a directory common to every instance of @jsenv/https-local
|
|
@@ -20,10 +17,10 @@ export const getCertificateAuthorityFileUrls = () => {
|
|
|
20
17
|
applicationDirectoryUrl,
|
|
21
18
|
)
|
|
22
19
|
|
|
23
|
-
const rootCertificatePrivateKeyFileUrl =
|
|
20
|
+
const rootCertificatePrivateKeyFileUrl = new URL(
|
|
24
21
|
"./https_local_root_certificate.key",
|
|
25
22
|
applicationDirectoryUrl,
|
|
26
|
-
)
|
|
23
|
+
).href
|
|
27
24
|
|
|
28
25
|
return {
|
|
29
26
|
certificateAuthorityJsonFileUrl,
|
|
@@ -37,18 +34,19 @@ export const getRootCertificateSymlinkUrls = ({
|
|
|
37
34
|
rootPrivateKeyFileUrl,
|
|
38
35
|
serverCertificateFileUrl,
|
|
39
36
|
}) => {
|
|
40
|
-
const serverCertificateDirectory =
|
|
37
|
+
const serverCertificateDirectory = new URL("./", serverCertificateFileUrl)
|
|
38
|
+
.href
|
|
41
39
|
|
|
42
40
|
const rootCertificateFilename = urlToFilename(rootCertificateFileUrl)
|
|
43
|
-
const rootCertificateSymlinkUrl =
|
|
41
|
+
const rootCertificateSymlinkUrl = new URL(
|
|
44
42
|
rootCertificateFilename,
|
|
45
43
|
serverCertificateDirectory,
|
|
46
|
-
)
|
|
44
|
+
).href
|
|
47
45
|
const rootPrivateKeyFilename = urlToFilename(rootPrivateKeyFileUrl)
|
|
48
|
-
const rootPrivateKeySymlinkUrl =
|
|
46
|
+
const rootPrivateKeySymlinkUrl = new URL(
|
|
49
47
|
rootPrivateKeyFilename,
|
|
50
48
|
serverCertificateDirectory,
|
|
51
|
-
)
|
|
49
|
+
).href
|
|
52
50
|
|
|
53
51
|
return {
|
|
54
52
|
rootCertificateSymlinkUrl,
|
|
@@ -61,37 +59,37 @@ const getJsenvApplicationDirectoryUrl = () => {
|
|
|
61
59
|
const { platform } = process
|
|
62
60
|
|
|
63
61
|
if (platform === "darwin") {
|
|
64
|
-
return
|
|
62
|
+
return new URL(
|
|
65
63
|
`./Library/Application Support/https_local/`,
|
|
66
64
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
67
|
-
)
|
|
65
|
+
).href
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
if (platform === "linux") {
|
|
71
69
|
if (process.env.XDG_CONFIG_HOME) {
|
|
72
|
-
return
|
|
70
|
+
return new URL(
|
|
73
71
|
`./https_local/`,
|
|
74
72
|
assertAndNormalizeDirectoryUrl(process.env.XDG_CONFIG_HOME),
|
|
75
|
-
)
|
|
73
|
+
).href
|
|
76
74
|
}
|
|
77
|
-
return
|
|
75
|
+
return new URL(
|
|
78
76
|
`./.config/https_local/`,
|
|
79
77
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
80
|
-
)
|
|
78
|
+
).href
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
if (platform === "win32") {
|
|
84
82
|
if (process.env.LOCALAPPDATA) {
|
|
85
|
-
return
|
|
83
|
+
return new URL(
|
|
86
84
|
`./https_local/`,
|
|
87
85
|
assertAndNormalizeDirectoryUrl(process.env.LOCALAPPDATA),
|
|
88
|
-
)
|
|
86
|
+
).href
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
return
|
|
89
|
+
return new URL(
|
|
92
90
|
`./Local Settings/Application Data/https_local/`,
|
|
93
91
|
assertAndNormalizeDirectoryUrl(process.env.USERPROFILE),
|
|
94
|
-
)
|
|
92
|
+
).href
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
throw new Error(`platform not supported`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// https://github.com/digitalbazaar/forge/blob/master/examples/create-cert.js
|
|
2
2
|
// https://github.com/digitalbazaar/forge/issues/660#issuecomment-467145103
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { forge } from "./forge.js"
|
|
5
5
|
import {
|
|
6
6
|
attributeArrayFromAttributeDescription,
|
|
7
7
|
attributeDescriptionFromAttributeArray,
|
|
@@ -23,7 +23,6 @@ export const createAuthorityRootCertificate = async ({
|
|
|
23
23
|
throw new TypeError(`serial must be a number but received ${serialNumber}`)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const forge = await importNodeForge()
|
|
27
26
|
const { pki } = forge
|
|
28
27
|
const rootCertificateForgeObject = pki.createCertificate()
|
|
29
28
|
const keyPair = pki.rsa.generateKeyPair(2048) // TODO: use async version https://github.com/digitalbazaar/forge#rsa
|
|
@@ -86,7 +85,7 @@ export const createAuthorityRootCertificate = async ({
|
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
export const requestCertificateFromAuthority =
|
|
88
|
+
export const requestCertificateFromAuthority = ({
|
|
90
89
|
authorityCertificateForgeObject, // could be intermediate or root certificate authority
|
|
91
90
|
auhtorityCertificatePrivateKeyForgeObject,
|
|
92
91
|
serialNumber,
|
|
@@ -116,7 +115,6 @@ export const requestCertificateFromAuthority = async ({
|
|
|
116
115
|
)
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
const forge = await importNodeForge()
|
|
120
118
|
const { pki } = forge
|
|
121
119
|
const certificateForgeObject = pki.createCertificate()
|
|
122
120
|
const keyPair = pki.rsa.generateKeyPair(2048) // TODO: use async version https://github.com/digitalbazaar/forge#rsa
|
package/src/internal/forge.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
2
|
import { execSync } from "node:child_process"
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
5
4
|
import { UNICODE } from "@jsenv/log"
|
|
5
|
+
|
|
6
|
+
import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js"
|
|
6
7
|
import {
|
|
7
8
|
nssCommandName,
|
|
8
9
|
detectIfNSSIsInstalled,
|
|
@@ -10,8 +11,6 @@ import {
|
|
|
10
11
|
getCertutilBinPath,
|
|
11
12
|
} from "./nss_linux.js"
|
|
12
13
|
|
|
13
|
-
import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js"
|
|
14
|
-
|
|
15
14
|
export const executeTrustQueryOnChrome = ({
|
|
16
15
|
logger,
|
|
17
16
|
certificateCommonName,
|
|
@@ -48,10 +47,10 @@ export const executeTrustQueryOnChrome = ({
|
|
|
48
47
|
logger.debug(`${UNICODE.INFO} Chrome not detected`)
|
|
49
48
|
return false
|
|
50
49
|
},
|
|
51
|
-
browserNSSDBDirectoryUrl:
|
|
50
|
+
browserNSSDBDirectoryUrl: new URL(
|
|
52
51
|
".pki/nssdb",
|
|
53
52
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
54
|
-
),
|
|
53
|
+
).href,
|
|
55
54
|
getBrowserClosedPromise: async () => {
|
|
56
55
|
if (!isChromeOpen()) {
|
|
57
56
|
return
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
2
|
import { execSync } from "node:child_process"
|
|
3
|
-
import {
|
|
3
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
4
4
|
import { UNICODE } from "@jsenv/log"
|
|
5
5
|
|
|
6
|
+
import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js"
|
|
6
7
|
import {
|
|
7
8
|
nssCommandName,
|
|
8
9
|
detectIfNSSIsInstalled,
|
|
@@ -10,8 +11,6 @@ import {
|
|
|
10
11
|
getCertutilBinPath,
|
|
11
12
|
} from "./nss_linux.js"
|
|
12
13
|
|
|
13
|
-
import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js"
|
|
14
|
-
|
|
15
14
|
export const executeTrustQueryOnFirefox = ({
|
|
16
15
|
logger,
|
|
17
16
|
certificateCommonName,
|
|
@@ -48,10 +47,10 @@ export const executeTrustQueryOnFirefox = ({
|
|
|
48
47
|
logger.debug(`${UNICODE.INFO} Firefox not detected`)
|
|
49
48
|
return false
|
|
50
49
|
},
|
|
51
|
-
browserNSSDBDirectoryUrl:
|
|
50
|
+
browserNSSDBDirectoryUrl: new URL(
|
|
52
51
|
".mozilla/firefox/",
|
|
53
52
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
54
|
-
),
|
|
53
|
+
).href,
|
|
55
54
|
getBrowserClosedPromise: async () => {
|
|
56
55
|
if (!isFirefoxOpen()) {
|
|
57
56
|
return
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { existsSync } from "node:fs"
|
|
6
|
-
import {
|
|
7
|
-
import { readFile
|
|
8
|
-
import { UNICODE } from "@jsenv/log"
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
import { readFile } from "@jsenv/filesystem"
|
|
8
|
+
import { createDetailedMessage, UNICODE } from "@jsenv/log"
|
|
9
9
|
|
|
10
|
-
import { exec } from "@jsenv/https-local/src/internal/exec.js"
|
|
11
10
|
import {
|
|
12
11
|
VERB_CHECK_TRUST,
|
|
13
12
|
VERB_ADD_TRUST,
|
|
13
|
+
VERB_ENSURE_TRUST,
|
|
14
14
|
VERB_REMOVE_TRUST,
|
|
15
15
|
} from "../trust_query.js"
|
|
16
|
+
import { exec } from "../exec.js"
|
|
16
17
|
|
|
17
18
|
const REASON_NEW_AND_TRY_TO_TRUST_DISABLED =
|
|
18
19
|
"certificate is new and tryToTrust is disabled"
|
|
@@ -50,7 +51,7 @@ export const executeTrustQueryOnLinux = async ({
|
|
|
50
51
|
logger.debug(
|
|
51
52
|
`Searching certificate file at ${JSENV_AUTHORITY_ROOT_CERTIFICATE_PATH}...`,
|
|
52
53
|
)
|
|
53
|
-
const certificateFilePath =
|
|
54
|
+
const certificateFilePath = fileURLToPath(certificateFileUrl)
|
|
54
55
|
const certificateStatus = await getCertificateStatus({ certificate })
|
|
55
56
|
|
|
56
57
|
if (certificateStatus === "missing" || certificateStatus === "outdated") {
|
|
@@ -100,7 +101,11 @@ export const executeTrustQueryOnLinux = async ({
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
logger.info(`${UNICODE.OK} certificate found in linux`)
|
|
103
|
-
if (
|
|
104
|
+
if (
|
|
105
|
+
verb === VERB_CHECK_TRUST ||
|
|
106
|
+
verb === VERB_ADD_TRUST ||
|
|
107
|
+
verb === VERB_ENSURE_TRUST
|
|
108
|
+
) {
|
|
104
109
|
return {
|
|
105
110
|
status: "trusted",
|
|
106
111
|
reason: REASON_FOUND_IN_LINUX,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UNICODE } from "@jsenv/log"
|
|
2
2
|
|
|
3
|
-
import { memoize } from "
|
|
4
|
-
import { exec } from "
|
|
3
|
+
import { memoize } from "../memoize.js"
|
|
4
|
+
import { exec } from "../exec.js"
|
|
5
5
|
|
|
6
6
|
export const nssCommandName = "libnss3-tools"
|
|
7
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs"
|
|
2
2
|
import { execSync } from "node:child_process"
|
|
3
|
-
import {
|
|
3
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
4
4
|
import { UNICODE, createTaskLog } from "@jsenv/log"
|
|
5
5
|
|
|
6
6
|
import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js"
|
|
@@ -47,10 +47,10 @@ export const executeTrustQueryOnFirefox = ({
|
|
|
47
47
|
logger.debug(`${UNICODE.INFO} firefox not detected`)
|
|
48
48
|
return false
|
|
49
49
|
},
|
|
50
|
-
browserNSSDBDirectoryUrl:
|
|
50
|
+
browserNSSDBDirectoryUrl: new URL(
|
|
51
51
|
`./Library/Application Support/Firefox/Profiles/`,
|
|
52
52
|
assertAndNormalizeDirectoryUrl(process.env.HOME),
|
|
53
|
-
),
|
|
53
|
+
).href,
|
|
54
54
|
getBrowserClosedPromise: async () => {
|
|
55
55
|
if (!isFirefoxOpen()) {
|
|
56
56
|
return
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// https://ss64.com/osx/security.html
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { createDetailedMessage } from "@jsenv/
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { createDetailedMessage, UNICODE } from "@jsenv/log"
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { searchCertificateInCommandOutput } from "@jsenv/https-local/src/internal/search_certificate_in_command_output.js"
|
|
6
|
+
import { exec } from "../exec.js"
|
|
7
|
+
import { searchCertificateInCommandOutput } from "../search_certificate_in_command_output.js"
|
|
9
8
|
import {
|
|
10
9
|
VERB_CHECK_TRUST,
|
|
11
10
|
VERB_ADD_TRUST,
|
|
11
|
+
VERB_ENSURE_TRUST,
|
|
12
12
|
VERB_REMOVE_TRUST,
|
|
13
13
|
} from "../trust_query.js"
|
|
14
14
|
|
|
@@ -53,6 +53,35 @@ export const executeTrustQueryOnMacKeychain = async ({
|
|
|
53
53
|
certificate,
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
+
const removeCert = async () => {
|
|
57
|
+
// https://ss64.com/osx/security-delete-cert.html
|
|
58
|
+
const removeTrustedCertCommand = `sudo security delete-certificate -c "${certificateCommonName}"`
|
|
59
|
+
logger.info(`Removing certificate from mac keychain...`)
|
|
60
|
+
logger.info(`${UNICODE.COMMAND} ${removeTrustedCertCommand}`)
|
|
61
|
+
try {
|
|
62
|
+
await exec(removeTrustedCertCommand)
|
|
63
|
+
logger.info(`${UNICODE.OK} certificate removed from mac keychain`)
|
|
64
|
+
return {
|
|
65
|
+
status: "not_trusted",
|
|
66
|
+
reason: REASON_REMOVE_FROM_KEYCHAIN_COMMAND_COMPLETED,
|
|
67
|
+
}
|
|
68
|
+
} catch (e) {
|
|
69
|
+
logger.error(
|
|
70
|
+
createDetailedMessage(
|
|
71
|
+
`${UNICODE.FAILURE} failed to remove certificate from mac keychain`,
|
|
72
|
+
{
|
|
73
|
+
"error stack": e.stack,
|
|
74
|
+
"certificate file url": certificateFileUrl,
|
|
75
|
+
},
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
return {
|
|
79
|
+
status: "not_trusted",
|
|
80
|
+
reason: REASON_REMOVE_FROM_KEYCHAIN_COMMAND_FAILED,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
56
85
|
if (!certificateFoundInCommandOutput) {
|
|
57
86
|
logger.info(`${UNICODE.INFO} certificate not found in mac keychain`)
|
|
58
87
|
if (verb === VERB_CHECK_TRUST || verb === VERB_REMOVE_TRUST) {
|
|
@@ -61,8 +90,14 @@ export const executeTrustQueryOnMacKeychain = async ({
|
|
|
61
90
|
reason: REASON_NOT_IN_KEYCHAIN,
|
|
62
91
|
}
|
|
63
92
|
}
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
if (verb === VERB_ENSURE_TRUST) {
|
|
94
|
+
// It seems possible for certificate PEM representation to be different
|
|
95
|
+
// in mackeychain and in the one we have written on the filesystem
|
|
96
|
+
// When it happens the certificate is not found but actually exists on mackeychain
|
|
97
|
+
// and must be deleted first
|
|
98
|
+
await removeCert()
|
|
99
|
+
}
|
|
100
|
+
const certificateFilePath = fileURLToPath(certificateFileUrl)
|
|
66
101
|
// https://ss64.com/osx/security-cert.html
|
|
67
102
|
const addTrustedCertCommand = `sudo security add-trusted-cert -d -r trustRoot -k ${systemKeychainPath} "${certificateFilePath}"`
|
|
68
103
|
logger.info(`Adding certificate to mac keychain...`)
|
|
@@ -96,37 +131,16 @@ export const executeTrustQueryOnMacKeychain = async ({
|
|
|
96
131
|
// but they shouldn't and I couldn't find an API to know if the cert is trusted or not
|
|
97
132
|
// just if it's in the keychain
|
|
98
133
|
logger.info(`${UNICODE.OK} certificate found in mac keychain`)
|
|
99
|
-
if (
|
|
134
|
+
if (
|
|
135
|
+
verb === VERB_CHECK_TRUST ||
|
|
136
|
+
verb === VERB_ADD_TRUST ||
|
|
137
|
+
verb === VERB_ENSURE_TRUST
|
|
138
|
+
) {
|
|
100
139
|
return {
|
|
101
140
|
status: "trusted",
|
|
102
141
|
reason: REASON_IN_KEYCHAIN,
|
|
103
142
|
}
|
|
104
143
|
}
|
|
105
144
|
|
|
106
|
-
|
|
107
|
-
const removeTrustedCertCommand = `sudo security delete-certificate -c "${certificateCommonName}"`
|
|
108
|
-
logger.info(`Removing certificate from mac keychain...`)
|
|
109
|
-
logger.info(`${UNICODE.COMMAND} ${removeTrustedCertCommand}`)
|
|
110
|
-
try {
|
|
111
|
-
await exec(removeTrustedCertCommand)
|
|
112
|
-
logger.info(`${UNICODE.OK} certificate removed from mac keychain`)
|
|
113
|
-
return {
|
|
114
|
-
status: "not_trusted",
|
|
115
|
-
reason: REASON_REMOVE_FROM_KEYCHAIN_COMMAND_COMPLETED,
|
|
116
|
-
}
|
|
117
|
-
} catch (e) {
|
|
118
|
-
logger.error(
|
|
119
|
-
createDetailedMessage(
|
|
120
|
-
`${UNICODE.FAILURE} failed to remove certificate from mac keychain`,
|
|
121
|
-
{
|
|
122
|
-
"error stack": e.stack,
|
|
123
|
-
"certificate file url": certificateFileUrl,
|
|
124
|
-
},
|
|
125
|
-
),
|
|
126
|
-
)
|
|
127
|
-
return {
|
|
128
|
-
status: "not_trusted",
|
|
129
|
-
reason: REASON_REMOVE_FROM_KEYCHAIN_COMMAND_FAILED,
|
|
130
|
-
}
|
|
131
|
-
}
|
|
145
|
+
return removeCert()
|
|
132
146
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
resolveUrl,
|
|
4
|
-
urlToFileSystemPath,
|
|
5
|
-
} from "@jsenv/filesystem"
|
|
1
|
+
import { fileURLToPath } from "node:url"
|
|
2
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
6
3
|
import { UNICODE } from "@jsenv/log"
|
|
7
4
|
|
|
8
|
-
import { memoize } from "
|
|
9
|
-
import { exec } from "
|
|
10
|
-
import { commandExists } from "
|
|
5
|
+
import { memoize } from "../memoize.js"
|
|
6
|
+
import { exec } from "../exec.js"
|
|
7
|
+
import { commandExists } from "../command.js"
|
|
11
8
|
|
|
12
9
|
export const nssCommandName = "nss"
|
|
13
10
|
|
|
@@ -31,8 +28,8 @@ export const getCertutilBinPath = memoize(async () => {
|
|
|
31
28
|
const nssCommandDirectoryUrl = assertAndNormalizeDirectoryUrl(
|
|
32
29
|
brewCommandOutput.trim(),
|
|
33
30
|
)
|
|
34
|
-
const certutilBinUrl =
|
|
35
|
-
const certutilBinPath =
|
|
31
|
+
const certutilBinUrl = new URL(`./bin/certutil`, nssCommandDirectoryUrl).href
|
|
32
|
+
const certutilBinPath = fileURLToPath(certutilBinUrl)
|
|
36
33
|
return certutilBinPath
|
|
37
34
|
})
|
|
38
35
|
|
|
@@ -4,19 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { existsSync } from "node:fs"
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
resolveUrl,
|
|
12
|
-
urlToFilename,
|
|
13
|
-
urlToFileSystemPath,
|
|
14
|
-
} from "@jsenv/filesystem"
|
|
15
|
-
import { UNICODE } from "@jsenv/log"
|
|
7
|
+
import { fileURLToPath } from "node:url"
|
|
8
|
+
import { urlToFilename } from "@jsenv/urls"
|
|
9
|
+
import { createDetailedMessage, UNICODE } from "@jsenv/log"
|
|
10
|
+
import { assertAndNormalizeDirectoryUrl, collectFiles } from "@jsenv/filesystem"
|
|
16
11
|
|
|
17
|
-
import { exec } from "
|
|
18
|
-
import { searchCertificateInCommandOutput } from "
|
|
19
|
-
import {
|
|
12
|
+
import { exec } from "./exec.js"
|
|
13
|
+
import { searchCertificateInCommandOutput } from "./search_certificate_in_command_output.js"
|
|
14
|
+
import {
|
|
15
|
+
VERB_CHECK_TRUST,
|
|
16
|
+
VERB_ADD_TRUST,
|
|
17
|
+
VERB_ENSURE_TRUST,
|
|
18
|
+
} from "./trust_query.js"
|
|
20
19
|
|
|
21
20
|
export const executeTrustQueryOnBrowserNSSDB = async ({
|
|
22
21
|
logger,
|
|
@@ -57,7 +56,7 @@ export const executeTrustQueryOnBrowserNSSDB = async ({
|
|
|
57
56
|
const nssIsInstalled = await detectIfNSSIsInstalled({ logger })
|
|
58
57
|
const cannotCheckMessage = `${UNICODE.FAILURE} cannot check if certificate is in ${browserName}`
|
|
59
58
|
if (!nssIsInstalled) {
|
|
60
|
-
if (verb === VERB_ADD_TRUST) {
|
|
59
|
+
if (verb === VERB_ADD_TRUST || verb === VERB_ENSURE_TRUST) {
|
|
61
60
|
const nssDynamicInstallInfo = await getNSSDynamicInstallInfo({ logger })
|
|
62
61
|
if (!nssDynamicInstallInfo.isInstallable) {
|
|
63
62
|
const reason = `"${nssCommandName}" is not installed and not cannot be installed`
|
|
@@ -131,7 +130,7 @@ export const executeTrustQueryOnBrowserNSSDB = async ({
|
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
const certificateFilePath =
|
|
133
|
+
const certificateFilePath = fileURLToPath(certificateFileUrl)
|
|
135
134
|
const certutilBinPath = await getCertutilBinPath()
|
|
136
135
|
|
|
137
136
|
const checkNSSDB = async ({ NSSDBFileUrl }) => {
|
|
@@ -214,7 +213,7 @@ export const executeTrustQueryOnBrowserNSSDB = async ({
|
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
215
|
|
|
217
|
-
if (verb === VERB_ADD_TRUST) {
|
|
216
|
+
if (verb === VERB_ADD_TRUST || verb === VERB_ENSURE_TRUST) {
|
|
218
217
|
if (missingCount === 0 && outdatedCount === 0) {
|
|
219
218
|
logger.info(`${UNICODE.OK} certificate found in ${browserName}`)
|
|
220
219
|
return {
|
|
@@ -288,7 +287,7 @@ const findNSSDBFiles = async ({ logger, NSSDBDirectoryUrl }) => {
|
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
logger.debug(`Searching nss database files in directory...`)
|
|
291
|
-
const NSSDBDirectoryPath =
|
|
290
|
+
const NSSDBDirectoryPath = fileURLToPath(NSSDBDirectoryUrl)
|
|
292
291
|
const NSSDBDirectoryExists = existsSync(NSSDBDirectoryPath)
|
|
293
292
|
if (!NSSDBDirectoryExists) {
|
|
294
293
|
logger.info(
|
|
@@ -300,13 +299,9 @@ const findNSSDBFiles = async ({ logger, NSSDBDirectoryUrl }) => {
|
|
|
300
299
|
NSSDBDirectoryUrl = assertAndNormalizeDirectoryUrl(NSSDBDirectoryUrl)
|
|
301
300
|
const NSSDBFiles = await collectFiles({
|
|
302
301
|
directoryUrl: NSSDBDirectoryUrl,
|
|
303
|
-
|
|
304
|
-
isLegacyNSSDB: {
|
|
305
|
-
|
|
306
|
-
},
|
|
307
|
-
isModernNSSDB: {
|
|
308
|
-
"./**/cert9.db": true,
|
|
309
|
-
},
|
|
302
|
+
associations: {
|
|
303
|
+
isLegacyNSSDB: { "./**/cert8.db": true },
|
|
304
|
+
isModernNSSDB: { "./**/cert9.db": true },
|
|
310
305
|
},
|
|
311
306
|
predicate: ({ isLegacyNSSDB, isModernNSSDB }) =>
|
|
312
307
|
isLegacyNSSDB || isModernNSSDB,
|
|
@@ -324,7 +319,7 @@ const findNSSDBFiles = async ({ logger, NSSDBDirectoryUrl }) => {
|
|
|
324
319
|
`${UNICODE.OK} found ${fileCount} nss database file in ${NSSDBDirectoryUrl}`,
|
|
325
320
|
)
|
|
326
321
|
const files = NSSDBFiles.map((file) => {
|
|
327
|
-
return
|
|
322
|
+
return new URL(file.relativeUrl, NSSDBDirectoryUrl).href
|
|
328
323
|
})
|
|
329
324
|
NSSDirectoryCache[NSSDBDirectoryUrl] = files
|
|
330
325
|
return files
|
|
@@ -332,8 +327,8 @@ const findNSSDBFiles = async ({ logger, NSSDBDirectoryUrl }) => {
|
|
|
332
327
|
|
|
333
328
|
const getDirectoryArgFromNSSDBFileUrl = (NSSDBFileUrl) => {
|
|
334
329
|
const nssDBFilename = urlToFilename(NSSDBFileUrl)
|
|
335
|
-
const nssDBDirectoryUrl =
|
|
336
|
-
const nssDBDirectoryPath =
|
|
330
|
+
const nssDBDirectoryUrl = new URL("./", NSSDBFileUrl).href
|
|
331
|
+
const nssDBDirectoryPath = fileURLToPath(nssDBDirectoryUrl)
|
|
337
332
|
return nssDBFilename === "cert8.db"
|
|
338
333
|
? `"${nssDBDirectoryPath}"`
|
|
339
334
|
: `sql:"${nssDBDirectoryPath}"`
|
|
@@ -2,7 +2,7 @@ import { createRequire } from "node:module"
|
|
|
2
2
|
import { existsSync } from "node:fs"
|
|
3
3
|
import { UNICODE } from "@jsenv/log"
|
|
4
4
|
|
|
5
|
-
import { memoize } from "
|
|
5
|
+
import { memoize } from "../memoize.js"
|
|
6
6
|
|
|
7
7
|
const require = createRequire(import.meta.url)
|
|
8
8
|
|
|
@@ -7,7 +7,7 @@ import { createRequire } from "node:module"
|
|
|
7
7
|
import { existsSync } from "node:fs"
|
|
8
8
|
import { UNICODE } from "@jsenv/log"
|
|
9
9
|
|
|
10
|
-
import { memoize } from "
|
|
10
|
+
import { memoize } from "../memoize.js"
|
|
11
11
|
|
|
12
12
|
const require = createRequire(import.meta.url)
|
|
13
13
|
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { UNICODE } from "@jsenv/log"
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
import { createDetailedMessage, UNICODE } from "@jsenv/log"
|
|
9
8
|
|
|
10
|
-
import { exec } from "
|
|
9
|
+
import { exec } from "../exec.js"
|
|
11
10
|
import {
|
|
12
11
|
VERB_CHECK_TRUST,
|
|
13
12
|
VERB_ADD_TRUST,
|
|
13
|
+
VERB_ENSURE_TRUST,
|
|
14
14
|
VERB_REMOVE_TRUST,
|
|
15
15
|
} from "../trust_query.js"
|
|
16
16
|
|
|
@@ -49,7 +49,7 @@ export const executeTrustQueryOnWindows = async ({
|
|
|
49
49
|
const certutilListCommand = `certutil -store -user root`
|
|
50
50
|
logger.debug(`${UNICODE.COMMAND} ${certutilListCommand}`)
|
|
51
51
|
const certutilListCommandOutput = await exec(certutilListCommand)
|
|
52
|
-
const certificateFilePath =
|
|
52
|
+
const certificateFilePath = fileURLToPath(certificateFileUrl)
|
|
53
53
|
|
|
54
54
|
// it's not super accurate and do not take into account if the cert is different
|
|
55
55
|
// but it's the best I could do with certutil command on windows
|
|
@@ -94,7 +94,11 @@ export const executeTrustQueryOnWindows = async ({
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
logger.info(`${UNICODE.OK} certificate found in windows`)
|
|
97
|
-
if (
|
|
97
|
+
if (
|
|
98
|
+
verb === VERB_CHECK_TRUST ||
|
|
99
|
+
verb === VERB_ADD_TRUST ||
|
|
100
|
+
verb === VERB_ENSURE_TRUST
|
|
101
|
+
) {
|
|
98
102
|
return {
|
|
99
103
|
status: "trusted",
|
|
100
104
|
reason: REASON_FOUND_IN_WINDOWS,
|
package/{main.js → src/main.js}
RENAMED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
export {
|
|
8
8
|
installCertificateAuthority,
|
|
9
9
|
uninstallCertificateAuthority,
|
|
10
|
-
} from "./
|
|
10
|
+
} from "./certificate_authority.js"
|
|
11
11
|
|
|
12
12
|
export {
|
|
13
13
|
createValidityDurationOfXYears,
|
|
14
14
|
createValidityDurationOfXDays,
|
|
15
|
-
} from "./
|
|
15
|
+
} from "./validity_duration.js"
|
|
16
16
|
|
|
17
|
-
export { verifyHostsFile } from "./
|
|
17
|
+
export { verifyHostsFile } from "./hosts_file_verif.js"
|
|
18
18
|
|
|
19
|
-
export { requestCertificateForLocalhost } from "./
|
|
19
|
+
export { requestCertificateForLocalhost } from "./certificate_for_localhost.js"
|
package/src/validity_duration.js
CHANGED
|
@@ -3,7 +3,6 @@ const MILLISECONDS_PER_YEAR = MILLISECONDS_PER_DAY * 365
|
|
|
3
3
|
|
|
4
4
|
export const verifyRootCertificateValidityDuration = (validityDurationInMs) => {
|
|
5
5
|
const durationInYears = validityDurationInMs / MILLISECONDS_PER_YEAR
|
|
6
|
-
|
|
7
6
|
if (durationInYears > 25) {
|
|
8
7
|
return {
|
|
9
8
|
ok: false,
|
|
@@ -13,26 +12,22 @@ export const verifyRootCertificateValidityDuration = (validityDurationInMs) => {
|
|
|
13
12
|
"https://serverfault.com/questions/847190/in-theory-could-a-ca-make-a-certificate-that-is-valid-for-arbitrarily-long",
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
|
-
|
|
17
15
|
return { ok: true }
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
export const verifyServerCertificateValidityDuration = (
|
|
21
|
-
|
|
19
|
+
validityDurationInMs,
|
|
22
20
|
) => {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (serverCertificateValidityDurationInDays > 397) {
|
|
21
|
+
const validityDurationInDays = validityDurationInMs / MILLISECONDS_PER_DAY
|
|
22
|
+
if (validityDurationInDays > 397) {
|
|
27
23
|
return {
|
|
28
24
|
ok: false,
|
|
29
25
|
maxAllowedValue: MILLISECONDS_PER_DAY * 397,
|
|
30
|
-
message: `certificate validity duration of ${
|
|
26
|
+
message: `certificate validity duration of ${validityDurationInMs} days is too much, using the max recommended duration: 397 days`,
|
|
31
27
|
details:
|
|
32
28
|
"https://www.globalsign.com/en/blog/maximum-ssltls-certificate-validity-now-one-year",
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
|
-
|
|
36
31
|
return { ok: true }
|
|
37
32
|
}
|
|
38
33
|
|