import createDebug from 'debug' ; import crypto from 'crypto' ; import { writeFileSync as write , readFileSync as read } from 'fs' ; import { sync as rimraf } from 'rimraf' ; import { Options } from '../index' ; import { assertNotTouchingFiles , openCertificateInFirefox } from './shared' ; import { Platform } from '.' ; import { run , sudo } from '../utils' ; import UI from '../user-interface' ; const debug = createDebug ( 'devcert:platforms:windows' ) ; let encryptionKey ; export default class WindowsPlatform implements Platform { private HOST_FILE_PATH = 'C:\\Windows\\System32\\Drivers\\etc\\hosts' ; /**
     * Windows is at least simple. Like macOS, most applications will delegate to
     * the system trust store, which is updated with the confusingly named
     * `certutil` exe (not the same as the NSS/Mozilla certutil). Firefox does it's
     * own thing as usual, and getting a copy of NSS certutil onto the Windows
     * machine to try updating the Firefox store is basically a nightmare, so we
     * don't even try it - we just bail out to the GUI.
     */ async addToTrustStores ( certificatePath , options = { } ) { debug ( 'adding devcert root to Windows OS trust store' ) ; try { run ( 'certutil' , [ '-addstore' , '-user' , 'root' , certificatePath ] ) ; } catch ( e ) { e . output . map ( ( buffer ) => { if ( buffer ) { console . log ( buffer . toString ( ) ) ; } } ) ; } debug ( 'adding devcert root to Firefox trust store' ) ; try { await openCertificateInFirefox ( 'start firefox' , certificatePath ) ; } catch { debug ( 'Error opening Firefox, most likely Firefox is not installed' ) ; } } removeFromTrustStores ( certificatePath ) { debug ( 'removing devcert root from Windows OS trust store' ) ; try { 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.' ) ; run ( 'certutil' , [ '-delstore' , '-user' , 'root' , 'devcert' ] ) ; } catch ( e ) { debug ( `failed to remove ${ certificatePath } from Windows OS trust store, continuing. ${ e . toString ( ) }` ) ; } } async addDomainToHostFileIfMissing ( domain ) { let hostsFileContents = read ( this . HOST_FILE_PATH , 'utf8' ) ; if ( ! hostsFileContents . includes ( domain ) ) { await sudo ( `echo 127.0.0.1  ${ domain } >> ${ this . HOST_FILE_PATH }` ) ; } } deleteProtectedFiles ( filepath ) { assertNotTouchingFiles ( filepath , 'delete' ) ; rimraf ( filepath ) ; } async readProtectedFile ( filepath ) { assertNotTouchingFiles ( filepath , 'read' ) ; if ( ! encryptionKey ) { encryptionKey = await UI . getWindowsEncryptionPassword ( ) ; } try { return this . decrypt ( read ( filepath , 'utf8' ) , encryptionKey ) ; } catch ( e ) { if ( e . message . indexOf ( 'bad decrypt' ) >= - 1 ) { encryptionKey = null ; return await this . readProtectedFile ( filepath ) ; } throw e ; } } async writeProtectedFile ( filepath , contents ) { assertNotTouchingFiles ( filepath , 'write' ) ; if ( ! encryptionKey ) { encryptionKey = await UI . getWindowsEncryptionPassword ( ) ; } let encryptedContents = this . encrypt ( contents , encryptionKey ) ; write ( filepath , encryptedContents ) ; } private encrypt ( text , key ) { let cipher = crypto . createCipher ( 'aes256' , new Buffer ( key ) ) ; return cipher . update ( text , 'utf8' , 'hex' ) + cipher . final ( 'hex' ) ; } private decrypt ( encrypted , key ) { let decipher = crypto . createDecipher ( 'aes256' , new Buffer ( key ) ) ; return decipher . update ( encrypted , 'hex' , 'utf8' ) + decipher . final ( 'utf8' ) ; } }