@expo/devcert 1.1.4 → 1.2.1
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 +12 -12
- package/dist/certificate-authority.d.ts +1 -1
- package/dist/certificate-authority.js +60 -66
- package/dist/certificates.js +22 -23
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +40 -27
- package/dist/index.js +77 -54
- package/dist/platforms/darwin.d.ts +2 -2
- package/dist/platforms/darwin.js +66 -73
- package/dist/platforms/index.d.ts +2 -2
- package/dist/platforms/index.js +1 -1
- package/dist/platforms/linux.d.ts +2 -2
- package/dist/platforms/linux.js +62 -69
- package/dist/platforms/shared.d.ts +2 -2
- package/dist/platforms/shared.js +87 -63
- package/dist/platforms/win32.d.ts +2 -2
- package/dist/platforms/win32.js +60 -66
- package/dist/user-interface.js +37 -29
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +34 -9
- package/openssl-configurations/domain-certificate-signing-requests.conf +3 -3
- package/openssl-configurations/domain-certificates.conf +5 -5
- package/package.json +3 -17
- package/src/certificate-authority.ts +9 -9
- package/src/certificates.ts +4 -5
- package/src/constants.ts +37 -24
- package/src/index.ts +1 -1
- package/src/platforms/darwin.ts +4 -5
- package/src/platforms/index.ts +2 -2
- package/src/platforms/linux.ts +6 -7
- package/src/platforms/shared.ts +63 -37
- package/src/platforms/win32.ts +4 -4
- package/src/user-interface.ts +21 -2
- package/src/utils.ts +29 -6
- package/tsconfig.json +2 -2
- package/src/types.d.ts +0 -7
package/src/constants.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import
|
|
3
|
-
import { sync as mkdirp } from 'mkdirp';
|
|
4
|
-
import { template as makeTemplate } from 'lodash';
|
|
5
|
-
import applicationConfigPath = require('application-config-path');
|
|
6
|
-
import eol from 'eol';
|
|
2
|
+
import fs from 'fs';
|
|
7
3
|
import { mktmp } from './utils';
|
|
8
4
|
|
|
5
|
+
function applicationConfigPath(name: string): string {
|
|
6
|
+
switch (process.platform) {
|
|
7
|
+
case 'darwin':
|
|
8
|
+
return path.join(process.env.HOME, 'Library', 'Application Support', name);
|
|
9
|
+
case 'win32':
|
|
10
|
+
return process.env.LOCALAPPDATA
|
|
11
|
+
? path.join(process.env.LOCALAPPDATA, name)
|
|
12
|
+
: path.join(process.env.USERPROFILE, 'Local Settings', 'Application Data', name);
|
|
13
|
+
case 'linux':
|
|
14
|
+
default:
|
|
15
|
+
return process.env.XDG_CONFIG_HOME
|
|
16
|
+
? path.join(process.env.XDG_CONFIG_HOME, name)
|
|
17
|
+
: path.join(process.env.HOME, '.config', name);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
export const VALID_IP = /(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}/;
|
|
10
22
|
export const VALID_DOMAIN = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.?)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i;
|
|
11
23
|
|
|
@@ -26,34 +38,35 @@ export const opensslSerialFilePath = configPath('certificate-authority', 'serial
|
|
|
26
38
|
export const opensslDatabaseFilePath = configPath('certificate-authority', 'index.txt');
|
|
27
39
|
export const caSelfSignConfig = path.join(__dirname, '../openssl-configurations/certificate-authority-self-signing.conf');
|
|
28
40
|
|
|
41
|
+
function eolAuto(str: string): string {
|
|
42
|
+
return str.replace(/\r?\n|\r/g, isWindows ? '\r\n' : '\n');
|
|
43
|
+
}
|
|
44
|
+
|
|
29
45
|
export function withDomainSigningRequestConfig(domain: string, cb: (filepath: string) => void) {
|
|
30
46
|
let tmpFile = mktmp();
|
|
31
|
-
let source =
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
writeFile(tmpFile, eol.auto(result));
|
|
47
|
+
let source = fs.readFileSync(path.join(__dirname, '../openssl-configurations/domain-certificate-signing-requests.conf'), 'utf-8');
|
|
48
|
+
let result = source.replace(/%DOMAIN%/g, domain);
|
|
49
|
+
fs.writeFileSync(tmpFile, eolAuto(result));
|
|
35
50
|
cb(tmpFile);
|
|
36
|
-
|
|
51
|
+
fs.rmSync(tmpFile);
|
|
37
52
|
}
|
|
38
53
|
|
|
39
54
|
export function withDomainCertificateConfig(domain: string, cb: (filepath: string) => void) {
|
|
40
55
|
let tmpFile = mktmp();
|
|
41
|
-
let source =
|
|
42
|
-
let
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
});
|
|
49
|
-
writeFile(tmpFile, eol.auto(result));
|
|
56
|
+
let source = fs.readFileSync(path.join(__dirname, '../openssl-configurations/domain-certificates.conf'), 'utf-8');
|
|
57
|
+
let result = source
|
|
58
|
+
.replace(/%DOMAIN%/g, domain)
|
|
59
|
+
.replace(/%SERIALFILE%/g, opensslSerialFilePath.replace(/\\/g, '\\\\'))
|
|
60
|
+
.replace(/%DATABASEFILE%/g, opensslDatabaseFilePath.replace(/\\/g, '\\\\'))
|
|
61
|
+
.replace(/%DOMAINDIR%/g, pathForDomain(domain).replace(/\\/g, '\\\\'))
|
|
62
|
+
fs.writeFileSync(tmpFile, eolAuto(result));
|
|
50
63
|
cb(tmpFile);
|
|
51
|
-
|
|
64
|
+
fs.rmSync(tmpFile);
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
// confTemplate = confTemplate.replace(/DATABASE_PATH/, configPath('index.txt').replace(/\\/g, '\\\\'));
|
|
55
68
|
// confTemplate = confTemplate.replace(/SERIAL_PATH/, configPath('serial').replace(/\\/g, '\\\\'));
|
|
56
|
-
// confTemplate =
|
|
69
|
+
// confTemplate = eolAuto(confTemplate);
|
|
57
70
|
|
|
58
71
|
export const rootCADir = configPath('certificate-authority');
|
|
59
72
|
export const rootCAKeyPath = configPath('certificate-authority', 'private-key.key');
|
|
@@ -73,9 +86,9 @@ export function getLegacyConfigDir(): string {
|
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
export function ensureConfigDirs() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
90
|
+
fs.mkdirSync(domainsDir, { recursive: true });
|
|
91
|
+
fs.mkdirSync(rootCADir, { recursive: true });
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
ensureConfigDirs();
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { rmSync as rm, readFileSync as readFile, readdirSync as readdir, existsSync as exists } from 'fs';
|
|
2
2
|
import createDebug from 'debug';
|
|
3
|
-
import { sync as commandExists } from 'command-exists';
|
|
4
3
|
import {
|
|
5
4
|
isMac,
|
|
6
5
|
isLinux,
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
VALID_IP
|
|
14
13
|
} from './constants';
|
|
15
14
|
import currentPlatform from './platforms';
|
|
15
|
+
import { commandExists } from './utils';
|
|
16
16
|
import installCertificateAuthority, { ensureCACertReadable, uninstall } from './certificate-authority';
|
|
17
17
|
import generateDomainCertificate from './certificates';
|
|
18
18
|
import UI, { UserInterface } from './user-interface';
|
package/src/platforms/darwin.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { writeFileSync as writeFile, existsSync as exists, readFileSync as read } from 'fs';
|
|
3
3
|
import createDebug from 'debug';
|
|
4
|
-
import {
|
|
5
|
-
import { run, sudoAppend } from '../utils';
|
|
4
|
+
import { run, sudoAppend, commandExists } from '../utils';
|
|
6
5
|
import { Options } from '../index';
|
|
7
6
|
import { addCertificateToNSSCertDB, assertNotTouchingFiles, openCertificateInFirefox, closeFirefox, removeCertificateFromNSSCertDB } from './shared';
|
|
8
7
|
import { Platform } from '.';
|
|
@@ -73,7 +72,7 @@ export default class MacOSPlatform implements Platform {
|
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
removeFromTrustStores(certificatePath: string) {
|
|
75
|
+
async removeFromTrustStores(certificatePath: string) {
|
|
77
76
|
debug('Removing devcert root CA from macOS system keychain');
|
|
78
77
|
try {
|
|
79
78
|
run('sudo', [
|
|
@@ -89,7 +88,7 @@ export default class MacOSPlatform implements Platform {
|
|
|
89
88
|
}
|
|
90
89
|
if (this.isFirefoxInstalled() && this.isNSSInstalled()) {
|
|
91
90
|
debug('Firefox install and certutil install detected. Trying to remove root CA from Firefox NSS databases');
|
|
92
|
-
removeCertificateFromNSSCertDB(this.FIREFOX_NSS_DIR, certificatePath, getCertUtilPath());
|
|
91
|
+
await removeCertificateFromNSSCertDB(this.FIREFOX_NSS_DIR, certificatePath, getCertUtilPath());
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
@@ -101,7 +100,7 @@ export default class MacOSPlatform implements Platform {
|
|
|
101
100
|
}
|
|
102
101
|
}
|
|
103
102
|
|
|
104
|
-
deleteProtectedFiles(filepath: string) {
|
|
103
|
+
async deleteProtectedFiles(filepath: string) {
|
|
105
104
|
assertNotTouchingFiles(filepath, 'delete');
|
|
106
105
|
run('sudo', ['rm', '-rf', filepath]);
|
|
107
106
|
}
|
package/src/platforms/index.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { Options } from '../index';
|
|
|
3
3
|
|
|
4
4
|
export interface Platform {
|
|
5
5
|
addToTrustStores(certificatePath: string, options?: Options): Promise<void>;
|
|
6
|
-
removeFromTrustStores(certificatePath: string): void
|
|
6
|
+
removeFromTrustStores(certificatePath: string): Promise<void>;
|
|
7
7
|
addDomainToHostFileIfMissing(domain: string): Promise<void>;
|
|
8
|
-
deleteProtectedFiles(filepath: string): void
|
|
8
|
+
deleteProtectedFiles(filepath: string): Promise<void>;
|
|
9
9
|
readProtectedFile(filepath: string): Promise<string>;
|
|
10
10
|
writeProtectedFile(filepath: string, contents: string): Promise<void>;
|
|
11
11
|
}
|
package/src/platforms/linux.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { existsSync as exists, readFileSync as read, writeFileSync as writeFile } from 'fs';
|
|
3
3
|
import createDebug from 'debug';
|
|
4
|
-
import { sync as commandExists } from 'command-exists';
|
|
5
4
|
import { addCertificateToNSSCertDB, assertNotTouchingFiles, openCertificateInFirefox, closeFirefox, removeCertificateFromNSSCertDB } from './shared';
|
|
6
|
-
import { run, sudoAppend } from '../utils';
|
|
5
|
+
import { run, sudoAppend, commandExists } from '../utils';
|
|
7
6
|
import { Options } from '../index';
|
|
8
7
|
import UI from '../user-interface';
|
|
9
8
|
import { Platform } from '.';
|
|
@@ -68,7 +67,7 @@ export default class LinuxPlatform implements Platform {
|
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
removeFromTrustStores(certificatePath: string) {
|
|
70
|
+
async removeFromTrustStores(certificatePath: string) {
|
|
72
71
|
try {
|
|
73
72
|
run('sudo', ['rm', '/usr/local/share/ca-certificates/devcert.crt']);
|
|
74
73
|
run('sudo', ['update-ca-certificates']);
|
|
@@ -77,10 +76,10 @@ export default class LinuxPlatform implements Platform {
|
|
|
77
76
|
}
|
|
78
77
|
if (commandExists('certutil')) {
|
|
79
78
|
if (this.isFirefoxInstalled()) {
|
|
80
|
-
removeCertificateFromNSSCertDB(this.FIREFOX_NSS_DIR, certificatePath, 'certutil');
|
|
79
|
+
await removeCertificateFromNSSCertDB(this.FIREFOX_NSS_DIR, certificatePath, 'certutil');
|
|
81
80
|
}
|
|
82
81
|
if (this.isChromeInstalled()) {
|
|
83
|
-
removeCertificateFromNSSCertDB(this.CHROME_NSS_DIR, certificatePath, 'certutil');
|
|
82
|
+
await removeCertificateFromNSSCertDB(this.CHROME_NSS_DIR, certificatePath, 'certutil');
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
}
|
|
@@ -93,7 +92,7 @@ export default class LinuxPlatform implements Platform {
|
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
deleteProtectedFiles(filepath: string) {
|
|
95
|
+
async deleteProtectedFiles(filepath: string) {
|
|
97
96
|
assertNotTouchingFiles(filepath, 'delete');
|
|
98
97
|
run('sudo', ['rm', '-rf', filepath]);
|
|
99
98
|
}
|
|
@@ -121,4 +120,4 @@ export default class LinuxPlatform implements Platform {
|
|
|
121
120
|
return exists(this.CHROME_BIN_PATH);
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
}
|
|
123
|
+
}
|
package/src/platforms/shared.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import url from 'url';
|
|
3
2
|
import createDebug from 'debug';
|
|
4
3
|
import assert from 'assert';
|
|
5
|
-
import
|
|
4
|
+
import net from 'net';
|
|
6
5
|
import http from 'http';
|
|
7
|
-
import
|
|
8
|
-
import { readFileSync as readFile, existsSync as exists } from 'fs';
|
|
6
|
+
import fs from 'fs';
|
|
9
7
|
import { run } from '../utils';
|
|
10
8
|
import { isMac, isLinux , configDir, getLegacyConfigDir } from '../constants';
|
|
11
9
|
import UI from '../user-interface';
|
|
@@ -13,47 +11,66 @@ import { execSync as exec } from 'child_process';
|
|
|
13
11
|
|
|
14
12
|
const debug = createDebug('devcert:platforms:shared');
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
async function* iterateNSSCertDBPaths(nssDirGlob: string): AsyncGenerator<string> {
|
|
15
|
+
const globIdx = nssDirGlob.indexOf('*');
|
|
16
|
+
if (globIdx === -1) {
|
|
17
|
+
try {
|
|
18
|
+
const stat = fs.statSync(nssDirGlob);
|
|
19
|
+
if (stat.isDirectory()) {
|
|
20
|
+
yield nssDirGlob;
|
|
21
|
+
}
|
|
22
|
+
} catch (_error) {
|
|
23
|
+
// no matching directory found
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
} else if (globIdx === nssDirGlob.length - 1) {
|
|
26
|
+
const targetDir = path.dirname(nssDirGlob);
|
|
27
|
+
for (const entry of await fs.promises.readdir(targetDir, { withFileTypes: true })) {
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
yield path.join(targetDir, entry.name);
|
|
30
|
+
}
|
|
30
31
|
}
|
|
31
|
-
}
|
|
32
|
+
} else {
|
|
33
|
+
throw new Error('Internal: Invalid `nssDirGlob` specified');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function* iterateNSSCertDBs(nssDirGlob: string): AsyncGenerator<{ dir: string; version: 'legacy' | 'modern' }> {
|
|
38
|
+
for await (const dir of iterateNSSCertDBPaths(nssDirGlob)) {
|
|
39
|
+
debug(`checking to see if ${dir} is a valid NSS database directory`);
|
|
40
|
+
if (fs.existsSync(path.join(dir, 'cert8.db'))) {
|
|
41
|
+
debug(`Found legacy NSS database in ${dir}, emitting...`);
|
|
42
|
+
yield { dir, version: 'legacy' };
|
|
43
|
+
}
|
|
44
|
+
if (fs.existsSync(path.join(dir, 'cert9.db'))) {
|
|
45
|
+
debug(`Found modern NSS database in ${dir}, running callback...`)
|
|
46
|
+
yield { dir, version: 'modern' };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
32
49
|
}
|
|
33
50
|
|
|
34
51
|
/**
|
|
35
52
|
* Given a directory or glob pattern of directories, attempt to install the
|
|
36
53
|
* CA certificate to each directory containing an NSS database.
|
|
37
54
|
*/
|
|
38
|
-
export function addCertificateToNSSCertDB(nssDirGlob: string, certPath: string, certutilPath: string): void {
|
|
55
|
+
export async function addCertificateToNSSCertDB(nssDirGlob: string, certPath: string, certutilPath: string): Promise<void> {
|
|
39
56
|
debug(`trying to install certificate into NSS databases in ${ nssDirGlob }`);
|
|
40
|
-
|
|
57
|
+
for await (const { dir, version } of iterateNSSCertDBs(nssDirGlob)) {
|
|
41
58
|
const dirArg = version === 'modern' ? `sql:${ dir }` : dir;
|
|
42
|
-
|
|
43
|
-
}
|
|
59
|
+
run(certutilPath, ['-A', '-d', dirArg, '-t', 'C,,', '-i', certPath, '-n', 'devcert']);
|
|
60
|
+
}
|
|
44
61
|
debug(`finished scanning & installing certificate in NSS databases in ${ nssDirGlob }`);
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
export function removeCertificateFromNSSCertDB(nssDirGlob: string, certPath: string, certutilPath: string): void {
|
|
64
|
+
export async function removeCertificateFromNSSCertDB(nssDirGlob: string, certPath: string, certutilPath: string): Promise<void> {
|
|
48
65
|
debug(`trying to remove certificates from NSS databases in ${ nssDirGlob }`);
|
|
49
|
-
|
|
66
|
+
for await (const { dir, version } of iterateNSSCertDBs(nssDirGlob)) {
|
|
50
67
|
const dirArg = version === 'modern' ? `sql:${ dir }` : dir;
|
|
51
68
|
try {
|
|
52
69
|
run(certutilPath, ['-A', '-d', dirArg, '-t', 'C,,', '-i', certPath, '-n', 'devcert']);
|
|
53
70
|
} catch (e) {
|
|
54
71
|
debug(`failed to remove ${ certPath } from ${ dir }, continuing. ${ e.toString() }`)
|
|
55
72
|
}
|
|
56
|
-
}
|
|
73
|
+
}
|
|
57
74
|
debug(`finished scanning & installing certificate in NSS databases in ${ nssDirGlob }`);
|
|
58
75
|
}
|
|
59
76
|
|
|
@@ -109,28 +126,37 @@ async function sleep(ms: number) {
|
|
|
109
126
|
*/
|
|
110
127
|
export async function openCertificateInFirefox(firefoxPath: string, certPath: string): Promise<void> {
|
|
111
128
|
debug('Adding devert to Firefox trust stores manually. Launching a webserver to host our certificate temporarily ...');
|
|
112
|
-
let port
|
|
113
|
-
|
|
114
|
-
let { pathname } =
|
|
129
|
+
let port: number;
|
|
130
|
+
const server = http.createServer(async (req, res) => {
|
|
131
|
+
let { pathname } = new URL(req.url);
|
|
115
132
|
if (pathname === '/certificate') {
|
|
116
133
|
res.writeHead(200, { 'Content-type': 'application/x-x509-ca-cert' });
|
|
117
|
-
res.write(
|
|
134
|
+
res.write(fs.readFileSync(certPath));
|
|
118
135
|
res.end();
|
|
119
136
|
} else {
|
|
120
137
|
res.writeHead(200);
|
|
121
|
-
res.write(await UI.firefoxWizardPromptPage(`http://localhost:${
|
|
138
|
+
res.write(await UI.firefoxWizardPromptPage(`http://localhost:${port}/certificate`));
|
|
122
139
|
res.end();
|
|
123
140
|
}
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
});
|
|
142
|
+
port = await new Promise((resolve, reject) => {
|
|
143
|
+
server.on('error', reject);
|
|
144
|
+
server.listen(() => {
|
|
145
|
+
resolve((server.address() as net.AddressInfo).port);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
try {
|
|
149
|
+
debug('Certificate server is up. Printing instructions for user and launching Firefox with hosted certificate URL');
|
|
150
|
+
await UI.startFirefoxWizard(`http://localhost:${port}`);
|
|
151
|
+
run(firefoxPath, [`http://localhost:${ port }`]);
|
|
152
|
+
await UI.waitForFirefoxWizard();
|
|
153
|
+
} finally {
|
|
154
|
+
server.close();
|
|
155
|
+
}
|
|
130
156
|
}
|
|
131
157
|
|
|
132
158
|
export function assertNotTouchingFiles(filepath: string, operation: string): void {
|
|
133
159
|
if (!filepath.startsWith(configDir) && !filepath.startsWith(getLegacyConfigDir())) {
|
|
134
160
|
throw new Error(`Devcert cannot ${ operation } ${ filepath }; it is outside known devcert config directories!`);
|
|
135
161
|
}
|
|
136
|
-
}
|
|
162
|
+
}
|
package/src/platforms/win32.ts
CHANGED
|
@@ -44,7 +44,7 @@ export default class WindowsPlatform implements Platform {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
removeFromTrustStores(certificatePath: string) {
|
|
47
|
+
async removeFromTrustStores(certificatePath: string) {
|
|
48
48
|
debug('removing devcert root from Windows OS trust store');
|
|
49
49
|
try {
|
|
50
50
|
console.warn('Removing old certificates from trust stores. You may be prompted to grant permission for this. It\'s safe to delete old devcert certificates.');
|
|
@@ -61,7 +61,7 @@ export default class WindowsPlatform implements Platform {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
deleteProtectedFiles(filepath: string) {
|
|
64
|
+
async deleteProtectedFiles(filepath: string) {
|
|
65
65
|
assertNotTouchingFiles(filepath, 'delete');
|
|
66
66
|
rm(filepath, { force: true, recursive: true });
|
|
67
67
|
}
|
|
@@ -94,12 +94,12 @@ export default class WindowsPlatform implements Platform {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
private encrypt(text: string, key: string) {
|
|
97
|
-
let cipher = crypto.createCipher('aes256',
|
|
97
|
+
let cipher = crypto.createCipher('aes256', Buffer.from(key));
|
|
98
98
|
return cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
private decrypt(encrypted: string, key: string) {
|
|
102
|
-
let decipher = crypto.createDecipher('aes256',
|
|
102
|
+
let decipher = crypto.createDecipher('aes256', Buffer.from(key));
|
|
103
103
|
return decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
|
|
104
104
|
}
|
|
105
105
|
|
package/src/user-interface.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import readline from 'node:readline';
|
|
2
2
|
import { waitForUser } from './utils';
|
|
3
3
|
|
|
4
4
|
export interface UserInterface {
|
|
@@ -10,6 +10,25 @@ export interface UserInterface {
|
|
|
10
10
|
waitForFirefoxWizard(): Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
async function passwordPrompt(prompt: string): Promise<string> {
|
|
14
|
+
const input = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
input.on('SIGINT', () => {
|
|
20
|
+
reject(new Error('SIGINT'));
|
|
21
|
+
});
|
|
22
|
+
input.question(prompt, (answer) => {
|
|
23
|
+
readline.moveCursor(process.stdout, 0, -1);
|
|
24
|
+
readline.clearLine(process.stdout, 0);
|
|
25
|
+
input.write(prompt + answer.replace(/./g, '*') + '\n');
|
|
26
|
+
input.close();
|
|
27
|
+
resolve(answer);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
13
32
|
const DefaultUI: UserInterface = {
|
|
14
33
|
async getWindowsEncryptionPassword() {
|
|
15
34
|
return await passwordPrompt('devcert password (http://bit.ly/devcert-what-password?):');
|
|
@@ -68,4 +87,4 @@ const DefaultUI: UserInterface = {
|
|
|
68
87
|
}
|
|
69
88
|
}
|
|
70
89
|
|
|
71
|
-
export default DefaultUI;
|
|
90
|
+
export default DefaultUI;
|
package/src/utils.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { execFileSync, ExecFileSyncOptions } from 'child_process';
|
|
2
|
-
import
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
3
5
|
import createDebug from 'debug';
|
|
4
6
|
import path from 'path';
|
|
5
|
-
import sudoPrompt from 'sudo-prompt';
|
|
7
|
+
import sudoPrompt from '@expo/sudo-prompt';
|
|
6
8
|
|
|
7
|
-
import { configPath } from './constants';
|
|
9
|
+
import { configPath, isWindows } from './constants';
|
|
8
10
|
|
|
9
11
|
const debug = createDebug('devcert:util');
|
|
10
12
|
|
|
@@ -40,9 +42,10 @@ export function reportableError(message: string) {
|
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export function mktmp() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const random = randomBytes(6).toString('hex');
|
|
46
|
+
const tmppath = path.join(os.tmpdir(), `tmp-${process.pid}${random}`);
|
|
47
|
+
fs.closeSync(fs.openSync(tmppath, 'w'));
|
|
48
|
+
return tmppath;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
export function sudo(cmd: string): Promise<string | null> {
|
|
@@ -53,3 +56,23 @@ export function sudo(cmd: string): Promise<string | null> {
|
|
|
53
56
|
});
|
|
54
57
|
});
|
|
55
58
|
}
|
|
59
|
+
|
|
60
|
+
const _commands: Record<string, string | null> = {};
|
|
61
|
+
|
|
62
|
+
export function commandExists(command: string): string | null {
|
|
63
|
+
if (_commands[command] !== undefined) {
|
|
64
|
+
return _commands[command];
|
|
65
|
+
}
|
|
66
|
+
const paths = process.env[isWindows ? 'Path' : 'PATH'].split(path.delimiter);
|
|
67
|
+
const extensions = [...(process.env.PATHEXT || '').split(path.delimiter), ''];
|
|
68
|
+
for (const dir of paths) {
|
|
69
|
+
for (const extension of extensions) {
|
|
70
|
+
const filePath = path.join(dir, command + extension);
|
|
71
|
+
try {
|
|
72
|
+
fs.accessSync(filePath, fs.constants.X_OK);
|
|
73
|
+
return (_commands[command] = filePath);
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return (_commands[command] = null);
|
|
78
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
"declaration": true,
|
|
5
5
|
"module": "commonjs",
|
|
6
6
|
"moduleResolution": "node",
|
|
7
|
-
"target": "
|
|
7
|
+
"target": "ES2018",
|
|
8
8
|
"noImplicitAny": true,
|
|
9
9
|
"sourceMap": false,
|
|
10
|
-
"importHelpers":
|
|
10
|
+
"importHelpers": false,
|
|
11
11
|
"inlineSourceMap": true,
|
|
12
12
|
"inlineSources": true,
|
|
13
13
|
"outDir": "dist",
|