@jsenv/https-local 3.0.6 → 3.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.
Files changed (46) hide show
  1. package/README.md +160 -192
  2. package/package.json +19 -38
  3. package/src/certificate_authority.js +111 -110
  4. package/src/certificate_request.js +34 -35
  5. package/src/hosts_file_verif.js +34 -35
  6. package/src/https_local_cli.mjs +74 -0
  7. package/src/internal/authority_file_infos.js +12 -13
  8. package/src/internal/browser_detection.js +4 -4
  9. package/src/internal/certificate_authority_file_urls.js +23 -23
  10. package/src/internal/certificate_data_converter.js +39 -39
  11. package/src/internal/certificate_generator.js +39 -39
  12. package/src/internal/command.js +6 -6
  13. package/src/internal/exec.js +10 -10
  14. package/src/internal/forge.js +3 -3
  15. package/src/internal/hosts/hosts_utils.js +2 -2
  16. package/src/internal/hosts/parse_hosts.js +67 -66
  17. package/src/internal/hosts/read_hosts.js +5 -6
  18. package/src/internal/hosts/write_hosts.js +29 -31
  19. package/src/internal/hosts/write_line_hosts.js +30 -32
  20. package/src/internal/hosts.js +5 -5
  21. package/src/internal/linux/chrome_linux.js +21 -20
  22. package/src/internal/linux/firefox_linux.js +23 -20
  23. package/src/internal/linux/linux.js +8 -8
  24. package/src/internal/linux/linux_trust_store.js +58 -59
  25. package/src/internal/linux/nss_linux.js +21 -20
  26. package/src/internal/mac/chrome_mac.js +15 -16
  27. package/src/internal/mac/firefox_mac.js +20 -21
  28. package/src/internal/mac/mac.js +10 -10
  29. package/src/internal/mac/mac_keychain.js +46 -47
  30. package/src/internal/mac/nss_mac.js +29 -30
  31. package/src/internal/mac/safari.js +2 -2
  32. package/src/internal/memoize.js +14 -14
  33. package/src/internal/nssdb_browser.js +150 -145
  34. package/src/internal/platform.js +6 -6
  35. package/src/internal/search_certificate_in_command_output.js +4 -4
  36. package/src/internal/trust_query.js +4 -4
  37. package/src/internal/unsupported_platform/unsupported_platform.js +5 -5
  38. package/src/internal/validity_formatting.js +32 -32
  39. package/src/internal/windows/chrome_windows.js +26 -27
  40. package/src/internal/windows/edge.js +2 -2
  41. package/src/internal/windows/firefox_windows.js +31 -32
  42. package/src/internal/windows/windows.js +10 -10
  43. package/src/internal/windows/windows_certutil.js +41 -42
  44. package/src/jsenvParameters.js +2 -2
  45. package/src/main.js +5 -8
  46. package/src/validity_duration.js +12 -12
@@ -1,37 +1,36 @@
1
- import { createRequire } from "node:module"
2
- import { existsSync } from "node:fs"
3
- import { UNICODE } from "@jsenv/log"
1
+ import { UNICODE } from "@jsenv/humanize";
2
+ import { existsSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { memoize } from "../memoize.js";
4
5
 
5
- import { memoize } from "../memoize.js"
6
+ const require = createRequire(import.meta.url);
6
7
 
7
- const require = createRequire(import.meta.url)
8
+ const which = require("which");
8
9
 
9
- const which = require("which")
10
-
11
- const REASON_CHROME_NOT_DETECTED = `Chrome not detected`
10
+ const REASON_CHROME_NOT_DETECTED = `Chrome not detected`;
12
11
 
13
12
  export const executeTrustQueryOnChrome = ({ logger, windowsTrustInfo }) => {
14
- const chromeDetected = detectChrome({ logger })
13
+ const chromeDetected = detectChrome({ logger });
15
14
  if (!chromeDetected) {
16
15
  return {
17
16
  status: "other",
18
17
  reason: REASON_CHROME_NOT_DETECTED,
19
- }
18
+ };
20
19
  }
21
20
 
22
21
  return {
23
22
  status: windowsTrustInfo.status,
24
23
  reason: windowsTrustInfo.reason,
25
- }
26
- }
24
+ };
25
+ };
27
26
 
28
27
  // https://github.com/litixsoft/karma-detect-browsers/blob/332b4bdb2ab3db7c6a1a6d3ec5a1c6ccf2332c4d/browsers/Chrome.js#L1
29
28
  const detectChrome = memoize(({ logger }) => {
30
- logger.debug(`Detecting Chrome...`)
29
+ logger.debug(`Detecting Chrome...`);
31
30
 
32
31
  if (process.env.CHROME_BIN && which.sync(process.env.CHROME_BIN)) {
33
- logger.debug(`${UNICODE.OK} Chrome detected`)
34
- return true
32
+ logger.debug(`${UNICODE.OK} Chrome detected`);
33
+ return true;
35
34
  }
36
35
 
37
36
  const executableCandidates = [
@@ -39,24 +38,24 @@ const detectChrome = memoize(({ logger }) => {
39
38
  `${process.env.ProgramW6432}\\Google\\Chrome\\Application\\chrome.exe`,
40
39
  `${process.env.ProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
41
40
  `${process.env["ProgramFiles(x86)"]}\\Google\\Chrome\\Application\\chrome.exe`,
42
- ]
41
+ ];
43
42
  const someExecutableFound = executableCandidates.some(
44
43
  (chromeExecutablePathCandidate) => {
45
44
  if (existsSync(chromeExecutablePathCandidate)) {
46
- return true
45
+ return true;
47
46
  }
48
47
  try {
49
- which.sync(chromeExecutablePathCandidate)
50
- return true
51
- } catch (e) {}
52
- return false
48
+ which.sync(chromeExecutablePathCandidate);
49
+ return true;
50
+ } catch {}
51
+ return false;
53
52
  },
54
- )
53
+ );
55
54
  if (someExecutableFound) {
56
- logger.debug(`${UNICODE.OK} Chrome detected`)
57
- return true
55
+ logger.debug(`${UNICODE.OK} Chrome detected`);
56
+ return true;
58
57
  }
59
58
 
60
- logger.debug(`${UNICODE.OK} Chrome detected`)
61
- return false
62
- })
59
+ logger.debug(`${UNICODE.OK} Chrome detected`);
60
+ return false;
61
+ });
@@ -2,5 +2,5 @@ export const executeTrustQueryOnEdge = ({ windowsTrustInfo }) => {
2
2
  return {
3
3
  status: windowsTrustInfo.status,
4
4
  reason: windowsTrustInfo.reason,
5
- }
6
- }
5
+ };
6
+ };
@@ -3,53 +3,52 @@
3
3
  * - A way to install and use NSS command on windows to update firefox NSS dabatase file
4
4
  */
5
5
 
6
- import { createRequire } from "node:module"
7
- import { existsSync } from "node:fs"
8
- import { UNICODE } from "@jsenv/log"
6
+ import { UNICODE } from "@jsenv/humanize";
7
+ import { existsSync } from "node:fs";
8
+ import { createRequire } from "node:module";
9
+ import { memoize } from "../memoize.js";
9
10
 
10
- import { memoize } from "../memoize.js"
11
+ const require = createRequire(import.meta.url);
11
12
 
12
- const require = createRequire(import.meta.url)
13
+ const which = require("which");
13
14
 
14
- const which = require("which")
15
-
16
- const REASON_FIREFOX_NOT_DETECTED = "Firefox not detected"
17
- const REASON_NOT_IMPLEMENTED_ON_WINDOWS = "not implemented on windows"
15
+ const REASON_FIREFOX_NOT_DETECTED = "Firefox not detected";
16
+ const REASON_NOT_IMPLEMENTED_ON_WINDOWS = "not implemented on windows";
18
17
 
19
18
  export const executeTrustQueryOnFirefox = ({ logger, certificateIsNew }) => {
20
- const firefoxDetected = detectFirefox({ logger })
19
+ const firefoxDetected = detectFirefox({ logger });
21
20
  if (!firefoxDetected) {
22
21
  return {
23
22
  status: "other",
24
23
  reason: REASON_FIREFOX_NOT_DETECTED,
25
- }
24
+ };
26
25
  }
27
26
 
28
27
  if (certificateIsNew) {
29
- logger.info(`${UNICODE.INFO} You should add certificate to firefox`)
28
+ logger.info(`${UNICODE.INFO} You should add certificate to firefox`);
30
29
  return {
31
30
  status: "not_trusted",
32
31
  reason: "certificate is new and tryToTrust is disabled",
33
- }
32
+ };
34
33
  }
35
34
 
36
- logger.info(`Check if certificate is in firefox...`)
35
+ logger.info(`Check if certificate is in firefox...`);
37
36
  logger.info(
38
37
  `${UNICODE.INFO} cannot check if certificate is in firefox (${REASON_NOT_IMPLEMENTED_ON_WINDOWS})`,
39
- )
38
+ );
40
39
  return {
41
40
  status: "unknown",
42
41
  reason: REASON_NOT_IMPLEMENTED_ON_WINDOWS,
43
- }
44
- }
42
+ };
43
+ };
45
44
 
46
45
  // https://github.com/litixsoft/karma-detect-browsers
47
46
  const detectFirefox = memoize(({ logger }) => {
48
- logger.debug(`Detecting Firefox...`)
47
+ logger.debug(`Detecting Firefox...`);
49
48
 
50
49
  if (process.env.FIREFOX_BIN && which.sync(process.env.FIREFOX_BIN)) {
51
- logger.debug(`${UNICODE.OK} Firefox detected`)
52
- return true
50
+ logger.debug(`${UNICODE.OK} Firefox detected`);
51
+ return true;
53
52
  }
54
53
 
55
54
  const executableCandidates = [
@@ -57,24 +56,24 @@ const detectFirefox = memoize(({ logger }) => {
57
56
  `${process.env.ProgramW6432}\\Mozilla Firefox\\firefox.exe`,
58
57
  `${process.env.ProgramFiles}\\Mozilla Firefox\\firefox.exe`,
59
58
  `${process.env["ProgramFiles(x86)"]}\\Mozilla Firefox\\firefox.exe`,
60
- ]
59
+ ];
61
60
  const someExecutableFound = executableCandidates.some(
62
61
  (firefoxExecutablePathCandidate) => {
63
62
  if (existsSync(firefoxExecutablePathCandidate)) {
64
- return true
63
+ return true;
65
64
  }
66
65
  try {
67
- which.sync(firefoxExecutablePathCandidate)
68
- return true
69
- } catch (e) {}
70
- return false
66
+ which.sync(firefoxExecutablePathCandidate);
67
+ return true;
68
+ } catch {}
69
+ return false;
71
70
  },
72
- )
71
+ );
73
72
  if (someExecutableFound) {
74
- logger.debug(`${UNICODE.OK} Firefox detected`)
75
- return true
73
+ logger.debug(`${UNICODE.OK} Firefox detected`);
74
+ return true;
76
75
  }
77
76
 
78
- logger.debug(`${UNICODE.INFO} Firefox detected`)
79
- return false
80
- })
77
+ logger.debug(`${UNICODE.INFO} Firefox detected`);
78
+ return false;
79
+ });
@@ -4,10 +4,10 @@
4
4
  * - https://www.unix.com/man-page/mojave/1/security/
5
5
  */
6
6
 
7
- import { executeTrustQueryOnWindows } from "./windows_certutil.js"
8
- import { executeTrustQueryOnChrome } from "./chrome_windows.js"
9
- import { executeTrustQueryOnEdge } from "./edge.js"
10
- import { executeTrustQueryOnFirefox } from "./firefox_windows.js"
7
+ import { executeTrustQueryOnChrome } from "./chrome_windows.js";
8
+ import { executeTrustQueryOnEdge } from "./edge.js";
9
+ import { executeTrustQueryOnFirefox } from "./firefox_windows.js";
10
+ import { executeTrustQueryOnWindows } from "./windows_certutil.js";
11
11
 
12
12
  export const executeTrustQuery = async ({
13
13
  logger,
@@ -24,26 +24,26 @@ export const executeTrustQuery = async ({
24
24
  certificateIsNew,
25
25
  certificate,
26
26
  verb,
27
- })
27
+ });
28
28
 
29
29
  const chromeTrustInfo = await executeTrustQueryOnChrome({
30
30
  logger,
31
31
  windowsTrustInfo,
32
- })
32
+ });
33
33
 
34
34
  const edgeTrustInfo = await executeTrustQueryOnEdge({
35
35
  windowsTrustInfo,
36
- })
36
+ });
37
37
 
38
38
  const firefoxTrustInfo = await executeTrustQueryOnFirefox({
39
39
  logger,
40
40
  certificateIsNew,
41
- })
41
+ });
42
42
 
43
43
  return {
44
44
  windows: windowsTrustInfo,
45
45
  chrome: chromeTrustInfo,
46
46
  edge: edgeTrustInfo,
47
47
  firefox: firefoxTrustInfo,
48
- }
49
- }
48
+ };
49
+ };
@@ -3,29 +3,28 @@
3
3
  * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil
4
4
  */
5
5
 
6
- import { fileURLToPath } from "node:url"
7
- import { createDetailedMessage, UNICODE } from "@jsenv/log"
8
-
9
- import { exec } from "../exec.js"
6
+ import { createDetailedMessage, UNICODE } from "@jsenv/humanize";
7
+ import { fileURLToPath } from "node:url";
8
+ import { exec } from "../exec.js";
10
9
  import {
11
- VERB_CHECK_TRUST,
12
10
  VERB_ADD_TRUST,
11
+ VERB_CHECK_TRUST,
13
12
  VERB_ENSURE_TRUST,
14
13
  VERB_REMOVE_TRUST,
15
- } from "../trust_query.js"
14
+ } from "../trust_query.js";
16
15
 
17
16
  const REASON_NEW_AND_TRY_TO_TRUST_DISABLED =
18
- "certificate is new and tryToTrust is disabled"
19
- const REASON_NOT_FOUND_IN_WINDOWS = "not found in windows store"
20
- const REASON_FOUND_IN_WINDOWS = "found in windows store"
17
+ "certificate is new and tryToTrust is disabled";
18
+ const REASON_NOT_FOUND_IN_WINDOWS = "not found in windows store";
19
+ const REASON_FOUND_IN_WINDOWS = "found in windows store";
21
20
  const REASON_ADD_COMMAND_FAILED =
22
- "command to add certificate to windows store failed"
21
+ "command to add certificate to windows store failed";
23
22
  const REASON_ADD_COMMAND_COMPLETED =
24
- "command to add certificate to windows store completed"
23
+ "command to add certificate to windows store completed";
25
24
  const REASON_DELETE_COMMAND_FAILED =
26
- "command to remove certificate from windows store failed"
25
+ "command to remove certificate from windows store failed";
27
26
  const REASON_DELETE_COMMAND_COMPLETED =
28
- "command to remove certificate from windows store completed"
27
+ "command to remove certificate from windows store completed";
29
28
 
30
29
  export const executeTrustQueryOnWindows = async ({
31
30
  logger,
@@ -36,46 +35,46 @@ export const executeTrustQueryOnWindows = async ({
36
35
  verb,
37
36
  }) => {
38
37
  if (verb === VERB_CHECK_TRUST && certificateIsNew) {
39
- logger.info(`${UNICODE.INFO} You should add certificate to windows`)
38
+ logger.info(`${UNICODE.INFO} You should add certificate to windows`);
40
39
  return {
41
40
  status: "not_trusted",
42
41
  reason: REASON_NEW_AND_TRY_TO_TRUST_DISABLED,
43
- }
42
+ };
44
43
  }
45
44
 
46
- logger.info(`Check if certificate is in windows...`)
45
+ logger.info(`Check if certificate is in windows...`);
47
46
  // https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil#-viewstore
48
47
  // TODO: check if -viewstore works better than -store
49
- const certutilListCommand = `certutil -store -user root`
50
- logger.debug(`${UNICODE.COMMAND} ${certutilListCommand}`)
51
- const certutilListCommandOutput = await exec(certutilListCommand)
52
- const certificateFilePath = fileURLToPath(certificateFileUrl)
48
+ const certutilListCommand = `certutil -store -user root`;
49
+ logger.debug(`${UNICODE.COMMAND} ${certutilListCommand}`);
50
+ const certutilListCommandOutput = await exec(certutilListCommand);
51
+ const certificateFilePath = fileURLToPath(certificateFileUrl);
53
52
 
54
53
  // it's not super accurate and do not take into account if the cert is different
55
54
  // but it's the best I could do with certutil command on windows
56
55
  const certificateInStore = certutilListCommandOutput.includes(
57
56
  certificateCommonName,
58
- )
57
+ );
59
58
  if (!certificateInStore) {
60
- logger.info(`${UNICODE.INFO} certificate not found in windows`)
59
+ logger.info(`${UNICODE.INFO} certificate not found in windows`);
61
60
  if (verb === VERB_CHECK_TRUST || verb === VERB_REMOVE_TRUST) {
62
61
  return {
63
62
  status: "not_trusted",
64
63
  reason: REASON_NOT_FOUND_IN_WINDOWS,
65
- }
64
+ };
66
65
  }
67
66
 
68
67
  // https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil#-addstore
69
- const certutilAddCommand = `certutil -addstore -user root "${certificateFilePath}"`
70
- logger.info(`Adding certificate to windows...`)
71
- logger.info(`${UNICODE.COMMAND} ${certutilAddCommand}`)
68
+ const certutilAddCommand = `certutil -addstore -user root "${certificateFilePath}"`;
69
+ logger.info(`Adding certificate to windows...`);
70
+ logger.info(`${UNICODE.COMMAND} ${certutilAddCommand}`);
72
71
  try {
73
- await exec(certutilAddCommand)
74
- logger.info(`${UNICODE.OK} certificate added to windows`)
72
+ await exec(certutilAddCommand);
73
+ logger.info(`${UNICODE.OK} certificate added to windows`);
75
74
  return {
76
75
  status: "trusted",
77
76
  reason: REASON_ADD_COMMAND_COMPLETED,
78
- }
77
+ };
79
78
  } catch (e) {
80
79
  logger.error(
81
80
  createDetailedMessage(
@@ -85,15 +84,15 @@ export const executeTrustQueryOnWindows = async ({
85
84
  "certificate file": certificateFilePath,
86
85
  },
87
86
  ),
88
- )
87
+ );
89
88
  return {
90
89
  status: "not_trusted",
91
90
  reason: REASON_ADD_COMMAND_FAILED,
92
- }
91
+ };
93
92
  }
94
93
  }
95
94
 
96
- logger.info(`${UNICODE.OK} certificate found in windows`)
95
+ logger.info(`${UNICODE.OK} certificate found in windows`);
97
96
  if (
98
97
  verb === VERB_CHECK_TRUST ||
99
98
  verb === VERB_ADD_TRUST ||
@@ -102,20 +101,20 @@ export const executeTrustQueryOnWindows = async ({
102
101
  return {
103
102
  status: "trusted",
104
103
  reason: REASON_FOUND_IN_WINDOWS,
105
- }
104
+ };
106
105
  }
107
106
 
108
107
  // https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil#-delstore
109
- const certutilRemoveCommand = `certutil -delstore -user root "${certificateCommonName}"`
110
- logger.info(`Removing certificate from windows...`)
111
- logger.info(`${UNICODE.COMMAND} ${certutilRemoveCommand}`)
108
+ const certutilRemoveCommand = `certutil -delstore -user root "${certificateCommonName}"`;
109
+ logger.info(`Removing certificate from windows...`);
110
+ logger.info(`${UNICODE.COMMAND} ${certutilRemoveCommand}`);
112
111
  try {
113
- await exec(certutilRemoveCommand)
114
- logger.info(`${UNICODE.OK} certificate removed from windows`)
112
+ await exec(certutilRemoveCommand);
113
+ logger.info(`${UNICODE.OK} certificate removed from windows`);
115
114
  return {
116
115
  status: "not_trusted",
117
116
  reason: REASON_DELETE_COMMAND_COMPLETED,
118
- }
117
+ };
119
118
  } catch (e) {
120
119
  logger.error(
121
120
  createDetailedMessage(
@@ -125,10 +124,10 @@ export const executeTrustQueryOnWindows = async ({
125
124
  "certificate file": certificateFilePath,
126
125
  },
127
126
  ),
128
- )
127
+ );
129
128
  return {
130
129
  status: "unknown", // maybe it was not trusted?
131
130
  reason: REASON_DELETE_COMMAND_FAILED,
132
- }
131
+ };
133
132
  }
134
- }
133
+ };
@@ -1,9 +1,9 @@
1
- import { createValidityDurationOfXYears } from "./validity_duration.js"
1
+ import { createValidityDurationOfXYears } from "./validity_duration.js";
2
2
 
3
3
  export const jsenvParameters = {
4
4
  certificateCommonName: "https local root certificate",
5
5
  certificateValidityDurationInMs: createValidityDurationOfXYears(20),
6
- }
6
+ };
7
7
 
8
8
  // const jsenvCertificateParams = {
9
9
  // rootCertificateOrganizationName: "jsenv",
package/src/main.js CHANGED
@@ -11,13 +11,10 @@
11
11
  export {
12
12
  installCertificateAuthority,
13
13
  uninstallCertificateAuthority,
14
- } from "./certificate_authority.js"
15
-
14
+ } from "./certificate_authority.js";
15
+ export { requestCertificate } from "./certificate_request.js";
16
+ export { verifyHostsFile } from "./hosts_file_verif.js";
16
17
  export {
17
- createValidityDurationOfXYears,
18
18
  createValidityDurationOfXDays,
19
- } from "./validity_duration.js"
20
-
21
- export { verifyHostsFile } from "./hosts_file_verif.js"
22
-
23
- export { requestCertificate } from "./certificate_request.js"
19
+ createValidityDurationOfXYears,
20
+ } from "./validity_duration.js";
@@ -1,8 +1,8 @@
1
- const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000
2
- const MILLISECONDS_PER_YEAR = MILLISECONDS_PER_DAY * 365
1
+ const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
2
+ const MILLISECONDS_PER_YEAR = MILLISECONDS_PER_DAY * 365;
3
3
 
4
4
  export const verifyRootCertificateValidityDuration = (validityDurationInMs) => {
5
- const durationInYears = validityDurationInMs / MILLISECONDS_PER_YEAR
5
+ const durationInYears = validityDurationInMs / MILLISECONDS_PER_YEAR;
6
6
  if (durationInYears > 25) {
7
7
  return {
8
8
  ok: false,
@@ -10,15 +10,15 @@ export const verifyRootCertificateValidityDuration = (validityDurationInMs) => {
10
10
  message: `root certificate validity duration of ${durationInYears} years is too much, using the max recommended duration: 25 years`,
11
11
  details:
12
12
  "https://serverfault.com/questions/847190/in-theory-could-a-ca-make-a-certificate-that-is-valid-for-arbitrarily-long",
13
- }
13
+ };
14
14
  }
15
- return { ok: true }
16
- }
15
+ return { ok: true };
16
+ };
17
17
 
18
18
  export const verifyServerCertificateValidityDuration = (
19
19
  validityDurationInMs,
20
20
  ) => {
21
- const validityDurationInDays = validityDurationInMs / MILLISECONDS_PER_DAY
21
+ const validityDurationInDays = validityDurationInMs / MILLISECONDS_PER_DAY;
22
22
  if (validityDurationInDays > 397) {
23
23
  return {
24
24
  ok: false,
@@ -26,13 +26,13 @@ export const verifyServerCertificateValidityDuration = (
26
26
  message: `certificate validity duration of ${validityDurationInMs} days is too much, using the max recommended duration: 397 days`,
27
27
  details:
28
28
  "https://www.globalsign.com/en/blog/maximum-ssltls-certificate-validity-now-one-year",
29
- }
29
+ };
30
30
  }
31
- return { ok: true }
32
- }
31
+ return { ok: true };
32
+ };
33
33
 
34
34
  export const createValidityDurationOfXYears = (years) =>
35
- MILLISECONDS_PER_YEAR * years + 5000
35
+ MILLISECONDS_PER_YEAR * years + 5000;
36
36
 
37
37
  export const createValidityDurationOfXDays = (days) =>
38
- MILLISECONDS_PER_DAY * days + 5000
38
+ MILLISECONDS_PER_DAY * days + 5000;