Skip to content

Commit

Permalink
Merge branch 'main' into fb/bump-wdio
Browse files Browse the repository at this point in the history
  • Loading branch information
FrederikBolding authored Aug 30, 2023
2 parents 2ff7c16 + 1d14bcb commit 6d5d049
Show file tree
Hide file tree
Showing 26 changed files with 1,125 additions and 45 deletions.
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
Expand All @@ -11,5 +11,5 @@ updates:
allow:
- dependency-name: '@metamask/*'
target-branch: 'main'
versioning-strategy: 'increase-if-necessary'
versioning-strategy: 'increase'
open-pull-requests-limit: 10
216 changes: 216 additions & 0 deletions .github/workflows/update-pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
name: Update pull request

on:
issue_comment:
types:
- created

jobs:
is-fork-pull-request:
name: Determine whether this issue comment was on a pull request from a fork
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '@metamaskbot update-pr') }}
runs-on: ubuntu-latest
outputs:
IS_FORK: ${{ steps.is-fork.outputs.IS_FORK }}
steps:
- uses: actions/checkout@v3
- name: Determine whether this PR is from a fork
id: is-fork
run: echo "IS_FORK=$(gh pr view --json isCrossRepository --jq '.isCrossRepository' "${PR_NUMBER}" )" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}

react-to-comment:
name: React to the comment
runs-on: ubuntu-latest
needs: is-fork-pull-request
# Early exit if this is a fork, since later steps are skipped for forks.
if: ${{ needs.is-fork-pull-request.outputs.IS_FORK == 'false' }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: React to the comment
run: |
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
-f content='+1'
env:
COMMENT_ID: ${{ github.event.comment.id }}
GITHUB_TOKEN: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
REPO: ${{ github.repository }}

prepare:
name: Prepare dependencies
runs-on: ubuntu-latest
needs: is-fork-pull-request
# Early exit if this is a fork, since later steps are skipped for forks.
if: ${{ needs.is-fork-pull-request.outputs.IS_FORK == 'false' }}
outputs:
COMMIT_SHA: ${{ steps.commit-sha.outputs.COMMIT_SHA }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install Yarn dependencies
run: yarn --immutable
- name: Get commit SHA
id: commit-sha
run: echo "COMMIT_SHA=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"

dedupe-yarn-lock:
name: Deduplicate yarn.lock
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies from cache
run: yarn --immutable --immutable-cache
- name: Deduplicate yarn.lock
run: yarn dedupe
- name: Cache yarn.lock
uses: actions/cache/save@v3
with:
path: yarn.lock
key: cache-yarn-lock-${{ needs.prepare.outputs.COMMIT_SHA }}

regenerate-lavamoat-policies:
name: Regenerate LavaMoat policies
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies from cache
run: yarn --immutable --immutable-cache
- name: Regenerate LavaMoat policies
run: yarn build:lavamoat:policy
- name: Cache LavaMoat policies
uses: actions/cache/save@v3
with:
path: packages/snaps-execution-environments/lavamoat
key: cache-lavamoat-${{ needs.prepare.outputs.COMMIT_SHA }}

update-examples:
name: Update examples
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies from cache
run: yarn --immutable --immutable-cache
- name: Build dependencies
run: |
yarn build:source
yarn build:types
- name: Update examples
run: yarn build:examples
- name: Cache examples
uses: actions/cache/save@v3
with:
path: packages/examples/packages
key: cache-examples-${{ needs.prepare.outputs.COMMIT_SHA }}

commit-result:
name: Commit result
runs-on: ubuntu-latest
needs:
- prepare
- dedupe-yarn-lock
- regenerate-lavamoat-policies
- update-examples
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
# Use PAT to ensure that the commit later can trigger status check
# workflows.
token: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
- name: Checkout pull request
run: gh pr checkout "${PR_NUMBER}"
env:
GITHUB_TOKEN: ${{ secrets.PULL_REQUEST_UPDATE_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
- name: Configure Git
run: |
git config --global user.name 'MetaMask Bot'
git config --global user.email '[email protected]'
- name: Get commit SHA
id: commit-sha
run: echo "COMMIT_SHA=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Restore yarn.lock
uses: actions/cache/restore@v3
with:
path: yarn.lock
key: cache-yarn-lock-${{ needs.prepare.outputs.COMMIT_SHA }}
fail-on-cache-miss: true
- name: Commit yarn.lock
run: |
git add yarn.lock
git commit -m "Deduplicate yarn.lock" || true
- name: Restore LavaMoat policies
uses: actions/cache/restore@v3
with:
path: packages/snaps-execution-environments/lavamoat
key: cache-lavamoat-${{ needs.prepare.outputs.COMMIT_SHA }}
fail-on-cache-miss: true
- name: Commit LavaMoat policies
run: |
git add packages/snaps-execution-environments/lavamoat
git commit -m "Update LavaMoat policies" || true
- name: Restore examples
uses: actions/cache/restore@v3
with:
path: packages/examples/packages
key: cache-examples-${{ needs.prepare.outputs.COMMIT_SHA }}
fail-on-cache-miss: true
- name: Commit examples
run: |
git add packages/examples/packages
git commit -m "Update example snaps" || true
- name: Push changes
run: git push
76 changes: 76 additions & 0 deletions docs/internals/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Architecture

Below is a sequence diagram of the architecture of the Metamask Snaps system. It
shows the flow of a request from a dapp to a snap, and back.

```mermaid
sequenceDiagram
participant d as Dapp
participant mm as MetaMask Background
participant sc as Snap Controller
participant ex as Execution Service
participant i as Iframe
participant exe as Execution Environment
participant ses as SES Compartment
participant s as A-Snap
Note over d,mm: Invoke A-Snap with RPC request
Note over d,mm: Permission for the dapp to invoke
d->>mm: invokeSnap(snapId, request)
Note over mm: Provider engine:<br>Permission to call handler check<br>Call matching handler
mm->>sc: handleRequest
Note over sc: > Snap is not running
sc->>sc: startSnap(snapId, code)
Note over sc: Permissions translated to actual endowments
sc->>ex: executeSnap<br>(snapId, code, endowments)
ex->>i: Create iframe
i->exe: Load environment
i-->>ex: _
exe->>exe: Initialization
ex->>ex: Set up streams and job
Note over ex,exe: postMessage is set up<br>⚠️ e.source===_targetWindow
Note over ex, exe: can use command(method, RPC) now
ex->>exe: await command("ping", …)
exe-->>ex: OK
ex->>mm: setupSnapProvider(snapId, stream)
Note over mm,ex: Communication is set up on the stream, <br>⚠️ checked for subjectType and snapId as origin
mm-->>ex: _
ex->>exe: await command("executeSnap", A-Snap code)
exe->>exe: Create endowments, module etc.
exe->>ses: Create Compartment
exe->>exe: this.executeInSnapContext
exe->>ses: evaluate(A-Snap code)
ses->>s: Execute
s-->>ses: Export RPC handler
ses-->>exe: _
exe->>exe: ⚠️ Validate and register exports
exe-->>ex: OK
ex->>ex: createSnapHooks
Note over ex: Wires up snapRpc to the exported handler
ex-->>sc: OK
Note over sc: Remember: We received a request.<br>It can now be sent to snapRpc
sc->>ex: handleRpcRequest
Note over sc,ex: Request from dapp is wrapped in<br>another RPC for snap command
sc->>sc: Set up timer
ex->>exe: Handle RPC
exe->s: Handle RPC
s->>s: Do stuff
Note over exe,s: Snap sends an RPC request through<br> the endowed API
s->>exe: request
exe->>exe: Check if method is wallet_* or snap_*
Note over exe: Asserts defensively, doesn't use<br>method.startsWith
exe->>mm: RPC request
Note over mm: Provider engine:<br>Permission to call handler check<br>Call matching handler
mm-->>exe: RPC response
exe-->>s: RPC response
s->>s: Do stuff
s-->>exe: Snap response
exe->>exe: Check if returned value is valid JSON
exe-->>ex: Snap response
ex->>ex: Throw if response is an error
ex-->>sc: Snap response
sc->>sc: Cancel timer
sc-->>d: Snap response
```
52 changes: 52 additions & 0 deletions docs/internals/execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Secure snap execution in SES

To avoid snaps from getting access to the client, dangerous APIs, and such,
we run snaps in a different "realm," which is a separate JavaScript execution
environment. Inside this realm we use [SES]'s lockdown feature to harden the
realm, and prevent the snap from breaking out of the sandbox. For certain APIs,
such as the events API, we must do an extra lockdown, to make sure that the
snap can't break out of the sandbox.

Inside this realm, we create an [SES] compartment, which is a sandboxed
JavaScript environment that limits access to global APIs, letting us control
what the snap can do.

## Endowments

The endowments are the global APIs that are available to the snap, such as the
`console` API, the `fetch` function, and so on. To avoid the snap
breaking out of the sandbox, we only give it access to a limited set of APIs,
and we make sure that the APIs we give it are safe to use. For example, snaps
don't have access to the `window` or `document` object, so they can't access the
DOM.

Each endowment we provide to the snap is hardened in a couple of ways:

- We freeze and seal the object, so that the snap can't modify it or add new
properties to it.
- We only provide a limited subset of APIs.
- Certain APIs are wrapped to ensure that they can be torn down properly
when the snap is being stopped as well as to prevent snaps interfering with
each other.

Some endowments are provided to the snap by default. A list of these can be
found [here](../../packages/snaps-utils/src/default-endowments.ts). Other
endowments must be requested by the snap, using the permissions system.

Endowments granted via the permission system map to one or more global APIs,
e.g., [endowment:network-access] grants access to `fetch`, `Request`, `Headers`,
and `Response`. These endowments may also be further hardened before being
passed to the snap, see for example [network hardening], which hardens the
`fetch` global before granting it to the snap.

<!--
## Hardening of the `snap` and `ethereum` globals
...
-->

[endowment:network-access]: ../../packages/snaps-controllers/src/snaps/endowments/network-access.ts
[network hardening]: ../../packages/snaps-execution-environments/src/common/endowments/network.ts
[ses]: https://github.com/endojs/endo/tree/master/packages/ses
Loading

0 comments on commit 6d5d049

Please sign in to comment.