diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 4513918a5..0bd8b60e9 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -41,15 +41,6 @@ import fs from 'fs-extra'; enableMapSet(); -if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') { - // By default Node.JS has its internal certificate storage and doesn't use - // the system store. Because of this, it's impossible to access ondemand / devserver - // which are signed using some internal self-issued FB certificates. These certificates - // are automatically installed to MacOS system store on FB machines, so here we're using - // this "mac-ca" library to load them into Node.JS. - electronRequire('mac-ca'); -} - async function start() { const app = remote.app; const execPath = process.execPath || remote.process.execPath; diff --git a/desktop/babel-transformer/src/electron-requires.tsx b/desktop/babel-transformer/src/electron-requires.tsx index 4adca0f06..ec59ff3b2 100644 --- a/desktop/babel-transformer/src/electron-requires.tsx +++ b/desktop/babel-transformer/src/electron-requires.tsx @@ -46,10 +46,10 @@ export const BUILTINS = [ 'repl', 'timers', 'perf_hooks', + 'worker_threads', + 'encoding', 'fsevents', './fsevents.node', - // MWE node-fetch looks strange here, not sure what the effect of changing that would be - 'node-fetch', // jest is referred to in source code, like in TestUtils, but we don't want to ever bundle it up! 'jest', '@testing-library/react', diff --git a/desktop/babel-transformer/src/prefixed-node-requires.tsx b/desktop/babel-transformer/src/prefixed-node-requires.tsx new file mode 100644 index 000000000..b7957e125 --- /dev/null +++ b/desktop/babel-transformer/src/prefixed-node-requires.tsx @@ -0,0 +1,40 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {CallExpression} from '@babel/types'; +import {NodePath} from '@babel/traverse'; + +// Core modules can be required as `node:fs` to bypass teh require cache +// https://nodejs.org/api/modules.html#core-modules +// It is not supported for "require" until Node v14.18. +// TODO: Remove this transform when we upgrade electron and node.js +const bypassRequireCachePrefix = 'node:'; + +module.exports = () => ({ + name: 'change-require-to-electronRequire-in-electron-app', + visitor: { + CallExpression(path: NodePath) { + const node = path.node; + if ( + node.type === 'CallExpression' && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node.arguments.length === 1 && + node.arguments[0].type === 'StringLiteral' + ) { + const source = node.arguments[0].value; + if (source.startsWith(bypassRequireCachePrefix)) { + node.arguments[0].value = source.substring( + bypassRequireCachePrefix.length, + ); + } + } + }, + }, +}); diff --git a/desktop/babel-transformer/src/transform-app.tsx b/desktop/babel-transformer/src/transform-app.tsx index 022d3ae11..c5e4cb1e9 100644 --- a/desktop/babel-transformer/src/transform-app.tsx +++ b/desktop/babel-transformer/src/transform-app.tsx @@ -13,6 +13,7 @@ import {default as getCacheKey} from './get-cache-key'; const presets = [require('@babel/preset-react')]; const plugins = [ require('./fsevents-dynamic-imports'), + require('./prefixed-node-requires'), require('./electron-requires'), require('./import-react'), require('./app-flipper-requires'), diff --git a/desktop/babel-transformer/src/transform-plugin.tsx b/desktop/babel-transformer/src/transform-plugin.tsx index d8ad39836..bce751feb 100644 --- a/desktop/babel-transformer/src/transform-plugin.tsx +++ b/desktop/babel-transformer/src/transform-plugin.tsx @@ -12,6 +12,7 @@ import {default as doTransform} from './transform'; const presets = [require('@babel/preset-react')]; const plugins = [ require('./fsevents-dynamic-imports'), + require('./prefixed-node-requires'), require('./electron-requires'), require('./plugin-flipper-requires'), require('./fb-stubs'), diff --git a/desktop/babel-transformer/src/transform-server-add-on.tsx b/desktop/babel-transformer/src/transform-server-add-on.tsx index 071e68069..a833332c6 100644 --- a/desktop/babel-transformer/src/transform-server-add-on.tsx +++ b/desktop/babel-transformer/src/transform-server-add-on.tsx @@ -25,6 +25,7 @@ const presets = [ const plugins = [ require('./fsevents-dynamic-imports'), + require('./prefixed-node-requires'), require('./electron-requires'), require('./plugin-flipper-requires'), require('./fb-stubs'), diff --git a/desktop/babel-transformer/src/transform-server-prod.tsx b/desktop/babel-transformer/src/transform-server-prod.tsx index 2fc6df3bd..6549af44e 100644 --- a/desktop/babel-transformer/src/transform-server-prod.tsx +++ b/desktop/babel-transformer/src/transform-server-prod.tsx @@ -29,6 +29,7 @@ const presets = [ // (which effectively makes them external, as electronRequire === require, but not rolled up with Metro) const plugins = [ require('./fsevents-dynamic-imports'), + require('./prefixed-node-requires'), require('./electron-requires'), require('./plugin-flipper-requires'), require('./fb-stubs'), diff --git a/desktop/flipper-server-core/package.json b/desktop/flipper-server-core/package.json index 8a4227ab6..84b24d731 100644 --- a/desktop/flipper-server-core/package.json +++ b/desktop/flipper-server-core/package.json @@ -27,6 +27,7 @@ "js-base64": "^3.7.2", "lodash.memoize": "^4.1.2", "node-fetch": "^3.2.4", + "node-forge": "^0.10.0", "open": "^8.3.0", "openssl-wrapper": "^0.3.4", "promisify-child-process": "^4.1.1", @@ -48,6 +49,7 @@ "@types/invariant": "^2.2.35", "@types/memorystream": "^0.3.0", "@types/node": "^17.0.31", + "@types/node-forge": "^0.10", "@types/rimraf": "^3.0.2", "@types/rsocket-core": "^0.0.7", "@types/rsocket-tcp-server": "^0.0.2", diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index 43afbfb64..957ab72f2 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -7,6 +7,7 @@ * @format */ +import './utils/macCa'; import './utils/fetch-polyfill'; import EventEmitter from 'events'; import {ServerController} from './comms/ServerController'; diff --git a/desktop/flipper-server-core/src/utils/macCa.tsx b/desktop/flipper-server-core/src/utils/macCa.tsx new file mode 100644 index 000000000..d8f6d4d6b --- /dev/null +++ b/desktop/flipper-server-core/src/utils/macCa.tsx @@ -0,0 +1,128 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +// Copy-paste from https://github.com/jfromaniello/mac-ca +// Babel does not want to transform https://github.com/jfromaniello/mac-ca/blob/fb2b2824c91e3c7f7ffdc8329dd8992e172b967b/lib/formatter.js#L2 because "package" is a reserved word +// It is easier to copy the package then add ye another babel transform to change the reserved word on the fly + +import https from 'https'; +import forge from 'node-forge'; + +const validFormats = { + der: 0, + pem: 1, + txt: 2, + asn1: 3, +}; + +function myASN(pem: any) { + const der = forge.pki.pemToDer(pem); + const asn1 = forge.asn1; + // @ts-expect-error + let crt = asn1.fromDer(der.data.toString('binary')).value[0].value; + const serial = crt[0]; + const hasSerial = + serial.tagClass === asn1.Class.CONTEXT_SPECIFIC && + serial.type === 0 && + serial.constructed; + crt = crt.slice(hasSerial); + return { + serial: crt[0], + issuer: crt[2], + valid: crt[3], + subject: crt[4], + }; +} + +function txtFormat(pem: string) { + const crt = myASN(pem); + const d = new Date(); + return `Subject\t${crt.subject.value + .map((rdn: any) => rdn.value[0].value[1].value) + .join('/')} +Valid\t${crt.valid.value.map((date: any) => date.value).join(' - ')} +Saved\t${d.toLocaleDateString()} ${d + .toTimeString() + .replace(/\s*\(.*\)\s*/, '')} by mac-ca +${pem}`; +} + +const transform = function (format: any) { + return function (pem: any) { + try { + switch (format) { + case validFormats.der: + return forge.pki.pemToDer(pem); + case validFormats.pem: + return pem; + case validFormats.txt: + return txtFormat(pem); + case validFormats.asn1: + return myASN(pem); + default: + return forge.pki.certificateFromPem(pem); + } + } catch (er) { + return; + } + }; +}; + +if (process.platform !== 'darwin') { + module.exports.all = () => []; + module.exports.each = () => {}; +} else { + const child_process = require('child_process'); + + const splitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; + const systemRootCertsPath = + '/System/Library/Keychains/SystemRootCertificates.keychain'; + const args = ['find-certificate', '-a', '-p']; + + // eslint-disable-next-line node/no-sync + const allTrusted = child_process + .spawnSync('/usr/bin/security', args) + .stdout.toString() + .split(splitPattern); + + // eslint-disable-next-line node/no-sync + const allRoot = child_process + .spawnSync('/usr/bin/security', args.concat(systemRootCertsPath)) + .stdout.toString() + .split(splitPattern); + + https.globalAgent.options.ca = https.globalAgent.options.ca || []; + + const ca = https.globalAgent.options.ca; + + function duplicated(cert: any, index: any, arr: any) { + return arr.indexOf(cert) === index; + } + + const all = allTrusted.concat(allRoot); + + all.filter(duplicated).forEach((cert: any) => (ca as any).push(cert)); + + module.exports.der2 = validFormats; + + module.exports.all = function (format: any) { + return all.map(transform(format)).filter((c: any) => c); + }; + + module.exports.each = function (format: any, callback: any) { + if (typeof format === 'function') { + callback = format; + format = undefined; + } + return all + .map(transform(format)) + .filter((c: any) => c) + .forEach(callback); + }; +} diff --git a/desktop/flipper-server/package.json b/desktop/flipper-server/package.json index e5f3964f0..67d95004b 100644 --- a/desktop/flipper-server/package.json +++ b/desktop/flipper-server/package.json @@ -8,19 +8,13 @@ "bin": "server.js", "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", - "dependenciesComment": "mac-ca is required dynamically for darwin, node-fetch is treated special in electron-requires, not sure why", - "dependencies": { - "exit-hook": "^2.1.1", - "http-proxy": "^1.18.1", - "mac-ca": "^1.0.6", - "node-fetch": "^2.6.7", - "ws": "^8.5.0", - "xdg-basedir": "^4" - }, + "dependencies": {}, "devDependencies": { "@types/express": "^4.17.13", "@types/http-proxy": "^1.17.8", "@types/node": "^17.0.31", + "exit-hook": "^2.1.1", + "http-proxy": "^1.18.1", "chalk": "^4", "express": "^4.17.3", "flipper-common": "0.0.0", @@ -31,7 +25,9 @@ "metro": "^0.70.2", "open": "^8.3.0", "p-filter": "^2.1.0", - "yargs": "^17.0.1" + "yargs": "^17.0.1", + "ws": "^8.5.0", + "xdg-basedir": "^4" }, "peerDependencies": {}, "scripts": { diff --git a/desktop/flipper-server/src/startFlipperServer.tsx b/desktop/flipper-server/src/startFlipperServer.tsx index 906f73ca6..a3371747b 100644 --- a/desktop/flipper-server/src/startFlipperServer.tsx +++ b/desktop/flipper-server/src/startFlipperServer.tsx @@ -40,15 +40,6 @@ export async function startFlipperServer( settingsString: string, enableLauncherSettings: boolean, ): Promise { - if (os.platform() === 'darwin') { - // By default Node.JS has its internal certificate storage and doesn't use - // the system store. Because of this, it's impossible to access ondemand / devserver - // which are signed using some internal self-issued FB certificates. These certificates - // are automatically installed to MacOS system store on FB machines, so here we're using - // this "mac-ca" library to load them into Node.JS. - electronRequire('mac-ca'); - } - const execPath = process.execPath; const appPath = rootDir; const isProduction = diff --git a/desktop/static/package.json b/desktop/static/package.json index e07bfc79e..25303cb7a 100644 --- a/desktop/static/package.json +++ b/desktop/static/package.json @@ -7,7 +7,6 @@ "dependencies": { "electron-devtools-installer": "^3.2.0", "fix-path": "^3.0.0", - "mac-ca": "^1.0.6", "mkdirp": "^1.0.4", "node-fetch": "^2.6.7", "ws": "^8.5.0", diff --git a/desktop/yarn.lock b/desktop/yarn.lock index da3566b76..2cd2ed904 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -2764,6 +2764,13 @@ "@types/node" "*" form-data "^3.0.0" +"@types/node-forge@^0.10": + version "0.10.10" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.10.10.tgz#07ffccf0f7f3ebb97de67446555912803be50e7b" + integrity sha512-iixn5bedlE9fm/5mN7fPpXraXlxCVrnNWHZekys8c5fknridLVWGnNRqlaWpenwaijIuB3bNI0lEOm+JD6hZUA== + dependencies: + "@types/node" "*" + "@types/node@*": version "16.11.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.0.tgz#4b95f2327bacd1ef8f08d8ceda193039c5d7f52e" @@ -8806,13 +8813,6 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= -mac-ca@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/mac-ca/-/mac-ca-1.0.6.tgz#89860edfeebcc4593567044281ab3500961ec15f" - integrity sha512-uuCaT+41YtIQlDDvbigP1evK1iUk97zRirP9+8rZJz8x0eIQZG8Z7YQegMTsCiMesLPb6LBgCS95uyAvVA1tmg== - dependencies: - node-forge "^0.10.0" - make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"