Policies in NodeJS - Part 1
Tags: node, polices, security
Published: 2022-02-27 23:35:53

---
title: Policies in NodeJS - Part 1
published: true
description: Policies in nodejs
tags: nodejs,polices,security
//cover_image: https://direct_url_to_image.jpg
---

Policy is one of the less frequently talked about features in NodeJS. The purpose of this feature is to enforce a level of security to what kind of code is loadable into your NodeJS Application (Which is kind of similar to deno `--allow-<module>` but more multifaceted).

Policies are currently `experimental` and can be used with the `--experimental-policy` flag

> 
This feature was introduced recently, and may change
or be removed in future versions.  Please try it out and provide feedback. If it addresses a use-case that is important to you, tell the node core team.

Every loadable code will go through an integrity verification check by comparing the sha256 value (base64 encoded) with what was specified in relation to that resource as well as all subresources. If there is a mismatch of the sha256 value with what was specified in the policy manifest file (this file dictates how a code should or should not be loaded), the behavior of what happens next will be defined in the policy manifest file. 

The sha256 value is calculated from the content of the loadable resource.
 
For example, if we have this code

```javascript
console.log('test')
```
copy the above into an empty folder and name it `test.js`
To get the sha256 value of `test.js`, you can use the oneliner specified in the node documentation for policies

`node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < ./test.js`

```json
{
   "onerror": "log",
   "resources": {
      "./test.js": {
         "integrity": "sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
      }
   }
}
```
copy the above into the same folder as `test.js` and name it `policy.json`

`onerror` can either be `log` , `throw`, or `exit`. When the integrity check fails, `log` outputs the error and continues execution of your program.

The loadable resources in this context is `test.js`. When `onerror` is not specified the default value is `throw`, it logs out the error and does not continue executing your program.
Running the below command will output a bunch of `ERR_MANIFEST_ASSERT_INTEGRITY` as well as `test`.

`node --experimental-policy=./policy.json ./test.js`

> not specifying the *integrity* field, defaults to `null` specifying `true` as the value for *integrity* bypasses the verification check. Any loadable resource that is loaded  using either `require` or `import` is also subject to the integrity verification check. The sha256 value becomes invalid when a little change is made to any of the loadable resource


Change the `onerror` value from log to either `throw` or `exit` to see how it behaves when a wrong sha256 value is used for a resource


### Enabling/Disabling modules from been loaded

copy the below code into `test-2.js`

```javascript
const fs = require("node:fs");
const os = require("node:os");
const test2_1 = require("./test-2-1.js");
console.log(fs.statSync(__filename));
console.log(os.userInfo());
```

copy the below code into `test-2-1.js`

```
const net = require("node:net");
console.log(new net.SocketAddress());
```

Run the below oneliner to generate the sha256 value for integrity verification.

`node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < ./test-2.js`

`node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < ./test-2-1.js`

copy the below manifest into `policy-2.json`

```json
{
  "onerror": "log",
  "resources": {
    "./test-2.js": {
      "integrity": "sha256-input test-2.js base64 encoded hash here",
      "dependencies": {
        "node:fs": true,
        "node:os": true,
        "./test-2-1.js": true
      }
    },
    "./test-2-1.js": {
      "integrity": "sha256-input test-2-1.js base64 encoded hash here",
      "dependencies": {
        "node:net": true
      }
    }
  }
}
```

The `dependencies` field contains the list of dependencies (used in a resource or subresource) and the rules of how it should be loaded. Subresource are resources that are loaded by other resource, for example `test-2-1.js` is a subresource to `test-2.js`

Run 
`node --experimental-policy=./policy-2.json ./test-2.js`

The output will be something like this , depending on your computer

```javascript
SocketAddress { address: '127.0.0.1', port: 0, family: 'ipv4', flowlabel: 0 }
Stats {
  dev: 16777221,
  mode: 33188,
  nlink: 1,
  uid: 502,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 15164992,
  size: 170,
  blocks: 8,
  atimeMs: 1645483771373.328,
  mtimeMs: 1645483770300.6633,
  ctimeMs: 1645483770300.6633,
  birthtimeMs: 1645482935166.657,
  atime: 2022-02-21T22:49:31.373Z,
  mtime: 2022-02-21T22:49:30.301Z,
  ctime: 2022-02-21T22:49:30.301Z,
  birthtime: 2022-02-21T22:35:35.167Z
}
{
  uid: 502,
  gid: 20,
  username: 'victoryosikwemhe',
  homedir: '/Users/victoryosikwemhe',
  shell: '/usr/local/bin/bash'
}
```

`policy-two.json` manifest file enables every dependency required/imported in `./test-2-1.js` and `./test-2.js`, a dependency can be disabled by setting the value of the dependency to `false`



```json
{
  "onerror": "log",
  "resources": {
    "./test-2.js": {
      "integrity": "sha256-input test-2.js base64 encoded hash here",
      "dependencies": {
        "node:fs": true,
        "node:os": true,
        "./test-2-1.js": true
      }
    },
    "./test-2-1.js": {
      "integrity": "sha256-input test-2-1.js base64 encoded hash here",
      "dependencies": {
        "node:net": false
      }
    }
  }
}
```

setting `node:net` to `false` disables the node core `net` module in only `test-2-1.js` , when `test-1.js` tries loading `test-2-1.js` it will cause an error.

Run 
`node --experimental-policy=./policy-2.json ./test-2.js`

It will throw `ERR_MANIFEST_INVALID_RESOURCE_FIELD(href, 'dependencies')` on `test-2-1.js`
 

### Enforcing using `import` 
You can also enforce that a module should be loaded with `import` or `require`

Modify `test-2.js` and `test-2-1.js` respectively to look like the below (You will have to generate the sha256 value of the contents)

`test-2.js`

```javascript
const { syncBuiltinESMExports } = require("node:module");
const os = require("node:os");
const test2_1 = require("./test-2-1.js");

console.log(os.userInfo());

syncBuiltinESMExports();

import("node:fs").then( f => {
  console.log(f.statSync(__filename));
});

```

`test-2-1.js`

```javascript
const net = require("node:net");
console.log(new net.SocketAddress());
module.exports = {};
```

(Note: Generate a new sha254 value for the above resources, you can also set integrity to true to avoid doing this for every little change - even for a single space)

```json
{
  "onerror": "log",
  "resources": {
    "./test-2.js": {
      "integrity": true,
      "dependencies": {
        "node:fs": { "require": true },
        "node:os": { "import":  true },
        "node:module": true
        "./test-2-1.js": true
      }
    },
    "./test-2-1.js": {
      "integrity": true,
      "dependencies": {
        "node:net": true
      }
    }
  }
}
```

Run
`node --experimental-policy=./policy-2.json ./test-2.js`

This will throw `ERR_INVALID_URL` because `./test-2.js` should only load `node:fs` with esm `import`. Changing `require: true` to `import: true` or loading `node:fs` with cjs `require` will make this check go away.

Sadly, switching the flip to `module.createRequire` behaves differently.

### Loading a different module other than what is required/imported

Another form of dependency redirection is loading Module A when Module B was initially required/imported.

`test-3.js`

```javascript
const fs = require('node:fs');
console.log(nodeFetch);
fs.readFileSync(__filename);
```

`mocked-fs.js`

```javascript
module.exports = {
  readFileSync(location) {
    console.log({ location });
  }
}
```

`policy-3.json`

```json
{
  "onerror": "log",
  "resources": {
    "./package.json": {
      "integrity": true
    },
    "./test-3.js": {
      "integrity": true,
      "dependencies": {
        "node:fs": "./mocked-fs.js"
      }
    },
    "./mocked-fs.js": {
      "integrity": true
    }
  }
}
```

Run
`node --experimental-policy=./policy-3.json ./test-3.js`

Output
```bash
{ location: '/Users/victoryosikwemhe/pp/test-3.js' }`
```

Instead of loading the `fs` module , it redirects to `mocked-fs.js`

The policy manifest file also supports `scopes` , `import maps` and `cascading`. I will cover them in the next part, until then, you can checkout the documentation on [policies](https://nodejs.org/api/policy.html)