From 73b7a7f8e1ffefc654151e8ea65011b1a21cb0db Mon Sep 17 00:00:00 2001 From: Zbyszek Tenerowicz Date: Tue, 17 Sep 2024 15:38:47 +0200 Subject: [PATCH] docs: policy review document v1 (#8) * initial draft of policy review document * Apply suggestions from code review Co-authored-by: LeoTM <1881059+leotm@users.noreply.github.com> * update formatting - markdownlint: specify json in fenced code block - prettier: format doc, except powerful APIs table * chore: Apply suggestions from code review Co-authored-by: Christopher Hiller * chore: Apply suggestions from code review Co-authored-by: Christopher Hiller * chore: more fixes to eventually squash * chore: fix formatting --------- Co-authored-by: LeoTM <1881059+leotm@users.noreply.github.com> Co-authored-by: Christopher Hiller --- src/content/docs/guides/policy-diff.md | 85 ++++++++++++++++++++++++++ src/content/docs/reference/glossary.md | 4 ++ 2 files changed, 89 insertions(+) create mode 100644 src/content/docs/guides/policy-diff.md diff --git a/src/content/docs/guides/policy-diff.md b/src/content/docs/guides/policy-diff.md new file mode 100644 index 0000000..3b9feb6 --- /dev/null +++ b/src/content/docs/guides/policy-diff.md @@ -0,0 +1,85 @@ +--- +title: Reviewing Policy +description: How to review LavaMoat Policy and its diffs +--- + + + +This guide will show you how to review changes to your LavaMoat Policy File. + +## Why review Policy? + +The Policy File generated by LavaMoat is based on a scan of your codebase, identifying all the powers it uses. The initial policy, resulting from the first time you run policy generation, doesn't provide security on its own. +**Instead, it's your review of the initial policy and the subsequent updates (with new or updated dependencies) that makes your application secure.** + +Reviewing diffs as dependencies change lets you spot suspicious packages or limit the powers you wish to allow newly added packages to use. + +The purpose of the initial review is twofold: + +1. It helps you build confidence that the current state of your app is not compromised +2. You may deny powers to dependencies if you determine they are excessive - not needed for the subset of functionality your app uses. + +Reviewing your initial policy may seem like a lot of effort - but think of it as an _investment_ in your application's security posture. + +## How to review your policy? + +The LavaMoat Policy lists all powers that a package can use; these are the `globals` and `builtin` fields. +It also lists which other packages are allowed for the current package to import. You can follow those relations to see whether a package with access to very [powerful APIs](#powerful-apis) is used by any suspicious packages as a dependency. See [Principle of Least Authority][PoLA] + +### What to look for when reviewing a Policy diff? + +The goal of reviewing the diff is to spot a malicious package being added. + +#### TL;DR + +- Check `globals` and `builtins` for new powers and investigate if you're surprised the package would need them +- Check if new relationships in `packages` are pointing to packages with very [powerful APIs](#powerful-apis) (e.g. spawning child processes in Node.js) +- Be aware that the identifier may change to `pkgC>actual-name` from `pkgB>pkgA>actual-name` BUT! If the package now also has totally different powers, it's likely a different package of the same name. Investigate! `npm ls actual-name` should help +- When a new package is added, consider limiting its powers to what you actually use + +#### Best Practices for Finding Suspicious Changes + +First of all - you need to check if any of the packages get access to new [powerful APIs](#powerful-apis) unexpectedly. + +If a package that was supposed to only be doing basic string operations is suddenly also using `fetch` and `process.env` in your build system, you should give it a closer look or add + +```json +"fetch": false, +"process": false +``` + +to the `globals` field for that package in `policy-override.json`. + +When a new dependency shows up in `packages` field of _packageA_: look up what it's pointing to and if the dependency has access to very [powerful APIs](#powerful-apis); doublecheck whether it makes sense to you that _packageA_ would need to use it. + +When dependency tree changes, it's possible that the dependency nesting might change - so the shortest identifier for one of the resources may now be `pkgC>actual-name`, _not_ `pkgB>pkgA>actual-name`. +But there are other more nefarious reasons why that could happen. +If the package now also has totally different powers or dependencies listed it's likely a different package of the same name. There can be more than one `actual-name` named package in this case. It could have been introduced as a different version or a totally different package installed from git or as a bundled dependency. + +Whn a new package is added, consider limiting its powers to what you actually use. + +### What to look for in initial review? + +The goal of reviewing the initial policy is to spot where packages are given powers that allow them escaping LavaMoat protections or abusing the application. + +The minimal viable review is to look at the `globals` and `builtins` fields of the policy file to see if any of the packages have access to unexpected [powerful APIs](#powerful-apis). + +A more advanced review would be to apply [Principle of Least Authority][PoLA] and add entries to policy-override.json to limit the powers of packages to what they actually need to serve your usecase. + +## Powerful APIs + +Examples of powerful APIs - not an exhaustive list: + +| global | builtin | description | +| ----------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| | `child_process` and any form of `exec` or `spawn` | Allows running arbitrary commands on the host machine and is not covered | +| | `fs` | Allows reading and writing files on the host machine | +| `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource` | `http`, `https`, `net` | Allows making network requests | +| `document` | | contains a lot of powerful APIs that can be used to manipulate the DOM, including creating iframes with unprotected globals | +| `open` | | `window.open` allows opening new windows/tabs and accessing clean globals there | +| `navigator` | | contains a lot of powerful APIs that can be used to fingerprint the user or control the browser | +| `chrome` or `browser` | | extension APIs - should only be accessed by a package that is a helper library for cross-browser extensions | +| `process` | | Allows reading and writing environment variables and other process-related operations | +| | `vm` | Allows running arbitrary code in a new context | + +[PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege diff --git a/src/content/docs/reference/glossary.md b/src/content/docs/reference/glossary.md index 442a99b..c766c1e 100644 --- a/src/content/docs/reference/glossary.md +++ b/src/content/docs/reference/glossary.md @@ -67,6 +67,10 @@ The `lockdown()` function introduced by [SES][], when called creates a [hardened ::: +## Powers + +There's a concept in [Hardened JavaScript][] called _powers_. Powers are the capabilities that a compartment has access to. The compartment can only access the powers that it has been granted. For the specific use-case that LavaMoat Policy provides, Powers are effectively the globals and built-in (in case of Node.js programs) modules that are available to the compartment. + ## Object Capability Programming > a.k.a. _OCAP_ or _object-capability model_