Skip to content

Latest commit

 

History

History
178 lines (136 loc) · 5.78 KB

policy.md

File metadata and controls

178 lines (136 loc) · 5.78 KB

Policy file explained

Say we generate a policy file for a project with the following dependency tree:

app.js
└─┬ some-package
  └─┬ entropoetry
    └── bn.js

The policy file is written to ./lavamoat/node/policy.json:

{
  "resources": {
    "some-package": {
      "globals": {
        "Buffer.from": true
      },
      "packages": {
        "some-package>entropoetry": true
      }
    },
    "some-package>entropoetry": {
      "builtin": {
        "assert": true,
        "buffer.Buffer": true,
        "zlib": true
      },
      "globals": {
        "console": true,
        "process.exitCode": "write",
      },
      "packages": {
        "some-package>entropoetry>bn.js": true
      }
    },
    "some-package>entropoetry>bn.js": {
      "builtin": {
        "buffer.Buffer": true
      },
      "globals": {
        "Buffer": true
      }
    }
  }
}

Fields

resources

All packages in your dependency graph accessible via require().

The keys of this object are names of packages assigned by LavaMoat. In order to identify a package, LavaMoat does not simply use the published name of the package, but generates a unique name by tracing the package's shortest route through its dependents within the dependency tree. This is done in order to prevent a malicious package which shares the same name as a package published to NPM from hijacking permissions.

packages and builtin

All packages accessible by the dependency. In this example, some-package has access to entropoetry.This means that some-package can require('entropoetry').

entropoetry can also require('assert') - a built-in module of Node.js.

globals

All platform APIs and global variables accessible by the dependency. In this example,entropoetry has access to console and it can write to process.exitCode. A value of true grants read-only access to a global; write access can be enabled by setting the value to a "write" instead.

Write access is represented by a string to make it stand out when inspecting the policy file.

Limiting access to fields of powerful objects

Sometimes a package needs a very specific functionality from a globally available reference or a builtin. Meanwhile, there's a meaningful difference between giving a script access to window.location.hash vs the entire window.location or fs.readFile vs entire fs.

You may have noticed the dot notation in keys within globals and builtins. It can be used to limit the reference the package can access to just a subset of fields on the object. That's how access to powerful references can be limited.

"Buffer.from": true means the Buffer global reference will exist in the package's scope but will only contain the from field and no other fields.
Similarly, for "buffer.Buffer": true in builtins - when the package does const buffer = require('buffer') it will get an object with just one reference to Buffer constructor.

LavaMoat can often generate precise lists of fields used by the package. You can narrow them down further if need be by creating a policy-override.json file next to the policy.json.

Using policy-override.json to expand or limit access

Adding access where LavaMoat's generated policy is too strict

Policy files are generated by statically analyzing the source of packages and finding out which powerful references (APIs available via globals or builtins) they seem to be using. It's impossible to detect it 100% correctly because there are JavaScript constructs that can't be detected without running the code.
If such missing permissions are identified, it's best to double check they're expected to be there and then add them to the policy-override file.

Example:
Allow entropoetry access to the global URL class and expand access to the entire buffer builtin on top of the generated policy above.

./lavamoat/node/policy-override.json:

{
  "resources": {
    "some-package>entropoetry": {
      "globals": {
        "URL": true,
      }
    },
    "some-package>entropoetry>bn.js": {
      "builtin": {
        "buffer": true
      }
    }
  }
}

Limiting access in the policy override

If LavaMoat's static analysis believes that a package refers to a certain resource, but your project does not actually use this resource, it's reasonable to deny access to this resource. You can achieve that through the policy override file.

Example:

  • Forbid requiring of assert by entropoetry
  • Deny access to console with the exception of console.log

./lavamoat/node/policy-override.json:

{
  "resources": {
    "some-package>entropoetry": {
      "builtin": {
        "assert": false
      },
      "globals": {
        "console": false,
        "console.log": true
      }
    }
  }
}

⚠️ Unsupported deny-list style policy

Warning
Explicitly allowing access to an entire item and then attempting to narrow it down by denying specific fields is not supported.
You might think this would work:

/*** broken example ***/
  "globals": {
    "a": true,
    "a.b": false
  }
/*** broken example ***/

a.b will remain accessible since a was declared as accessible in its entirety.

To properly deny access to a.b while allowing access to other fields of a you'd need an allow-list style policy like this:

  "globals": {
    "a.c": true,
    "a.d": true
  }

More specifically, the deepest chain of properties needs to point to an allowed reference and skipping all-but-necessary access on higher levels is possible, so the following will work and allow a.z and a.b.c but not a.b.y

  "globals": {
    "a": true,
    "a.b": false,
    "a.b.c":true
  }

See packages/core/test/globals.js for reference

You should not need this level of complexity often and LavaMoat will not generate negative entries for you to review.