@jsenv/https-local 2.0.0 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/https-local",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "A programmatic way to generate locally trusted certificates",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -231,13 +231,12 @@ export const installCertificateAuthority = async ({
231
231
  const rootCertificatePrivateKeyForgeObject = pki.privateKeyFromPem(
232
232
  rootCertificatePrivateKey,
233
233
  )
234
-
235
234
  const trustInfo = await platformMethods.executeTrustQuery({
236
235
  logger,
237
236
  certificateCommonName,
238
237
  certificateFileUrl: rootCertificateFileInfo.url,
239
238
  certificate: rootCertificate,
240
- verb: tryToTrust ? "ADD_TRUST" : "CHECK_TRUST",
239
+ verb: tryToTrust ? "ENSURE_TRUST" : "CHECK_TRUST",
241
240
  NSSDynamicInstall,
242
241
  })
243
242
 
@@ -10,6 +10,7 @@ import { createDetailedMessage, UNICODE } from "@jsenv/log"
10
10
  import {
11
11
  VERB_CHECK_TRUST,
12
12
  VERB_ADD_TRUST,
13
+ VERB_ENSURE_TRUST,
13
14
  VERB_REMOVE_TRUST,
14
15
  } from "../trust_query.js"
15
16
  import { exec } from "../exec.js"
@@ -100,7 +101,11 @@ export const executeTrustQueryOnLinux = async ({
100
101
  }
101
102
 
102
103
  logger.info(`${UNICODE.OK} certificate found in linux`)
103
- if (verb === VERB_CHECK_TRUST || verb === VERB_ADD_TRUST) {
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,
@@ -8,6 +8,7 @@ import { searchCertificateInCommandOutput } from "../search_certificate_in_comma
8
8
  import {
9
9
  VERB_CHECK_TRUST,
10
10
  VERB_ADD_TRUST,
11
+ VERB_ENSURE_TRUST,
11
12
  VERB_REMOVE_TRUST,
12
13
  } from "../trust_query.js"
13
14
 
@@ -52,6 +53,35 @@ export const executeTrustQueryOnMacKeychain = async ({
52
53
  certificate,
53
54
  )
54
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
+
55
85
  if (!certificateFoundInCommandOutput) {
56
86
  logger.info(`${UNICODE.INFO} certificate not found in mac keychain`)
57
87
  if (verb === VERB_CHECK_TRUST || verb === VERB_REMOVE_TRUST) {
@@ -60,7 +90,13 @@ export const executeTrustQueryOnMacKeychain = async ({
60
90
  reason: REASON_NOT_IN_KEYCHAIN,
61
91
  }
62
92
  }
63
-
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
+ }
64
100
  const certificateFilePath = fileURLToPath(certificateFileUrl)
65
101
  // https://ss64.com/osx/security-cert.html
66
102
  const addTrustedCertCommand = `sudo security add-trusted-cert -d -r trustRoot -k ${systemKeychainPath} "${certificateFilePath}"`
@@ -95,37 +131,16 @@ export const executeTrustQueryOnMacKeychain = async ({
95
131
  // but they shouldn't and I couldn't find an API to know if the cert is trusted or not
96
132
  // just if it's in the keychain
97
133
  logger.info(`${UNICODE.OK} certificate found in mac keychain`)
98
- if (verb === VERB_CHECK_TRUST || verb === VERB_ADD_TRUST) {
134
+ if (
135
+ verb === VERB_CHECK_TRUST ||
136
+ verb === VERB_ADD_TRUST ||
137
+ verb === VERB_ENSURE_TRUST
138
+ ) {
99
139
  return {
100
140
  status: "trusted",
101
141
  reason: REASON_IN_KEYCHAIN,
102
142
  }
103
143
  }
104
144
 
105
- // https://ss64.com/osx/security-delete-cert.html
106
- const removeTrustedCertCommand = `sudo security delete-certificate -c "${certificateCommonName}"`
107
- logger.info(`Removing certificate from mac keychain...`)
108
- logger.info(`${UNICODE.COMMAND} ${removeTrustedCertCommand}`)
109
- try {
110
- await exec(removeTrustedCertCommand)
111
- logger.info(`${UNICODE.OK} certificate removed from mac keychain`)
112
- return {
113
- status: "not_trusted",
114
- reason: REASON_REMOVE_FROM_KEYCHAIN_COMMAND_COMPLETED,
115
- }
116
- } catch (e) {
117
- logger.error(
118
- createDetailedMessage(
119
- `${UNICODE.FAILURE} failed to remove certificate from mac keychain`,
120
- {
121
- "error stack": e.stack,
122
- "certificate file url": certificateFileUrl,
123
- },
124
- ),
125
- )
126
- return {
127
- status: "not_trusted",
128
- reason: REASON_REMOVE_FROM_KEYCHAIN_COMMAND_FAILED,
129
- }
130
- }
145
+ return removeCert()
131
146
  }
@@ -11,7 +11,11 @@ import { assertAndNormalizeDirectoryUrl, collectFiles } from "@jsenv/filesystem"
11
11
 
12
12
  import { exec } from "./exec.js"
13
13
  import { searchCertificateInCommandOutput } from "./search_certificate_in_command_output.js"
14
- import { VERB_CHECK_TRUST, VERB_ADD_TRUST } from "./trust_query.js"
14
+ import {
15
+ VERB_CHECK_TRUST,
16
+ VERB_ADD_TRUST,
17
+ VERB_ENSURE_TRUST,
18
+ } from "./trust_query.js"
15
19
 
16
20
  export const executeTrustQueryOnBrowserNSSDB = async ({
17
21
  logger,
@@ -52,7 +56,7 @@ export const executeTrustQueryOnBrowserNSSDB = async ({
52
56
  const nssIsInstalled = await detectIfNSSIsInstalled({ logger })
53
57
  const cannotCheckMessage = `${UNICODE.FAILURE} cannot check if certificate is in ${browserName}`
54
58
  if (!nssIsInstalled) {
55
- if (verb === VERB_ADD_TRUST) {
59
+ if (verb === VERB_ADD_TRUST || verb === VERB_ENSURE_TRUST) {
56
60
  const nssDynamicInstallInfo = await getNSSDynamicInstallInfo({ logger })
57
61
  if (!nssDynamicInstallInfo.isInstallable) {
58
62
  const reason = `"${nssCommandName}" is not installed and not cannot be installed`
@@ -209,7 +213,7 @@ export const executeTrustQueryOnBrowserNSSDB = async ({
209
213
  }
210
214
  }
211
215
 
212
- if (verb === VERB_ADD_TRUST) {
216
+ if (verb === VERB_ADD_TRUST || verb === VERB_ENSURE_TRUST) {
213
217
  if (missingCount === 0 && outdatedCount === 0) {
214
218
  logger.info(`${UNICODE.OK} certificate found in ${browserName}`)
215
219
  return {
@@ -1,5 +1,4 @@
1
1
  export const VERB_CHECK_TRUST = "CHECK_TRUST"
2
-
3
2
  export const VERB_ADD_TRUST = "ADD_TRUST"
4
-
3
+ export const VERB_ENSURE_TRUST = "ENSURE_TRUST"
5
4
  export const VERB_REMOVE_TRUST = "REMOVE_TRUST"
@@ -10,6 +10,7 @@ import { exec } from "../exec.js"
10
10
  import {
11
11
  VERB_CHECK_TRUST,
12
12
  VERB_ADD_TRUST,
13
+ VERB_ENSURE_TRUST,
13
14
  VERB_REMOVE_TRUST,
14
15
  } from "../trust_query.js"
15
16
 
@@ -93,7 +94,11 @@ export const executeTrustQueryOnWindows = async ({
93
94
  }
94
95
 
95
96
  logger.info(`${UNICODE.OK} certificate found in windows`)
96
- if (verb === VERB_CHECK_TRUST || verb === VERB_ADD_TRUST) {
97
+ if (
98
+ verb === VERB_CHECK_TRUST ||
99
+ verb === VERB_ADD_TRUST ||
100
+ verb === VERB_ENSURE_TRUST
101
+ ) {
97
102
  return {
98
103
  status: "trusted",
99
104
  reason: REASON_FOUND_IN_WINDOWS,