Browse Source

docs: update checklists (#32902)

Erick Zhao 3 years ago
parent
commit
cc0eb7b908
2 changed files with 195 additions and 168 deletions
  1. 37 29
      docs/tutorial/performance.md
  2. 158 139
      docs/tutorial/security.md

+ 37 - 29
docs/tutorial/performance.md

@@ -1,3 +1,11 @@
+---
+title: Performance
+description: A set of guidelines for building performant Electron apps
+slug: performance
+hide_title: true
+toc_max_heading_level: 3
+---
+
 # Performance
 
 Developers frequently ask about strategies to optimize the performance of
@@ -49,7 +57,7 @@ at once, consider the [Chrome Tracing](https://www.chromium.org/developers/how-t
 * [Get Started With Analyzing Runtime Performance][chrome-devtools-tutorial]
 * [Talk: "Visual Studio Code - The First Second"][vscode-first-second]
 
-## Checklist
+## Checklist: Performance recommendations
 
 Chances are that your app could be a little leaner, faster, and generally less
 resource-hungry if you attempt these steps.
@@ -62,7 +70,7 @@ resource-hungry if you attempt these steps.
 6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests)
 7. [Bundle your code](#7-bundle-your-code)
 
-## 1) Carelessly including modules
+### 1. Carelessly including modules
 
 Before adding a Node.js module to your application, examine said module. How
 many dependencies does that module include? What kind of resources does
@@ -70,7 +78,7 @@ it need to simply be called in a `require()` statement? You might find
 that the module with the most downloads on the NPM package registry or the most stars on GitHub
 is not in fact the leanest or smallest one available.
 
-### Why?
+#### Why?
 
 The reasoning behind this recommendation is best illustrated with a real-world
 example. During the early days of Electron, reliable detection of network
@@ -99,7 +107,7 @@ running Linux might be bad news for your app's performance. In this particular
 example, the correct solution was to use no module at all, and to instead use
 connectivity checks included in later versions of Chromium.
 
-### How?
+#### How?
 
 When considering a module, we recommend that you check:
 
@@ -128,7 +136,7 @@ In this example, on the author's machine, we saw that loading `request` took
 almost half a second, whereas `node-fetch` took dramatically less memory
 and less than 50ms.
 
-## 2) Loading and running code too soon
+### 2. Loading and running code too soon
 
 If you have expensive setup operations, consider deferring those. Inspect all
 the work being executed right after the application starts. Instead of firing
@@ -141,7 +149,7 @@ using the same strategy _and_ are using sizable modules that you do not
 immediately need, apply the same strategy and defer loading to a more
 opportune time.
 
-### Why?
+#### Why?
 
 Loading modules is a surprisingly expensive operation, especially on Windows.
 When your app starts, it should not make users wait for operations that are
@@ -157,14 +165,14 @@ immediately display the file to you without any code highlighting, prioritizing
 your ability to interact with the text. Once it has done that work, it will
 move on to code highlighting.
 
-### How?
+#### How?
 
 Let's consider an example and assume that your application is parsing files
 in the fictitious `.foo` format. In order to do that, it relies on the
 equally fictitious `foo-parser` module. In traditional Node.js development,
 you might write code that eagerly loads dependencies:
 
-```js
+```js title='parser.js'
 const fs = require('fs')
 const fooParser = require('foo-parser')
 
@@ -187,7 +195,7 @@ In the above example, we're doing a lot of work that's being executed as soon
 as the file is loaded. Do we need to get parsed files right away? Could we
 do this work a little later, when `getParsedFiles()` is actually called?
 
-```js
+```js title='parser.js'
 // "fs" is likely already being loaded, so the `require()` call is cheap
 const fs = require('fs')
 
@@ -223,7 +231,7 @@ module.exports = { parser }
 In short, allocate resources "just in time" rather than allocating them all
 when your app starts.
 
-## 3) Blocking the main process
+### 3. Blocking the main process
 
 Electron's main process (sometimes called "browser process") is special: It is
 the parent process to all your app's other processes and the primary process
@@ -235,7 +243,7 @@ Under no circumstances should you block this process and the UI thread with
 long-running operations. Blocking the UI thread means that your entire app
 will freeze until the main process is ready to continue processing.
 
-### Why?
+#### Why?
 
 The main process and its UI thread are essentially the control tower for major
 operations inside your app. When the operating system tells your app about a
@@ -246,31 +254,31 @@ the GPU process about that – once again going through the main process.
 Electron and Chromium are careful to put heavy disk I/O and CPU-bound operations
 onto new threads to avoid blocking the UI thread. You should do the same.
 
-### How?
+#### How?
 
 Electron's powerful multi-process architecture stands ready to assist you with
 your long-running tasks, but also includes a small number of performance traps.
 
-1) For long running CPU-heavy tasks, make use of
+1. For long running CPU-heavy tasks, make use of
 [worker threads][worker-threads], consider moving them to the BrowserWindow, or
 (as a last resort) spawn a dedicated process.
 
-2) Avoid using the synchronous IPC and the `remote` module as much as possible.
-While there are legitimate use cases, it is far too easy to unknowingly block
-the UI thread using the `remote` module.
+2. Avoid using the synchronous IPC and the `@electron/remote` module as much
+as possible. While there are legitimate use cases, it is far too easy to
+unknowingly block the UI thread.
 
-3) Avoid using blocking I/O operations in the main process. In short, whenever
+3. Avoid using blocking I/O operations in the main process. In short, whenever
 core Node.js modules (like `fs` or `child_process`) offer a synchronous or an
 asynchronous version, you should prefer the asynchronous and non-blocking
 variant.
 
-## 4) Blocking the renderer process
+### 4. Blocking the renderer process
 
 Since Electron ships with a current version of Chrome, you can make use of the
 latest and greatest features the Web Platform offers to defer or offload heavy
 operations in a way that keeps your app smooth and responsive.
 
-### Why?
+#### Why?
 
 Your app probably has a lot of JavaScript to run in the renderer process. The
 trick is to execute operations as quickly as possible without taking away
@@ -280,7 +288,7 @@ at 60fps.
 Orchestrating the flow of operations in your renderer's code is
 particularly useful if users complain about your app sometimes "stuttering".
 
-### How?
+#### How?
 
 Generally speaking, all advice for building performant web apps for modern
 browsers apply to Electron's renderers, too. The two primary tools at your
@@ -300,14 +308,14 @@ some caveats to consider – consult Electron's
 for any operation that requires a lot of CPU power for an extended period of
 time.
 
-## 5) Unnecessary polyfills
+### 5. Unnecessary polyfills
 
 One of Electron's great benefits is that you know exactly which engine will
 parse your JavaScript, HTML, and CSS. If you're re-purposing code that was
 written for the web at large, make sure to not polyfill features included in
 Electron.
 
-### Why?
+#### Why?
 
 When building a web application for today's Internet, the oldest environments
 dictate what features you can and cannot use. Even though Electron supports
@@ -323,7 +331,7 @@ It is rare for a JavaScript-based polyfill to be faster than the equivalent
 native feature in Electron. Do not slow down your Electron app by shipping your
 own version of standard web platform features.
 
-### How?
+#### How?
 
 Operate under the assumption that polyfills in current versions of Electron
 are unnecessary. If you have doubts, check [caniuse.com](https://caniuse.com/)
@@ -338,12 +346,12 @@ If you're using a transpiler/compiler like TypeScript, examine its configuration
 and ensure that you're targeting the latest ECMAScript version supported by
 Electron.
 
-## 6) Unnecessary or blocking network requests
+### 6. Unnecessary or blocking network requests
 
 Avoid fetching rarely changing resources from the internet if they could easily
 be bundled with your application.
 
-### Why?
+#### Why?
 
 Many users of Electron start with an entirely web-based app that they're
 turning into a desktop application. As web developers, we are used to loading
@@ -360,7 +368,7 @@ will take care of the rest.
 When building an Electron app, your users are better served if you download
 the fonts and include them in your app's bundle.
 
-### How?
+#### How?
 
 In an ideal world, your application wouldn't need the network to operate at
 all. To get there, you must understand what resources your app is downloading
@@ -387,21 +395,21 @@ without shipping an application update is a powerful strategy. For advanced
 control over how resources are being loaded, consider investing in
 [Service Workers][service-workers].
 
-## 7) Bundle your code
+### 7. Bundle your code
 
 As already pointed out in
 "[Loading and running code too soon](#2-loading-and-running-code-too-soon)",
 calling `require()` is an expensive operation. If you are able to do so,
 bundle your application's code into a single file.
 
-### Why?
+#### Why?
 
 Modern JavaScript development usually involves many files and modules. While
 that's perfectly fine for developing with Electron, we heavily recommend that
 you bundle all your code into one single file to ensure that the overhead
 included in calling `require()` is only paid once when your application loads.
 
-### How?
+#### How?
 
 There are numerous JavaScript bundlers out there and we know better than to
 anger the community by recommending one tool over another. We do however

+ 158 - 139
docs/tutorial/security.md

@@ -1,6 +1,24 @@
-# Security, Native Capabilities, and Your Responsibility
+---
+title: Security
+description: A set of guidelines for building secure Electron apps
+slug: security
+hide_title: true
+toc_max_heading_level: 3
+---
+# Security
+
+:::info Reporting security issues
+For information on how to properly disclose an Electron vulnerability,
+see [SECURITY.md](https://github.com/electron/electron/tree/main/SECURITY.md).
+
+For upstream Chromium vulnerabilities: Electron keeps up to date with alternating
+Chromium releases. For more information, see the
+[Electron Release Timelines](../tutorial/electron-timelines.md) document.
+:::
+
+## Preface
 
-As web developers, we usually enjoy the strong security net of the browser -
+As web developers, we usually enjoy the strong security net of the browser 
 the risks associated with the code we write are relatively small. Our websites
 are granted limited powers in a sandbox, and we trust that our users enjoy a
 browser built by a large team of engineers that is able to quickly respond to
@@ -17,20 +35,12 @@ With that in mind, be aware that displaying arbitrary content from untrusted
 sources poses a severe security risk that Electron is not intended to handle.
 In fact, the most popular Electron apps (Atom, Slack, Visual Studio Code, etc)
 display primarily local content (or trusted, secure remote content without Node
-integration)  if your application executes code from an online source, it is
+integration)  if your application executes code from an online source, it is
 your responsibility to ensure that the code is not malicious.
 
-## Reporting Security Issues
-
-For information on how to properly disclose an Electron vulnerability,
-see [SECURITY.md](https://github.com/electron/electron/tree/main/SECURITY.md)
-
-## Chromium Security Issues and Upgrades
+## General guidelines
 
-Electron keeps up to date with alternating Chromium releases. For more information,
-see the [Electron Release Cadence blog post](https://electronjs.org/blog/12-week-cadence).
-
-## Security Is Everyone's Responsibility
+### Security is everyone's responsibility
 
 It is important to remember that the security of your Electron application is
 the result of the overall security of the framework foundation
@@ -56,7 +66,7 @@ is your own code. Common web vulnerabilities, such as Cross-Site Scripting (XSS)
 have a higher security impact on Electron applications hence it is highly recommended
 to adopt secure software development best practices and perform security testing.
 
-## Isolation For Untrusted Content
+### Isolation for untrusted content
 
 A security issue exists whenever you receive code from an untrusted source (e.g.
 a remote server) and execute it locally. As an example, consider a remote
@@ -65,72 +75,74 @@ an attacker somehow manages to change said content (either by attacking the
 source directly, or by sitting between your app and the actual destination), they
 will be able to execute native code on the user's machine.
 
-> :warning: Under no circumstances should you load and execute remote code with
+:::warning
+Under no circumstances should you load and execute remote code with
 Node.js integration enabled. Instead, use only local files (packaged together
 with your application) to execute Node.js code. To display remote content, use
 the [`<webview>`][webview-tag] tag or [`BrowserView`][browser-view], make sure
 to disable the `nodeIntegration` and enable `contextIsolation`.
+:::
 
-## Electron Security Warnings
-
-From Electron 2.0 on, developers will see warnings and recommendations printed
-to the developer console. They only show up when the binary's name is Electron,
-indicating that a developer is currently looking at the console.
+:::info Electron security warnings
+Security warnings and recommendations are printed to the developer console.
+They only show up when the binary's name is Electron, indicating that a developer
+is currently looking at the console.
 
 You can force-enable or force-disable these warnings by setting
 `ELECTRON_ENABLE_SECURITY_WARNINGS` or `ELECTRON_DISABLE_SECURITY_WARNINGS` on
 either `process.env` or the `window` object.
+:::
 
-## Checklist: Security Recommendations
+## Checklist: Security recommendations
 
 You should at least follow these steps to improve the security of your application:
 
 1. [Only load secure content](#1-only-load-secure-content)
 2. [Disable the Node.js integration in all renderers that display remote content](#2-do-not-enable-nodejs-integration-for-remote-content)
 3. [Enable context isolation in all renderers that display remote content](#3-enable-context-isolation-for-remote-content)
-4. [Enable sandboxing](#4-enable-sandboxing)
+4. [Enable process sandboxing](#4-enable-process-sandboxing)
 5. [Use `ses.setPermissionRequestHandler()` in all sessions that load remote content](#5-handle-session-permission-requests-from-remote-content)
 6. [Do not disable `webSecurity`](#6-do-not-disable-websecurity)
 7. [Define a `Content-Security-Policy`](#7-define-a-content-security-policy) and use restrictive rules (i.e. `script-src 'self'`)
-8. [Do not set `allowRunningInsecureContent` to `true`](#8-do-not-set-allowrunninginsecurecontent-to-true)
+8. [Do not enable `allowRunningInsecureContent`](#8-do-not-enable-allowrunninginsecurecontent)
 9. [Do not enable experimental features](#9-do-not-enable-experimental-features)
 10. [Do not use `enableBlinkFeatures`](#10-do-not-use-enableblinkfeatures)
-11. [`<webview>`: Do not use `allowpopups`](#11-do-not-use-allowpopups)
+11. [`<webview>`: Do not use `allowpopups`](#11-do-not-use-allowpopups-for-webviews)
 12. [`<webview>`: Verify options and params](#12-verify-webview-options-before-creation)
 13. [Disable or limit navigation](#13-disable-or-limit-navigation)
 14. [Disable or limit creation of new windows](#14-disable-or-limit-creation-of-new-windows)
-15. [Do not use `openExternal` with untrusted content](#15-do-not-use-openexternal-with-untrusted-content)
+15. [Do not use `shell.openExternal` with untrusted content](#15-do-not-use-shellopenexternal-with-untrusted-content)
 16. [Use a current version of Electron](#16-use-a-current-version-of-electron)
 
 To automate the detection of misconfigurations and insecure patterns, it is
 possible to use
-[electronegativity](https://github.com/doyensec/electronegativity). For
+[Electronegativity](https://github.com/doyensec/electronegativity). For
 additional details on potential weaknesses and implementation bugs when
 developing applications using Electron, please refer to this [guide for
-developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf)
+developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf).
 
-## 1) Only Load Secure Content
+### 1. Only load secure content
 
 Any resources not included with your application should be loaded using a
 secure protocol like `HTTPS`. In other words, do not use insecure protocols
 like `HTTP`. Similarly, we recommend the use of `WSS` over `WS`, `FTPS` over
 `FTP`, and so on.
 
-### Why?
+#### Why?
 
 `HTTPS` has three main benefits:
 
-1) It authenticates the remote server, ensuring your app connects to the correct
+1. It authenticates the remote server, ensuring your app connects to the correct
    host instead of an impersonator.
-2) It ensures data integrity, asserting that the data was not modified while in
+1. It ensures data integrity, asserting that the data was not modified while in
    transit between your application and the host.
-3) It encrypts the traffic between your user and the destination host, making it
+1. It encrypts the traffic between your user and the destination host, making it
    more difficult to eavesdrop on the information sent between your app and
    the host.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 // Bad
 browserWindow.loadURL('http://example.com')
 
@@ -138,7 +150,7 @@ browserWindow.loadURL('http://example.com')
 browserWindow.loadURL('https://example.com')
 ```
 
-```html
+```html title='index.html (Renderer Process)'
 <!-- Bad -->
 <script crossorigin src="http://example.com/react.js"></script>
 <link rel="stylesheet" href="http://example.com/style.css">
@@ -148,9 +160,11 @@ browserWindow.loadURL('https://example.com')
 <link rel="stylesheet" href="https://example.com/style.css">
 ```
 
-## 2) Do not enable Node.js Integration for Remote Content
+### 2. Do not enable Node.js integration for remote content
 
-_This recommendation is the default behavior in Electron since 5.0.0._
+:::info
+This recommendation is the default behavior in Electron since 5.0.0.
+:::
 
 It is paramount that you do not enable Node.js integration in any renderer
 ([`BrowserWindow`][browser-window], [`BrowserView`][browser-view], or
@@ -163,7 +177,7 @@ After this, you can grant additional permissions for specific hosts. For example
 if you are opening a BrowserWindow pointed at `https://example.com/`, you can
 give that website exactly the abilities it needs, but no more.
 
-### Why?
+#### Why?
 
 A cross-site-scripting (XSS) attack is more dangerous if an attacker can jump
 out of the renderer process and execute code on the user's computer.
@@ -172,12 +186,13 @@ power is usually limited to messing with the website that they are executed on.
 Disabling Node.js integration helps prevent an XSS from being escalated into a
 so-called "Remote Code Execution" (RCE) attack.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 // Bad
 const mainWindow = new BrowserWindow({
   webPreferences: {
+    contextIsolation: false,
     nodeIntegration: true,
     nodeIntegrationInWorker: true
   }
@@ -186,7 +201,7 @@ const mainWindow = new BrowserWindow({
 mainWindow.loadURL('https://example.com')
 ```
 
-```js
+```js title='main.js (Main Process)'
 // Good
 const mainWindow = new BrowserWindow({
   webPreferences: {
@@ -197,7 +212,7 @@ const mainWindow = new BrowserWindow({
 mainWindow.loadURL('https://example.com')
 ```
 
-```html
+```html title='index.html (Renderer Process)'
 <!-- Bad -->
 <webview nodeIntegration src="page.html"></webview>
 
@@ -208,21 +223,13 @@ mainWindow.loadURL('https://example.com')
 When disabling Node.js integration, you can still expose APIs to your website that
 do consume Node.js modules or features. Preload scripts continue to have access
 to `require` and other Node.js features, allowing developers to expose a custom
-API to remotely loaded content.
-
-In the following example preload script, the later loaded website will have
-access to a `window.readConfig()` method, but no Node.js features.
+API to remotely loaded content via the [contextBridge API](../api/context-bridge.md).
 
-```js
-const { readFileSync } = require('fs')
-
-window.readConfig = () => {
-  const data = readFileSync('./config.json')
-  return data
-}
-```
+### 3. Enable Context Isolation for remote content
 
-## 3) Enable Context Isolation for Remote Content
+:::info
+This recommendation is the default behavior in Electron since 12.0.0.
+:::
 
 Context isolation is an Electron feature that allows developers to run code
 in preload scripts and in Electron APIs in a dedicated JavaScript context. In
@@ -235,48 +242,42 @@ to enable this behavior.
 Even when `nodeIntegration: false` is used, to truly enforce strong isolation
 and prevent the use of Node primitives `contextIsolation` **must** also be used.
 
-### Why & How?
-
+:::info
 For more information on what `contextIsolation` is and how to enable it please
 see our dedicated [Context Isolation](context-isolation.md) document.
+:::info
 
-## 4) Enable Sandboxing
+### 4. Enable process sandboxing
 
-[Sandboxing](sandbox.md) is a Chromium feature that uses the operating system to
+[Sandboxing](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md)
+is a Chromium feature that uses the operating system to
 significantly limit what renderer processes have access to. You should enable
 the sandbox in all renderers. Loading, reading or processing any untrusted
 content in an unsandboxed process, including the main process, is not advised.
 
-### How?
-
-When creating a window, pass the `sandbox: true` option in `webPreferences`:
-
-```js
-const win = new BrowserWindow({
-  webPreferences: {
-    sandbox: true
-  }
-})
-```
+:::info
+For more information on what `contextIsolation` is and how to enable it please
+see our dedicated [Process Sandboxing](sandbox.md) document.
+:::info
 
-## 5) Handle Session Permission Requests From Remote Content
+### 5. Handle session permission requests from remote content
 
-You may have seen permission requests while using Chrome: They pop up whenever
+You may have seen permission requests while using Chrome: they pop up whenever
 the website attempts to use a feature that the user has to manually approve (
 like notifications).
 
 The API is based on the [Chromium permissions API](https://developer.chrome.com/extensions/permissions)
 and implements the same types of permissions.
 
-### Why?
+#### Why?
 
 By default, Electron will automatically approve all permission requests unless
 the developer has manually configured a custom handler. While a solid default,
 security-conscious developers might want to assume the very opposite.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 const { session } = require('electron')
 
 session
@@ -297,9 +298,11 @@ session
   })
 ```
 
-## 6) Do Not Disable WebSecurity
+### 6. Do not disable `webSecurity`
 
-_Recommendation is Electron's default_
+:::info
+This recommendation is Electron's default.
+:::
 
 You may have already guessed that disabling the `webSecurity` property on a
 renderer process ([`BrowserWindow`][browser-window],
@@ -308,15 +311,15 @@ security features.
 
 Do not disable `webSecurity` in production applications.
 
-### Why?
+#### Why?
 
 Disabling `webSecurity` will disable the same-origin policy and set
 `allowRunningInsecureContent` property to `true`. In other words, it allows
 the execution of insecure code from different domains.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 // Bad
 const mainWindow = new BrowserWindow({
   webPreferences: {
@@ -325,12 +328,12 @@ const mainWindow = new BrowserWindow({
 })
 ```
 
-```js
+```js title='main.js (Main Process)'
 // Good
 const mainWindow = new BrowserWindow()
 ```
 
-```html
+```html title='index.html (Renderer Process)'
 <!-- Bad -->
 <webview disablewebsecurity src="page.html"></webview>
 
@@ -338,13 +341,13 @@ const mainWindow = new BrowserWindow()
 <webview src="page.html"></webview>
 ```
 
-## 7) Define a Content Security Policy
+### 7. Define a Content Security Policy
 
 A Content Security Policy (CSP) is an additional layer of protection against
 cross-site-scripting attacks and data injection attacks. We recommend that they
 be enabled by any website you load inside Electron.
 
-### Why?
+#### Why?
 
 CSP allows the server serving content to restrict and control the resources
 Electron can load for that given web page. `https://example.com` should
@@ -352,6 +355,8 @@ be allowed to load scripts from the origins you defined while scripts from
 `https://evil.attacker.com` should not be allowed to run. Defining a CSP is an
 easy way to improve your application's security.
 
+#### How?
+
 The following CSP will allow Electron to execute scripts from the current
 website and from `apis.example.com`.
 
@@ -363,14 +368,14 @@ Content-Security-Policy: '*'
 Content-Security-Policy: script-src 'self' https://apis.example.com
 ```
 
-### CSP HTTP Header
+#### CSP HTTP headers
 
 Electron respects the [`Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
 which can be set using Electron's
 [`webRequest.onHeadersReceived`](../api/web-request.md#webrequestonheadersreceivedfilter-listener)
 handler:
 
-```javascript
+```javascript title='main.js (Main Process)'
 const { session } = require('electron')
 
 session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
@@ -383,20 +388,22 @@ session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
 })
 ```
 
-### CSP Meta Tag
+#### CSP meta tag
 
-CSP's preferred delivery mechanism is an HTTP header, however it is not possible
+CSP's preferred delivery mechanism is an HTTP header. However, it is not possible
 to use this method when loading a resource using the `file://` protocol. It can
-be useful in some cases, such as using the `file://` protocol, to set a policy
-on a page directly in the markup using a `<meta>` tag:
+be useful in some cases to set a policy on a page directly in the markup using a
+`<meta>` tag:
 
-```html
+```html title='index.html (Renderer Process)'
 <meta http-equiv="Content-Security-Policy" content="default-src 'none'">
 ```
 
-## 8) Do Not Set `allowRunningInsecureContent` to `true`
+### 8. Do not enable `allowRunningInsecureContent`
 
-_Recommendation is Electron's default_
+:::info
+This recommendation is Electron's default.
+:::
 
 By default, Electron will not allow websites loaded over `HTTPS` to load and
 execute scripts, CSS, or plugins from insecure sources (`HTTP`). Setting the
@@ -405,15 +412,15 @@ property `allowRunningInsecureContent` to `true` disables that protection.
 Loading the initial HTML of a website over `HTTPS` and attempting to load
 subsequent resources via `HTTP` is also known as "mixed content".
 
-### Why?
+#### Why?
 
 Loading content over `HTTPS` assures the authenticity and integrity
 of the loaded resources while encrypting the traffic itself. See the section on
 [only displaying secure content](#1-only-load-secure-content) for more details.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 // Bad
 const mainWindow = new BrowserWindow({
   webPreferences: {
@@ -422,19 +429,21 @@ const mainWindow = new BrowserWindow({
 })
 ```
 
-```js
+```js title='main.js (Main Process)'
 // Good
 const mainWindow = new BrowserWindow({})
 ```
 
-## 9) Do Not Enable Experimental Features
+### 9. Do not enable experimental features
 
-_Recommendation is Electron's default_
+:::info
+This recommendation is Electron's default.
+:::
 
 Advanced users of Electron can enable experimental Chromium features using the
 `experimentalFeatures` property.
 
-### Why?
+#### Why?
 
 Experimental features are, as the name suggests, experimental and have not been
 enabled for all Chromium users. Furthermore, their impact on Electron as a whole
@@ -443,9 +452,9 @@ has likely not been tested.
 Legitimate use cases exist, but unless you know what you are doing, you should
 not enable this property.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 // Bad
 const mainWindow = new BrowserWindow({
   webPreferences: {
@@ -454,20 +463,22 @@ const mainWindow = new BrowserWindow({
 })
 ```
 
-```js
+```js title='main.js (Main Process)'
 // Good
 const mainWindow = new BrowserWindow({})
 ```
 
-## 10) Do Not Use `enableBlinkFeatures`
+### 10. Do not use `enableBlinkFeatures`
 
-_Recommendation is Electron's default_
+:::info
+This recommendation is Electron's default.
+:::
 
 Blink is the name of the rendering engine behind Chromium. As with
 `experimentalFeatures`, the `enableBlinkFeatures` property allows developers to
 enable features that have been disabled by default.
 
-### Why?
+#### Why?
 
 Generally speaking, there are likely good reasons if a feature was not enabled
 by default. Legitimate use cases for enabling specific features exist. As a
@@ -475,9 +486,9 @@ developer, you should know exactly why you need to enable a feature, what the
 ramifications are, and how it impacts the security of your application. Under
 no circumstances should you enable features speculatively.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 // Bad
 const mainWindow = new BrowserWindow({
   webPreferences: {
@@ -486,14 +497,16 @@ const mainWindow = new BrowserWindow({
 })
 ```
 
-```js
+```js title='main.js (Main Process)'
 // Good
 const mainWindow = new BrowserWindow()
 ```
 
-## 11) Do Not Use `allowpopups`
+### 11. Do not use `allowpopups` for WebViews
 
-_Recommendation is Electron's default_
+:::info
+This recommendation is Electron's default.
+:::
 
 If you are using [`<webview>`][webview-tag], you might need the pages and scripts
 loaded in your `<webview>` tag to open new windows. The `allowpopups` attribute
@@ -501,16 +514,16 @@ enables them to create new [`BrowserWindows`][browser-window] using the
 `window.open()` method. `<webview>` tags are otherwise not allowed to create new
 windows.
 
-### Why?
+#### Why?
 
 If you do not need popups, you are better off not allowing the creation of
 new [`BrowserWindows`][browser-window] by default. This follows the principle
 of minimally required access: Don't let a website create new popups unless
 you know it needs that feature.
 
-### How?
+#### How?
 
-```html
+```html title='index.html (Renderer Process)'
 <!-- Bad -->
 <webview allowpopups src="page.html"></webview>
 
@@ -518,7 +531,7 @@ you know it needs that feature.
 <webview src="page.html"></webview>
 ```
 
-## 12) Verify WebView Options Before Creation
+### 12. Verify WebView options before creation
 
 A WebView created in a renderer process that does not have Node.js integration
 enabled will not be able to enable integration itself. However, a WebView will
@@ -528,7 +541,7 @@ It is a good idea to control the creation of new [`<webview>`][webview-tag] tags
 from the main process and to verify that their webPreferences do not disable
 security features.
 
-### Why?
+#### Why?
 
 Since `<webview>` live in the DOM, they can be created by a script running on your
 website even if Node.js integration is otherwise disabled.
@@ -538,13 +551,13 @@ a renderer process. In most cases, developers do not need to disable any of
 those features - and you should therefore not allow different configurations
 for newly created [`<webview>`][webview-tag] tags.
 
-### How?
+#### How?
 
 Before a [`<webview>`][webview-tag] tag is attached, Electron will fire the
 `will-attach-webview` event on the hosting `webContents`. Use the event to
 prevent the creation of `webViews` with possibly insecure options.
 
-```js
+```js title='main.js (Main Process)'
 app.on('web-contents-created', (event, contents) => {
   contents.on('will-attach-webview', (event, webPreferences, params) => {
     // Strip away preload scripts if unused or verify their location is legitimate
@@ -562,16 +575,16 @@ app.on('web-contents-created', (event, contents) => {
 })
 ```
 
-Again, this list merely minimizes the risk, it does not remove it. If your goal
+Again, this list merely minimizes the risk, but does not remove it. If your goal
 is to display a website, a browser will be a more secure option.
 
-## 13) Disable or limit navigation
+### 13. Disable or limit navigation
 
 If your app has no need to navigate or only needs to navigate to known pages,
 it is a good idea to limit navigation outright to that known scope, disallowing
 any other kinds of navigation.
 
-### Why?
+#### Why?
 
 Navigation is a common attack vector. If an attacker can convince your app to
 navigate away from its current page, they can possibly force your app to open
@@ -584,7 +597,7 @@ A common attack pattern is that the attacker convinces your app's users to
 interact with the app in such a way that it navigates to one of the attacker's
 pages. This is usually done via links, plugins, or other user-generated content.
 
-### How?
+#### How?
 
 If your app has no need for navigation, you can call `event.preventDefault()`
 in a [`will-navigate`][will-navigate] handler. If you know which pages your app
@@ -595,7 +608,7 @@ We recommend that you use Node's parser for URLs. Simple string comparisons can
 sometimes be fooled - a `startsWith('https://example.com')` test would let
 `https://example.com.attacker.com` through.
 
-```js
+```js title='main.js (Main Process)'
 const URL = require('url').URL
 
 app.on('web-contents-created', (event, contents) => {
@@ -609,12 +622,12 @@ app.on('web-contents-created', (event, contents) => {
 })
 ```
 
-## 14) Disable or limit creation of new windows
+### 14. Disable or limit creation of new windows
 
 If you have a known set of windows, it's a good idea to limit the creation of
 additional windows in your app.
 
-### Why?
+#### Why?
 
 Much like navigation, the creation of new `webContents` is a common attack
 vector. Attackers attempt to convince your app to create new windows, frames,
@@ -627,7 +640,7 @@ security at no cost. This is commonly the case for apps that open one
 `BrowserWindow` and do not need to open an arbitrary number of additional
 windows at runtime.
 
-### How?
+#### How?
 
 [`webContents`][web-contents] will delegate to its [window open
 handler][window-open-handler] before creating new windows. The handler will
@@ -635,7 +648,7 @@ receive, amongst other parameters, the `url` the window was requested to open
 and the options used to create it. We recommend that you register a handler to
 monitor the creation of windows, and deny any unexpected window creation.
 
-```js
+```js title='main.js (Main Process)'
 const { shell } = require('electron')
 
 app.on('web-contents-created', (event, contents) => {
@@ -656,40 +669,40 @@ app.on('web-contents-created', (event, contents) => {
 })
 ```
 
-## 15) Do not use `openExternal` with untrusted content
+### 15. Do not use `shell.openExternal` with untrusted content
 
-Shell's [`openExternal`][open-external] allows opening a given protocol URI with
-the desktop's native utilities. On macOS, for instance, this function is similar
-to the `open` terminal command utility and will open the specific application
-based on the URI and filetype association.
+The shell module's [`openExternal`][open-external] API allows opening a given
+protocol URI with the desktop's native utilities. On macOS, for instance, this
+function is similar to the `open` terminal command utility and will open the
+specific application based on the URI and filetype association.
 
-### Why?
+#### Why?
 
 Improper use of [`openExternal`][open-external] can be leveraged to compromise
 the user's host. When openExternal is used with untrusted content, it can be
 leveraged to execute arbitrary commands.
 
-### How?
+#### How?
 
-```js
+```js title='main.js (Main Process)'
 //  Bad
 const { shell } = require('electron')
 shell.openExternal(USER_CONTROLLED_DATA_HERE)
 ```
 
-```js
+```js title='main.js (Main Process)'
 //  Good
 const { shell } = require('electron')
 shell.openExternal('https://example.com/index.html')
 ```
 
-## 16) Use a current version of Electron
+### 16. Use a current version of Electron
 
 You should strive for always using the latest available version of Electron.
 Whenever a new major version is released, you should attempt to update your
 app as quickly as possible.
 
-### Why?
+#### Why?
 
 An application built with an older version of Electron, Chromium, and Node.js
 is an easier target than an application that is using more recent versions of
@@ -705,6 +718,13 @@ to fix issues before publishing them. Your application will be more secure if
 it is running a recent version of Electron (and thus, Chromium and Node.js) for
 which potential security issues are not as widely known.
 
+#### How?
+
+Migrate your app one major version at a time, while referring to Electron's
+[Breaking Changes][breaking-changes] document to see if any code needs to
+be updated.
+
+[breaking-changes]: ../breaking-changes.md
 [browser-window]: ../api/browser-window.md
 [browser-view]: ../api/browser-view.md
 [webview-tag]: ../api/webview-tag.md
@@ -712,5 +732,4 @@ which potential security issues are not as widely known.
 [window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
 [will-navigate]: ../api/web-contents.md#event-will-navigate
 [open-external]: ../api/shell.md#shellopenexternalurl-options
-[sandbox]: ../tutorial/sandbox.md
 [responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure