diff --git a/.eslintrc b/.eslintrc index 5e5eda4..24f7ed4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -24,5 +24,11 @@ "@typescript-eslint/explicit-member-accessibility": 0, "@typescript-eslint/prefer-includes": 0, "@typescript-eslint/require-await": 1 - } + }, + "overrides": [{ + "files": ["test/**/*.ts"], + "parserOptions": { + "project": "./test/tsconfig.json" + } + }] } diff --git a/cli/commands/remote.ts b/cli/commands/remote.ts index d563416..21c34b3 100644 --- a/cli/commands/remote.ts +++ b/cli/commands/remote.ts @@ -1,5 +1,5 @@ import { Argv, Arguments } from 'yargs'; -import { rootCACertPath, DEFAULT_REMOTE_PORT } from '../../src/constants'; +import { ROOT_CA_CERT_PATH, DEFAULT_REMOTE_PORT } from '../../src/constants'; import * as express from 'express'; import * as https from 'https'; import * as fs from 'fs'; @@ -50,12 +50,12 @@ export const handler = (argv: Arguments): void => { cert: cert.replace(/\\n/g, '\n') }; app.get('/get_remote_certificate', (req, res) => { - if (!fs.existsSync(rootCACertPath)) { + if (!fs.existsSync(ROOT_CA_CERT_PATH)) { throw new Error( - `Could not read the public certificate file ${rootCACertPath}, please check the file exists and try again.` + `Could not read the public certificate file ${ROOT_CA_CERT_PATH}, please check the file exists and try again.` ); } - res.send(fs.readFileSync(rootCACertPath, 'utf8')); + res.send(fs.readFileSync(ROOT_CA_CERT_PATH, 'utf8')); }); const httpsServer = https.createServer(credentials, app); diff --git a/package.json b/package.json index d5c0e2a..1db17d7 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "clean": "rimraf dist", "build": "yarn clean && yarn build:ts && yarn build:api-extract && yarn build:api-docs", "build:ts": "tsc", - "lint": "eslint src --ext ts", + "lint": "eslint . --ext ts", "prepublishOnly": "yarn build", "build:api-docs": "yarn api-documenter markdown -i ./temp -o ./docs", "build:api-extract": "yarn api-extractor run" diff --git a/src/certificate-authority.ts b/src/certificate-authority.ts index 17e59d9..0bb9adf 100644 --- a/src/certificate-authority.ts +++ b/src/certificate-authority.ts @@ -6,16 +6,16 @@ import { import * as createDebug from 'debug'; import { - domainsDir, - rootCADir, + DOMAINS_DIR, + ROOT_CA_DIR, ensureConfigDirs, getLegacyConfigDir, - rootCAKeyPath, - rootCACertPath, - caSelfSignConfig, - opensslSerialFilePath, - opensslDatabaseFilePath, - caVersionFile + ROOT_CA_KEY_PATH, + ROOT_CA_CERT_PATH, + CA_SELF_SIGN_CONFIG_PATH, + OPENSSL_SERIAL_FILE_PATH, + OPENSSL_DB_PATH, + CA_VERSION_FILE_PATH } from './constants'; import currentPlatform from './platforms'; import { openssl, tmpDir } from './utils'; @@ -53,7 +53,7 @@ export default async function installCertificateAuthority( debug(`Generating a CA certificate`); openssl( - `req -new -x509 -config "${caSelfSignConfig}" -key "${rootKeyPath}" -out "${rootCACertPath}" -days ${certOptions.caCertExpiry}`, + `req -new -x509 -config "${CA_SELF_SIGN_CONFIG_PATH}" -key "${rootKeyPath}" -out "${ROOT_CA_CERT_PATH}" -days ${certOptions.caCertExpiry}`, 'generating CA CSR' ); @@ -61,7 +61,7 @@ export default async function installCertificateAuthority( await saveCertificateAuthorityCredentials(rootKeyPath); debug(`Adding the root certificate authority to trust stores`); - await currentPlatform.addToTrustStores(rootCACertPath, options); + await currentPlatform.addToTrustStores(ROOT_CA_CERT_PATH, options); } /** @@ -70,10 +70,10 @@ export default async function installCertificateAuthority( */ function seedConfigFiles(): void { // This is v2 of the devcert certificate authority setup - writeFile(caVersionFile, '2'); + writeFile(CA_VERSION_FILE_PATH, '2'); // OpenSSL CA files - writeFile(opensslDatabaseFilePath, ''); - writeFile(opensslSerialFilePath, '01'); + writeFile(OPENSSL_DB_PATH, ''); + writeFile(OPENSSL_SERIAL_FILE_PATH, '01'); } export async function withCertificateAuthorityCredentials( @@ -89,8 +89,8 @@ export async function withCertificateAuthorityCredentials( const tmp = tmpDir(); const caKeyPath = join(tmp.name, 'ca.key'); const caCertPath = join(caKeyPath, '..', 'ca.crt'); - const caKey = await currentPlatform.readProtectedFile(rootCAKeyPath); - const caCrt = await currentPlatform.readProtectedFile(rootCACertPath); + const caKey = await currentPlatform.readProtectedFile(ROOT_CA_KEY_PATH); + const caCrt = await currentPlatform.readProtectedFile(ROOT_CA_CERT_PATH); writeFile(caKeyPath, caKey); writeFile(caCertPath, caCrt); await cb({ caKeyPath, caCertPath }); @@ -104,13 +104,13 @@ async function saveCertificateAuthorityCredentials( ): Promise { debug(`Saving devcert's certificate authority credentials`); const key = readFile(keypath, 'utf-8'); - await currentPlatform.writeProtectedFile(rootCAKeyPath, key); + await currentPlatform.writeProtectedFile(ROOT_CA_KEY_PATH, key); } function certErrors(): string { try { openssl( - `x509 -in "${rootCACertPath}" -noout`, + `x509 -in "${ROOT_CA_CERT_PATH}" -noout`, 'checking for certificate errors' ); return ''; @@ -143,10 +143,10 @@ export async function ensureCACertReadable( */ try { const caFileContents = await currentPlatform.readProtectedFile( - rootCACertPath + ROOT_CA_CERT_PATH ); - currentPlatform.deleteProtectedFiles(rootCACertPath); - writeFile(rootCACertPath, caFileContents); + currentPlatform.deleteProtectedFiles(ROOT_CA_CERT_PATH); + writeFile(ROOT_CA_CERT_PATH, caFileContents); } catch (e) { return installCertificateAuthority(options, certOptions); } @@ -174,8 +174,8 @@ export async function ensureCACertReadable( * @public */ export function uninstall(): void { - currentPlatform.removeFromTrustStores(rootCACertPath); - currentPlatform.deleteProtectedFiles(domainsDir); - currentPlatform.deleteProtectedFiles(rootCADir); + currentPlatform.removeFromTrustStores(ROOT_CA_CERT_PATH); + currentPlatform.deleteProtectedFiles(DOMAINS_DIR); + currentPlatform.deleteProtectedFiles(ROOT_CA_DIR); currentPlatform.deleteProtectedFiles(getLegacyConfigDir()); } diff --git a/src/constants.ts b/src/constants.ts index 9e13c33..1e33c14 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -12,37 +12,38 @@ import applicationConfigPath = require('application-config-path'); import * as _createDebug from 'debug'; const debug = _createDebug('devcert:constants'); + // Platform shortcuts -export const isMac = process.platform === 'darwin'; -export const isLinux = process.platform === 'linux'; -export const isWindows = process.platform === 'win32'; +export const IS_MAC = process.platform === 'darwin'; +export const IS_LINUX = process.platform === 'linux'; +export const IS_WINDOWS = process.platform === 'win32'; // Common paths -export const configDir = applicationConfigPath('devcert'); -export const configPath: (...pathSegments: string[]) => string = path.join.bind( - path, - configDir -); +export const CONFIG_DIR = applicationConfigPath('devcert'); + +export const makeConfigPath: ( + ...pathSegments: string[] +) => string = path.join.bind(path, CONFIG_DIR); export const DEFAULT_REMOTE_PORT = 2702; -export const domainsDir = configPath('domains'); +export const DOMAINS_DIR = makeConfigPath('domains'); -export const caVersionFile = configPath('devcert-ca-version'); -export const opensslSerialFilePath = configPath( +export const CA_VERSION_FILE_PATH = makeConfigPath('devcert-ca-version'); +export const OPENSSL_SERIAL_FILE_PATH = makeConfigPath( 'certificate-authority', 'serial' ); -export const opensslDatabaseFilePath = configPath( +export const OPENSSL_DB_PATH = makeConfigPath( 'certificate-authority', 'index.txt' ); -export const opensslConfigDir = path.join( +export const OPENSSL_CONFIG_DIR = path.join( __dirname, '../../openssl-configurations/' ); -export const caSelfSignConfig = path.join( - opensslConfigDir, +export const CA_SELF_SIGN_CONFIG_PATH = path.join( + OPENSSL_CONFIG_DIR, 'certificate-authority-self-signing.conf' ); @@ -64,7 +65,7 @@ export async function withDomainSigningRequestConfig( 'domain-certificate-signing-requests.conf' ); const source = readFile( - path.join(opensslConfigDir, 'domain-certificate-signing-requests.conf'), + path.join(OPENSSL_CONFIG_DIR, 'domain-certificate-signing-requests.conf'), 'utf-8' ); const template = makeTemplate(source); @@ -86,15 +87,15 @@ export async function withDomainCertificateConfig( const tmp = tmpDir(); const tmpFile = path.join(tmp.name, 'ca.cfg'); const source = readFile( - path.join(opensslConfigDir, 'domain-certificates.conf'), + path.join(OPENSSL_CONFIG_DIR, 'domain-certificates.conf'), 'utf-8' ); const template = makeTemplate(source); const result = template({ commonName, altNames: includeWildcards([commonName, ...alternativeNames]), - serialFile: opensslSerialFilePath, - databaseFile: opensslDatabaseFilePath, + serialFile: OPENSSL_SERIAL_FILE_PATH, + databaseFile: OPENSSL_DB_PATH, domainDir: pathForDomain(commonName) }); writeFile(tmpFile, eol.auto(result)); @@ -107,22 +108,22 @@ export async function withDomainCertificateConfig( // confTemplate = confTemplate.replace(/SERIAL_PATH/, configPath('serial').replace(/\\/g, '\\\\')); // confTemplate = eol.auto(confTemplate); -export const rootCADir = configPath('certificate-authority'); -export const rootCAKeyPath = path.join(rootCADir, 'private-key.key'); -export const rootCACertPath = path.join(rootCADir, 'certificate.cert'); +export const ROOT_CA_DIR = makeConfigPath('certificate-authority'); +export const ROOT_CA_KEY_PATH = path.join(ROOT_CA_DIR, 'private-key.key'); +export const ROOT_CA_CERT_PATH = path.join(ROOT_CA_DIR, 'certificate.cert'); -debug('rootCACertPath', rootCACertPath); -debug('rootCAKeyPath', rootCAKeyPath); -debug('rootCADir', rootCADir); +debug('rootCACertPath', ROOT_CA_CERT_PATH); +debug('rootCAKeyPath', ROOT_CA_KEY_PATH); +debug('rootCADir', ROOT_CA_DIR); // Exposed for uninstallation purposes. export function getLegacyConfigDir(): string { - if (isWindows && process.env.LOCALAPPDATA) { + if (IS_WINDOWS && process.env.LOCALAPPDATA) { return path.join(process.env.LOCALAPPDATA, 'devcert', 'config'); } else { const uid = process.getuid && process.getuid(); const userHome = - isLinux && uid === 0 + IS_LINUX && uid === 0 ? path.resolve('/usr/local/share') : require('os').homedir(); return path.join(userHome, '.config', 'devcert'); @@ -130,9 +131,9 @@ export function getLegacyConfigDir(): string { } export function ensureConfigDirs(): void { - mkdirp(configDir); - mkdirp(domainsDir); - mkdirp(rootCADir); + mkdirp(CONFIG_DIR); + mkdirp(DOMAINS_DIR); + mkdirp(ROOT_CA_DIR); } ensureConfigDirs(); diff --git a/src/index.ts b/src/index.ts index f280ce0..c81387d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,12 +18,12 @@ import { sync as commandExists } from 'command-exists'; import * as rimraf from 'rimraf'; import { version } from '../package.json'; import { - isMac, - isLinux, - isWindows, - domainsDir, - rootCAKeyPath, - rootCACertPath, + IS_MAC, + IS_LINUX, + IS_WINDOWS, + DOMAINS_DIR, + ROOT_CA_KEY_PATH, + ROOT_CA_CERT_PATH, DEFAULT_REMOTE_PORT } from './constants'; import currentPlatform from './platforms'; @@ -320,7 +320,7 @@ async function certificateForImpl< Object.assign(UI, options.ui); } - if (!isMac && !isLinux && !isWindows) { + if (!IS_MAC && !IS_LINUX && !IS_WINDOWS) { throw new Error(`Platform not supported: "${process.platform}"`); } @@ -333,7 +333,7 @@ async function certificateForImpl< const domainKeyPath = keyPathForDomain(commonName); const domainCertPath = certPathForDomain(commonName); - if (!exists(rootCAKeyPath)) { + if (!exists(ROOT_CA_KEY_PATH)) { debug( 'Root CA is not installed yet, so it must be our first run. Installing root CA ...' ); @@ -389,8 +389,9 @@ async function certificateForImpl< cert: readFile(domainCertPath) } as IReturnData; if (options.getCaBuffer) - ((ret as unknown) as CaBuffer).ca = readFile(rootCACertPath); - if (options.getCaPath) ((ret as unknown) as CaPath).caPath = rootCACertPath; + ((ret as unknown) as CaBuffer).ca = readFile(ROOT_CA_CERT_PATH); + if (options.getCaPath) + ((ret as unknown) as CaPath).caPath = ROOT_CA_CERT_PATH; return ret; } @@ -677,7 +678,7 @@ export function hasCertificateFor(commonName: string): boolean { * @alpha */ export function configuredDomains(): string[] { - return readdir(domainsDir); + return readdir(DOMAINS_DIR); } /** diff --git a/src/platforms/shared.ts b/src/platforms/shared.ts index 0d92b8c..b91254a 100644 --- a/src/platforms/shared.ts +++ b/src/platforms/shared.ts @@ -8,19 +8,23 @@ import { existsSync } from 'fs'; import { sync as glob } from 'glob'; import { readFileSync as readFile, existsSync as exists } from 'fs'; import { run } from '../utils'; -import { isMac, isLinux, configDir, getLegacyConfigDir } from '../constants'; +import { IS_MAC, IS_LINUX, CONFIG_DIR, getLegacyConfigDir } from '../constants'; import UI from '../user-interface'; import { execSync as exec } from 'child_process'; - +import { homedir } from 'os'; const debug = createDebug('devcert:platforms:shared'); -export const HOME = process.env.HOME - ? process.env.HOME - : (function(): never { - throw new Error( - 'HOME environment variable was not set. It should be something like "/Users/exampleName"' - ); - })(); +function determineHomeDir(): string { + if (process.env.HOME) return process.env.HOME; + if (typeof process.env.HOME !== 'undefined') { + throw new Error( + 'HOME environment variable was not set. It should be something like "/Users/exampleName"' + ); + } + return homedir(); +} + +export const HOME = determineHomeDir(); /** * Given a directory or glob pattern of directories, run a callback for each db @@ -120,7 +124,7 @@ function isFirefoxOpen(): boolean { // never needs to check this, because it doesn't update the NSS DB // automaticaly. assert( - isMac || isLinux, + IS_MAC || IS_LINUX, 'checkForOpenFirefox was invoked on a platform other than Mac or Linux' ); return exec('ps aux').indexOf('firefox') > -1; @@ -193,7 +197,7 @@ export function assertNotTouchingFiles( operation: string ): void { if ( - !filepath.startsWith(configDir) && + !filepath.startsWith(CONFIG_DIR) && !filepath.startsWith(getLegacyConfigDir()) ) { throw new Error( diff --git a/src/remote-utils.ts b/src/remote-utils.ts index 4906402..3665f28 100644 --- a/src/remote-utils.ts +++ b/src/remote-utils.ts @@ -1,5 +1,5 @@ import fetch from 'node-fetch'; -import { rootCACertPath } from '../src/constants'; +import { ROOT_CA_CERT_PATH } from '../src/constants'; import { Agent } from 'https'; import * as fs from 'fs'; @@ -7,12 +7,14 @@ import * as fs from 'fs'; * Returns the agent for fetch requests. */ function _getAgent(): Agent { - if (!fs.existsSync(rootCACertPath)) { + if (!fs.existsSync(ROOT_CA_CERT_PATH)) { throw new Error( - `Public certificate file ${rootCACertPath} does not exist.` + `Public certificate file ${ROOT_CA_CERT_PATH} does not exist.` ); } - const rootCACertData = fs.readFileSync(rootCACertPath, { encoding: 'utf-8' }); + const rootCACertData = fs.readFileSync(ROOT_CA_CERT_PATH, { + encoding: 'utf-8' + }); return new Agent({ ca: rootCACertData diff --git a/src/utils.ts b/src/utils.ts index 9541f09..ca9a9da 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,7 +7,7 @@ import * as execa from 'execa'; import * as assert from 'assert'; import * as chalk from 'chalk'; -import { configPath, domainsDir } from './constants'; +import { makeConfigPath, DOMAINS_DIR } from './constants'; import { existsSync } from 'fs'; const debug = createDebug('devcert:util'); @@ -18,7 +18,7 @@ export function openssl(cmd: string, description: string): string { stdio: 'pipe', env: Object.assign( { - RANDFILE: path.join(configPath('.rnd')) + RANDFILE: path.join(makeConfigPath('.rnd')) }, process.env ) @@ -85,9 +85,9 @@ export function pathForDomain( domain: string, ...pathSegments: string[] ): string { - assert(typeof domainsDir === 'string', 'domainsDir must be a string'); - assert(domainsDir.length > 0, 'domainsDir must be > 0 length'); - return path.join(domainsDir, domain, ...pathSegments); + assert(typeof DOMAINS_DIR === 'string', 'domainsDir must be a string'); + assert(DOMAINS_DIR.length > 0, 'domainsDir must be > 0 length'); + return path.join(DOMAINS_DIR, domain, ...pathSegments); } export function certPathForDomain(commonName: string): string {