-
Notifications
You must be signed in to change notification settings - Fork 563
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into fb/bump-wdio
- Loading branch information
Showing
26 changed files
with
1,125 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.