@jsenv/https-local 1.0.0 → 1.0.5
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 +56 -65
- package/package.json +27 -33
- package/src/certificate_authority.js +67 -26
- package/src/certificate_for_localhost.js +27 -11
- package/src/hosts_file_verif.js +13 -6
- package/src/internal/authority_file_infos.js +9 -3
- package/src/internal/certificate_authority_file_urls.js +21 -11
- package/src/internal/certificate_data_converter.js +6 -2
- package/src/internal/certificate_generator.js +21 -7
- package/src/internal/command.js +2 -1
- package/src/internal/exec.js +4 -1
- package/src/internal/hosts/parse_hosts.js +15 -5
- package/src/internal/hosts/write_hosts.js +23 -11
- package/src/internal/hosts/write_line_hosts.js +28 -12
- package/src/internal/linux/chrome_linux.js +8 -2
- package/src/internal/linux/firefox_linux.js +8 -2
- package/src/internal/linux/linux_trust_store.js +45 -20
- package/src/internal/linux/nss_linux.js +8 -2
- package/src/internal/mac/firefox_mac.js +8 -2
- package/src/internal/mac/mac_keychain.js +29 -12
- package/src/internal/mac/nss_mac.js +12 -4
- package/src/internal/nssdb_browser.js +17 -6
- package/src/internal/search_certificate_in_command_output.js +4 -1
- package/src/internal/unsupported_platform/unsupported_platform.js +3 -1
- package/src/internal/validity_formatting.js +3 -1
- package/src/internal/windows/chrome_windows.js +12 -10
- package/src/internal/windows/firefox_windows.js +12 -10
- package/src/internal/windows/windows_certutil.js +38 -16
- package/src/jsenvParameters.js +1 -1
- package/src/validity_duration.js +7 -3
|
@@ -3,15 +3,26 @@
|
|
|
3
3
|
import { urlToFileSystemPath } from "@jsenv/filesystem"
|
|
4
4
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
commandSign,
|
|
8
|
+
okSign,
|
|
9
|
+
infoSign,
|
|
10
|
+
failureSign,
|
|
11
|
+
} from "@jsenv/https-local/src/internal/logs.js"
|
|
7
12
|
import { exec } from "@jsenv/https-local/src/internal/exec.js"
|
|
8
13
|
import { searchCertificateInCommandOutput } from "@jsenv/https-local/src/internal/search_certificate_in_command_output.js"
|
|
9
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
VERB_CHECK_TRUST,
|
|
16
|
+
VERB_ADD_TRUST,
|
|
17
|
+
VERB_REMOVE_TRUST,
|
|
18
|
+
} from "../trust_query.js"
|
|
10
19
|
|
|
11
|
-
const REASON_NEW_AND_TRY_TO_TRUST_DISABLED =
|
|
20
|
+
const REASON_NEW_AND_TRY_TO_TRUST_DISABLED =
|
|
21
|
+
"certificate is new and tryToTrust is disabled"
|
|
12
22
|
const REASON_NOT_IN_KEYCHAIN = "certificate not found in mac keychain"
|
|
13
23
|
const REASON_IN_KEYCHAIN = "certificate found in mac keychain"
|
|
14
|
-
const REASON_ADD_TO_KEYCHAIN_COMMAND_FAILED =
|
|
24
|
+
const REASON_ADD_TO_KEYCHAIN_COMMAND_FAILED =
|
|
25
|
+
"command to add certificate in mac keychain failed"
|
|
15
26
|
const REASON_ADD_TO_KEYCHAIN_COMMAND_COMPLETED =
|
|
16
27
|
"command to add certificate in mac keychain completed"
|
|
17
28
|
const REASON_REMOVE_FROM_KEYCHAIN_COMMAND_FAILED =
|
|
@@ -70,10 +81,13 @@ export const executeTrustQueryOnMacKeychain = async ({
|
|
|
70
81
|
}
|
|
71
82
|
} catch (e) {
|
|
72
83
|
logger.error(
|
|
73
|
-
createDetailedMessage(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
createDetailedMessage(
|
|
85
|
+
`${failureSign} failed to add certificate to mac keychain`,
|
|
86
|
+
{
|
|
87
|
+
"error stack": e.stack,
|
|
88
|
+
"certificate file": certificateFilePath,
|
|
89
|
+
},
|
|
90
|
+
),
|
|
77
91
|
)
|
|
78
92
|
return {
|
|
79
93
|
status: "not_trusted",
|
|
@@ -107,10 +121,13 @@ export const executeTrustQueryOnMacKeychain = async ({
|
|
|
107
121
|
}
|
|
108
122
|
} catch (e) {
|
|
109
123
|
logger.error(
|
|
110
|
-
createDetailedMessage(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
createDetailedMessage(
|
|
125
|
+
`${failureSign} failed to remove certificate from mac keychain`,
|
|
126
|
+
{
|
|
127
|
+
"error stack": e.stack,
|
|
128
|
+
"certificate file url": certificateFileUrl,
|
|
129
|
+
},
|
|
130
|
+
),
|
|
114
131
|
)
|
|
115
132
|
return {
|
|
116
133
|
status: "not_trusted",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
assertAndNormalizeDirectoryUrl,
|
|
3
|
+
resolveUrl,
|
|
4
|
+
urlToFileSystemPath,
|
|
5
|
+
} from "@jsenv/filesystem"
|
|
2
6
|
|
|
3
7
|
import { memoize } from "@jsenv/https-local/src/internal/memoize.js"
|
|
4
8
|
import { exec } from "@jsenv/https-local/src/internal/exec.js"
|
|
@@ -24,7 +28,9 @@ export const detectIfNSSIsInstalled = async ({ logger }) => {
|
|
|
24
28
|
export const getCertutilBinPath = memoize(async () => {
|
|
25
29
|
const brewCommand = `brew --prefix nss`
|
|
26
30
|
const brewCommandOutput = await exec(brewCommand)
|
|
27
|
-
const nssCommandDirectoryUrl = assertAndNormalizeDirectoryUrl(
|
|
31
|
+
const nssCommandDirectoryUrl = assertAndNormalizeDirectoryUrl(
|
|
32
|
+
brewCommandOutput.trim(),
|
|
33
|
+
)
|
|
28
34
|
const certutilBinUrl = resolveUrl(`./bin/certutil`, nssCommandDirectoryUrl)
|
|
29
35
|
const certutilBinPath = urlToFileSystemPath(certutilBinUrl)
|
|
30
36
|
return certutilBinPath
|
|
@@ -32,12 +38,14 @@ export const getCertutilBinPath = memoize(async () => {
|
|
|
32
38
|
|
|
33
39
|
export const getNSSDynamicInstallInfo = () => {
|
|
34
40
|
return {
|
|
35
|
-
|
|
41
|
+
nssIsInstallable: commandExists("brew"),
|
|
36
42
|
nssNotInstallableReason: `"brew" is not available`,
|
|
37
43
|
nssInstallFixSuggestion: `install "brew" on this mac`,
|
|
38
44
|
installNss: async ({ logger }) => {
|
|
39
45
|
const brewInstallCommand = `brew install nss`
|
|
40
|
-
logger.info(
|
|
46
|
+
logger.info(
|
|
47
|
+
`"nss" is not installed, trying to install "nss" via Homebrew`,
|
|
48
|
+
)
|
|
41
49
|
logger.info(`> ${brewInstallCommand}`)
|
|
42
50
|
await exec(brewInstallCommand)
|
|
43
51
|
},
|
|
@@ -64,8 +64,12 @@ export const executeTrustQueryOnBrowserNSSDB = async ({
|
|
|
64
64
|
const cannotCheckMessage = `${failureSign} cannot check if certificate is in ${browserName}`
|
|
65
65
|
if (!nssIsInstalled) {
|
|
66
66
|
if (verb === VERB_ADD_TRUST) {
|
|
67
|
-
const {
|
|
68
|
-
|
|
67
|
+
const {
|
|
68
|
+
nssIsInstallable,
|
|
69
|
+
nssNotInstallableReason,
|
|
70
|
+
nssInstallFixSuggestion,
|
|
71
|
+
nssInstall,
|
|
72
|
+
} = await getNSSDynamicInstallInfo({ logger })
|
|
69
73
|
if (!nssIsInstallable) {
|
|
70
74
|
const reason = `"${nssCommandName}" is not installed and not cannot be installed`
|
|
71
75
|
logger.warn(
|
|
@@ -314,16 +318,21 @@ const findNSSDBFiles = async ({ logger, NSSDBDirectoryUrl }) => {
|
|
|
314
318
|
"./**/cert9.db": true,
|
|
315
319
|
},
|
|
316
320
|
},
|
|
317
|
-
predicate: ({ isLegacyNSSDB, isModernNSSDB }) =>
|
|
321
|
+
predicate: ({ isLegacyNSSDB, isModernNSSDB }) =>
|
|
322
|
+
isLegacyNSSDB || isModernNSSDB,
|
|
318
323
|
})
|
|
319
324
|
const fileCount = NSSDBFiles.length
|
|
320
325
|
if (fileCount === 0) {
|
|
321
|
-
logger.warn(
|
|
326
|
+
logger.warn(
|
|
327
|
+
`${warningSign} could not find nss database file in ${NSSDBDirectoryUrl}`,
|
|
328
|
+
)
|
|
322
329
|
NSSDirectoryCache[NSSDBDirectoryUrl] = []
|
|
323
330
|
return []
|
|
324
331
|
}
|
|
325
332
|
|
|
326
|
-
logger.debug(
|
|
333
|
+
logger.debug(
|
|
334
|
+
`${okSign} found ${fileCount} nss database file in ${NSSDBDirectoryUrl}`,
|
|
335
|
+
)
|
|
327
336
|
const files = NSSDBFiles.map((file) => {
|
|
328
337
|
return resolveUrl(file.relativeUrl, NSSDBDirectoryUrl)
|
|
329
338
|
})
|
|
@@ -335,7 +344,9 @@ const getDirectoryArgFromNSSDBFileUrl = (NSSDBFileUrl) => {
|
|
|
335
344
|
const nssDBFilename = urlToFilename(NSSDBFileUrl)
|
|
336
345
|
const nssDBDirectoryUrl = resolveUrl("./", NSSDBFileUrl)
|
|
337
346
|
const nssDBDirectoryPath = urlToFileSystemPath(nssDBDirectoryUrl)
|
|
338
|
-
return nssDBFilename === "cert8.db"
|
|
347
|
+
return nssDBFilename === "cert8.db"
|
|
348
|
+
? `"${nssDBDirectoryPath}"`
|
|
349
|
+
: `sql:"${nssDBDirectoryPath}"`
|
|
339
350
|
}
|
|
340
351
|
|
|
341
352
|
const execCertutilCommmand = async (command) => {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export const searchCertificateInCommandOutput = (
|
|
1
|
+
export const searchCertificateInCommandOutput = (
|
|
2
|
+
commandOutput,
|
|
3
|
+
certificateAsPEM,
|
|
4
|
+
) => {
|
|
2
5
|
commandOutput = commandOutput.replace(/\r\n/g, "\n").trim()
|
|
3
6
|
certificateAsPEM = certificateAsPEM.replace(/\r\n/g, "\n").trim()
|
|
4
7
|
return commandOutput.includes(certificateAsPEM)
|
|
@@ -6,7 +6,9 @@ const platformTrustInfo = {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export const executeTrustQuery = ({ logger }) => {
|
|
9
|
-
logger.warn(
|
|
9
|
+
logger.warn(
|
|
10
|
+
`${warningSign} platform not supported, cannot execute trust query`,
|
|
11
|
+
)
|
|
10
12
|
return {
|
|
11
13
|
platform: platformTrustInfo,
|
|
12
14
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const formatStillValid = ({ certificateName, validityRemainingMs }) => {
|
|
2
|
-
return `${certificateName} still valid for ${formatDuration(
|
|
2
|
+
return `${certificateName} still valid for ${formatDuration(
|
|
3
|
+
validityRemainingMs,
|
|
4
|
+
)}`
|
|
3
5
|
}
|
|
4
6
|
|
|
5
7
|
export const formatAboutToExpire = ({
|
|
@@ -40,16 +40,18 @@ const detectChrome = memoize(({ logger }) => {
|
|
|
40
40
|
`${process.env.ProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
41
41
|
`${process.env["ProgramFiles(x86)"]}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
42
42
|
]
|
|
43
|
-
const someExecutableFound = executableCandidates.some(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
const someExecutableFound = executableCandidates.some(
|
|
44
|
+
(chromeExecutablePathCandidate) => {
|
|
45
|
+
if (existsSync(chromeExecutablePathCandidate)) {
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
which.sync(chromeExecutablePathCandidate)
|
|
50
|
+
return true
|
|
51
|
+
} catch (e) {}
|
|
52
|
+
return false
|
|
53
|
+
},
|
|
54
|
+
)
|
|
53
55
|
if (someExecutableFound) {
|
|
54
56
|
logger.debug(`${okSign} Chrome detected`)
|
|
55
57
|
return true
|
|
@@ -58,16 +58,18 @@ const detectFirefox = memoize(({ logger }) => {
|
|
|
58
58
|
`${process.env.ProgramFiles}\\Mozilla Firefox\\firefox.exe`,
|
|
59
59
|
`${process.env["ProgramFiles(x86)"]}\\Mozilla Firefox\\firefox.exe`,
|
|
60
60
|
]
|
|
61
|
-
const someExecutableFound = executableCandidates.some(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
const someExecutableFound = executableCandidates.some(
|
|
62
|
+
(firefoxExecutablePathCandidate) => {
|
|
63
|
+
if (existsSync(firefoxExecutablePathCandidate)) {
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
which.sync(firefoxExecutablePathCandidate)
|
|
68
|
+
return true
|
|
69
|
+
} catch (e) {}
|
|
70
|
+
return false
|
|
71
|
+
},
|
|
72
|
+
)
|
|
71
73
|
if (someExecutableFound) {
|
|
72
74
|
logger.debug(`${okSign} Firefox detected`)
|
|
73
75
|
return true
|
|
@@ -6,17 +6,31 @@
|
|
|
6
6
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
7
7
|
import { urlToFileSystemPath } from "@jsenv/filesystem"
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
commandSign,
|
|
11
|
+
okSign,
|
|
12
|
+
infoSign,
|
|
13
|
+
failureSign,
|
|
14
|
+
} from "@jsenv/https-local/src/internal/logs.js"
|
|
10
15
|
import { exec } from "@jsenv/https-local/src/internal/exec.js"
|
|
11
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
VERB_CHECK_TRUST,
|
|
18
|
+
VERB_ADD_TRUST,
|
|
19
|
+
VERB_REMOVE_TRUST,
|
|
20
|
+
} from "../trust_query.js"
|
|
12
21
|
|
|
13
|
-
const REASON_NEW_AND_TRY_TO_TRUST_DISABLED =
|
|
22
|
+
const REASON_NEW_AND_TRY_TO_TRUST_DISABLED =
|
|
23
|
+
"certificate is new and tryToTrust is disabled"
|
|
14
24
|
const REASON_NOT_FOUND_IN_WINDOWS = "not found in windows store"
|
|
15
25
|
const REASON_FOUND_IN_WINDOWS = "found in windows store"
|
|
16
|
-
const REASON_ADD_COMMAND_FAILED =
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
26
|
+
const REASON_ADD_COMMAND_FAILED =
|
|
27
|
+
"command to add certificate to windows store failed"
|
|
28
|
+
const REASON_ADD_COMMAND_COMPLETED =
|
|
29
|
+
"command to add certificate to windows store completed"
|
|
30
|
+
const REASON_DELETE_COMMAND_FAILED =
|
|
31
|
+
"command to remove certificate from windows store failed"
|
|
32
|
+
const REASON_DELETE_COMMAND_COMPLETED =
|
|
33
|
+
"command to remove certificate from windows store completed"
|
|
20
34
|
|
|
21
35
|
export const executeTrustQueryOnWindows = async ({
|
|
22
36
|
logger,
|
|
@@ -44,7 +58,9 @@ export const executeTrustQueryOnWindows = async ({
|
|
|
44
58
|
|
|
45
59
|
// it's not super accurate and do not take into account if the cert is different
|
|
46
60
|
// but it's the best I could do with certutil command on windows
|
|
47
|
-
const certificateInStore = certutilListCommandOutput.includes(
|
|
61
|
+
const certificateInStore = certutilListCommandOutput.includes(
|
|
62
|
+
certificateCommonName,
|
|
63
|
+
)
|
|
48
64
|
if (!certificateInStore) {
|
|
49
65
|
logger.info(`${infoSign} certificate not found in windows`)
|
|
50
66
|
if (verb === VERB_CHECK_TRUST || verb === VERB_REMOVE_TRUST) {
|
|
@@ -67,10 +83,13 @@ export const executeTrustQueryOnWindows = async ({
|
|
|
67
83
|
}
|
|
68
84
|
} catch (e) {
|
|
69
85
|
logger.error(
|
|
70
|
-
createDetailedMessage(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
86
|
+
createDetailedMessage(
|
|
87
|
+
`${failureSign} Failed to add certificate to windows`,
|
|
88
|
+
{
|
|
89
|
+
"error stack": e.stack,
|
|
90
|
+
"certificate file": certificateFilePath,
|
|
91
|
+
},
|
|
92
|
+
),
|
|
74
93
|
)
|
|
75
94
|
return {
|
|
76
95
|
status: "not_trusted",
|
|
@@ -100,10 +119,13 @@ export const executeTrustQueryOnWindows = async ({
|
|
|
100
119
|
}
|
|
101
120
|
} catch (e) {
|
|
102
121
|
logger.error(
|
|
103
|
-
createDetailedMessage(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
createDetailedMessage(
|
|
123
|
+
`${failureSign} failed to remove certificate from windows`,
|
|
124
|
+
{
|
|
125
|
+
"error stack": e.stack,
|
|
126
|
+
"certificate file": certificateFilePath,
|
|
127
|
+
},
|
|
128
|
+
),
|
|
107
129
|
)
|
|
108
130
|
return {
|
|
109
131
|
status: "unknown", // maybe it was not trusted?
|
package/src/jsenvParameters.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createValidityDurationOfXYears } from "./validity_duration.js"
|
|
2
2
|
|
|
3
3
|
export const jsenvParameters = {
|
|
4
|
-
certificateCommonName: "
|
|
4
|
+
certificateCommonName: "https local root certificate",
|
|
5
5
|
certificateValidityDurationInMs: createValidityDurationOfXYears(20),
|
|
6
6
|
}
|
|
7
7
|
|
package/src/validity_duration.js
CHANGED
|
@@ -17,7 +17,9 @@ export const verifyRootCertificateValidityDuration = (validityDurationInMs) => {
|
|
|
17
17
|
return { ok: true }
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export const verifyServerCertificateValidityDuration = (
|
|
20
|
+
export const verifyServerCertificateValidityDuration = (
|
|
21
|
+
serverCertificateValidityDurationInMs,
|
|
22
|
+
) => {
|
|
21
23
|
const serverCertificateValidityDurationInDays =
|
|
22
24
|
serverCertificateValidityDurationInMs / MILLISECONDS_PER_DAY
|
|
23
25
|
|
|
@@ -34,6 +36,8 @@ export const verifyServerCertificateValidityDuration = (serverCertificateValidit
|
|
|
34
36
|
return { ok: true }
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
export const createValidityDurationOfXYears = (years) =>
|
|
39
|
+
export const createValidityDurationOfXYears = (years) =>
|
|
40
|
+
MILLISECONDS_PER_YEAR * years + 5000
|
|
38
41
|
|
|
39
|
-
export const createValidityDurationOfXDays = (days) =>
|
|
42
|
+
export const createValidityDurationOfXDays = (days) =>
|
|
43
|
+
MILLISECONDS_PER_DAY * days + 5000
|