@bastion-ai/openclaw-plugin 0.1.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/README.md +186 -0
- package/dist/bastionBridge.d.ts +17 -0
- package/dist/bastionBridge.d.ts.map +1 -0
- package/dist/bastionBridge.js +73 -0
- package/dist/bastionBridge.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +177 -0
- package/dist/plugin.js.map +1 -0
- package/dist/responseAdapter.d.ts +10 -0
- package/dist/responseAdapter.d.ts.map +1 -0
- package/dist/responseAdapter.js +35 -0
- package/dist/responseAdapter.js.map +1 -0
- package/dist/ruleEngine.d.ts +23 -0
- package/dist/ruleEngine.d.ts.map +1 -0
- package/dist/ruleEngine.js +103 -0
- package/dist/ruleEngine.js.map +1 -0
- package/dist/secretRef.d.ts +7 -0
- package/dist/secretRef.d.ts.map +1 -0
- package/dist/secretRef.js +32 -0
- package/dist/secretRef.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/openclaw.plugin.json +92 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# @bastion-ai/openclaw-plugin
|
|
2
|
+
|
|
3
|
+
OpenClaw plugin for [Bastion](https://github.com/Matthieuhakim/Bastion).
|
|
4
|
+
|
|
5
|
+
It ships a `bastion_fetch` tool that sends outbound HTTP requests through Bastion, so Bastion can enforce policy, inject credentials, handle HITL approval, and append audit records. It can also block direct calls to protected URLs on built-in tools like `web_fetch`.
|
|
6
|
+
|
|
7
|
+
## Compatibility
|
|
8
|
+
|
|
9
|
+
- OpenClaw `2026.3.13+`
|
|
10
|
+
- Node.js `22+`
|
|
11
|
+
- A running Bastion server
|
|
12
|
+
|
|
13
|
+
This plugin targets the current released OpenClaw runtime by registering an explicit tool. It does not rely on unreleased transparent result-injection hooks.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### From npm
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
openclaw plugins install @bastion-ai/openclaw-plugin
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The installed plugin ID is `bastion-fetch`, so configure it under `plugins.entries["bastion-fetch"]`.
|
|
24
|
+
|
|
25
|
+
### Local development / pre-publish
|
|
26
|
+
|
|
27
|
+
From the Bastion repo root:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run build --workspace=packages/openclaw-plugin
|
|
31
|
+
openclaw plugins install -l ./packages/openclaw-plugin
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install a packed tarball:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm pack --workspace=packages/openclaw-plugin
|
|
38
|
+
openclaw plugins install ./packages/openclaw-plugin/bastion-ai-openclaw-plugin-0.1.0.tgz
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Bastion Setup
|
|
42
|
+
|
|
43
|
+
1. Create an agent and save the returned `agentSecret` (`bst_...`):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
curl -X POST http://localhost:3000/v1/agents \
|
|
47
|
+
-H "Authorization: Bearer $PROJECT_API_KEY" \
|
|
48
|
+
-H "Content-Type: application/json" \
|
|
49
|
+
-d '{"name": "my-openclaw-agent"}'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Store the upstream credential Bastion should inject:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
curl -X POST http://localhost:3000/v1/credentials \
|
|
56
|
+
-H "Authorization: Bearer $PROJECT_API_KEY" \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d '{"name": "Stripe API Key", "type": "API_KEY", "value": "sk_live_...", "agentId": "<agentId>"}'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
3. Create a policy that allows the action:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
curl -X POST http://localhost:3000/v1/policies \
|
|
65
|
+
-H "Authorization: Bearer $PROJECT_API_KEY" \
|
|
66
|
+
-H "Content-Type: application/json" \
|
|
67
|
+
-d '{"agentId": "<agentId>", "credentialId": "<credentialId>", "allowedActions": ["stripe.*"]}'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## OpenClaw Configuration
|
|
71
|
+
|
|
72
|
+
Add this to `openclaw.json`:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"plugins": {
|
|
77
|
+
"entries": {
|
|
78
|
+
"bastion-fetch": {
|
|
79
|
+
"enabled": true,
|
|
80
|
+
"config": {
|
|
81
|
+
"serverUrl": "http://localhost:3000",
|
|
82
|
+
"agentSecret": { "$env": "BASTION_AGENT_SECRET" },
|
|
83
|
+
"rules": [
|
|
84
|
+
{
|
|
85
|
+
"tool": "web_fetch",
|
|
86
|
+
"urlPattern": "https://api.stripe.com/**",
|
|
87
|
+
"credentialId": "cred_abc123",
|
|
88
|
+
"action": "stripe.charges"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"tool": "web_fetch",
|
|
92
|
+
"urlPattern": "https://api.github.com/**",
|
|
93
|
+
"credentialId": "cred_def456",
|
|
94
|
+
"action": "github.api",
|
|
95
|
+
"injection": { "location": "header", "key": "Authorization" }
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"timeout": 30000
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Set your agent secret:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
export BASTION_AGENT_SECRET=bst_...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## How Users Implement It
|
|
113
|
+
|
|
114
|
+
Agents should call `bastion_fetch` for protected outbound API requests.
|
|
115
|
+
|
|
116
|
+
Example tool call:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"tool": "bastion_fetch",
|
|
121
|
+
"params": {
|
|
122
|
+
"url": "https://api.stripe.com/v1/charges",
|
|
123
|
+
"method": "POST",
|
|
124
|
+
"body": {
|
|
125
|
+
"amount": 5000,
|
|
126
|
+
"currency": "usd"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The plugin matches the request URL against the configured rules, resolves the Bastion credential/action pair, calls Bastion's `/v1/proxy/execute`, and returns a structured tool result containing:
|
|
133
|
+
|
|
134
|
+
- `status`
|
|
135
|
+
- `headers`
|
|
136
|
+
- `body`
|
|
137
|
+
- `url`
|
|
138
|
+
- `_bastion` metadata (`credentialId`, `action`, `policyDecision`, `durationMs`, optional `hitlRequestId`)
|
|
139
|
+
|
|
140
|
+
If a rule includes `tool`, the plugin also blocks direct calls to that tool for matching URLs. For example, `tool: "web_fetch"` prevents the model from bypassing Bastion for those domains.
|
|
141
|
+
|
|
142
|
+
## Prompting Guidance
|
|
143
|
+
|
|
144
|
+
In your agent instructions, tell the model:
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
Use `bastion_fetch` for requests to protected APIs such as Stripe or GitHub. Do not use `web_fetch` for those domains.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
That keeps the workflow deterministic and lets the plugin enforce policy cleanly.
|
|
151
|
+
|
|
152
|
+
## `agentSecret` formats
|
|
153
|
+
|
|
154
|
+
| Format | Example |
|
|
155
|
+
|--------|---------|
|
|
156
|
+
| Plain string | `"bst_abc123..."` |
|
|
157
|
+
| Environment variable | `{ "$env": "BASTION_AGENT_SECRET" }` |
|
|
158
|
+
| File | `{ "$file": "/run/secrets/bastion_secret" }` |
|
|
159
|
+
| Command | `{ "$exec": "vault read -field=secret secret/bastion" }` |
|
|
160
|
+
|
|
161
|
+
## Rule Options
|
|
162
|
+
|
|
163
|
+
| Field | Required | Description |
|
|
164
|
+
|-------|----------|-------------|
|
|
165
|
+
| `tool` | No | Built-in tool to block for matching URLs, e.g. `web_fetch` |
|
|
166
|
+
| `urlPattern` | Yes | Glob pattern. `*` matches one path segment, `**` matches any depth |
|
|
167
|
+
| `credentialId` | Yes | Bastion credential ID |
|
|
168
|
+
| `action` | Yes | Action name for Bastion policy evaluation |
|
|
169
|
+
| `injection` | No | Override credential injection (`header` / `query` / `body`) |
|
|
170
|
+
| `params` | No | Dot-paths to extract Bastion policy params, e.g. `{ "amount": "body.amount" }` |
|
|
171
|
+
|
|
172
|
+
Rules are evaluated in order. Put more specific patterns before broader wildcards.
|
|
173
|
+
|
|
174
|
+
## Troubleshooting
|
|
175
|
+
|
|
176
|
+
**Plugin logs "server is unreachable"**
|
|
177
|
+
Bastion is not running or not reachable from OpenClaw. Start it with `docker compose up -d && npm run dev`.
|
|
178
|
+
|
|
179
|
+
**`bastion_fetch` returns "Blocked by Bastion policy"**
|
|
180
|
+
The agent's policy denied the action. Check Bastion policies or audit entries.
|
|
181
|
+
|
|
182
|
+
**`bastion_fetch` hangs for minutes**
|
|
183
|
+
The request hit a HITL rule and Bastion is waiting for approval. Review pending requests via `GET /v1/hitl/pending`.
|
|
184
|
+
|
|
185
|
+
**Direct `web_fetch` calls are blocked**
|
|
186
|
+
That is expected when a matching rule defines `tool: "web_fetch"`. Use `bastion_fetch` instead.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProxyExecuteResult } from '@bastion-ai/sdk';
|
|
2
|
+
import type { CompiledRule } from './ruleEngine.js';
|
|
3
|
+
export declare class BastionBridge {
|
|
4
|
+
private readonly client;
|
|
5
|
+
private readonly defaultTimeout;
|
|
6
|
+
constructor(serverUrl: string, agentSecret: string, defaultTimeout?: number);
|
|
7
|
+
/**
|
|
8
|
+
* Execute a proxied request through Bastion.
|
|
9
|
+
* Builds the ProxyExecuteInput from the matched rule and tool args, then
|
|
10
|
+
* delegates to the SDK. Throws BastionUnreachableError on network failures
|
|
11
|
+
* and BastionBlockedError when the policy denies the request.
|
|
12
|
+
*/
|
|
13
|
+
executeProxy(rule: CompiledRule, toolArgs: Record<string, unknown>): Promise<ProxyExecuteResult>;
|
|
14
|
+
/** Check Bastion server availability. Returns false if unreachable. */
|
|
15
|
+
healthCheck(): Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=bastionBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bastionBridge.d.ts","sourceRoot":"","sources":["../src/bastionBridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAI1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKpD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,SAAS;IAK3E;;;;;OAKG;IACG,YAAY,CAChB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAyC9B,uEAAuE;IACjE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAQtC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BastionClient } from '@bastion-ai/sdk';
|
|
2
|
+
import { BastionForbiddenError } from '@bastion-ai/sdk';
|
|
3
|
+
import { BastionUnreachableError, BastionBlockedError } from './errors.js';
|
|
4
|
+
import { extractParams } from './ruleEngine.js';
|
|
5
|
+
/** Timeout for HITL-escalated requests (5.5 min — slightly above Bastion's 5-min HITL window). */
|
|
6
|
+
const HITL_CLIENT_TIMEOUT_MS = 330_000;
|
|
7
|
+
export class BastionBridge {
|
|
8
|
+
client;
|
|
9
|
+
defaultTimeout;
|
|
10
|
+
constructor(serverUrl, agentSecret, defaultTimeout = 30_000) {
|
|
11
|
+
this.client = new BastionClient({ baseUrl: serverUrl, apiKey: agentSecret });
|
|
12
|
+
this.defaultTimeout = defaultTimeout;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Execute a proxied request through Bastion.
|
|
16
|
+
* Builds the ProxyExecuteInput from the matched rule and tool args, then
|
|
17
|
+
* delegates to the SDK. Throws BastionUnreachableError on network failures
|
|
18
|
+
* and BastionBlockedError when the policy denies the request.
|
|
19
|
+
*/
|
|
20
|
+
async executeProxy(rule, toolArgs) {
|
|
21
|
+
const url = toolArgs['url'];
|
|
22
|
+
const method = typeof toolArgs['method'] === 'string' ? toolArgs['method'] : 'GET';
|
|
23
|
+
const headers = typeof toolArgs['headers'] === 'object' && toolArgs['headers'] !== null
|
|
24
|
+
? toolArgs['headers']
|
|
25
|
+
: {};
|
|
26
|
+
const body = toolArgs['body'];
|
|
27
|
+
const requestedTimeout = typeof toolArgs['timeout'] === 'number' && toolArgs['timeout'] > 0
|
|
28
|
+
? toolArgs['timeout']
|
|
29
|
+
: this.defaultTimeout;
|
|
30
|
+
const params = rule.params ? extractParams(toolArgs, rule.params) : undefined;
|
|
31
|
+
// For HITL escalations, Bastion may block up to 5 min — use a longer client timeout.
|
|
32
|
+
const timeout = Math.max(requestedTimeout, HITL_CLIENT_TIMEOUT_MS);
|
|
33
|
+
try {
|
|
34
|
+
return await this.client.execute({
|
|
35
|
+
credentialId: rule.credentialId,
|
|
36
|
+
action: rule.action,
|
|
37
|
+
params: Object.keys(params ?? {}).length > 0 ? params : undefined,
|
|
38
|
+
target: { url, method, headers, body },
|
|
39
|
+
injection: rule.injection,
|
|
40
|
+
timeout,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof BastionForbiddenError) {
|
|
45
|
+
throw new BastionBlockedError(error.message);
|
|
46
|
+
}
|
|
47
|
+
// Network-level failure (TypeError from fetch, or any non-HTTP error)
|
|
48
|
+
if (error instanceof TypeError || isNetworkError(error)) {
|
|
49
|
+
throw new BastionUnreachableError(`Bastion server unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Check Bastion server availability. Returns false if unreachable. */
|
|
55
|
+
async healthCheck() {
|
|
56
|
+
try {
|
|
57
|
+
await this.client.health();
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isNetworkError(error) {
|
|
66
|
+
if (!(error instanceof Error))
|
|
67
|
+
return false;
|
|
68
|
+
// Node fetch throws TypeError for network errors; also check for ECONNREFUSED etc.
|
|
69
|
+
return (error.message.includes('ECONNREFUSED') ||
|
|
70
|
+
error.message.includes('ENOTFOUND') ||
|
|
71
|
+
error.message.includes('fetch failed'));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=bastionBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bastionBridge.js","sourceRoot":"","sources":["../src/bastionBridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,kGAAkG;AAClG,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAEvC,MAAM,OAAO,aAAa;IACP,MAAM,CAAgB;IACtB,cAAc,CAAS;IAExC,YAAY,SAAiB,EAAE,WAAmB,EAAE,cAAc,GAAG,MAAM;QACzE,IAAI,CAAC,MAAM,GAAG,IAAI,aAAa,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAChB,IAAkB,EAClB,QAAiC;QAEjC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAW,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACnF,MAAM,OAAO,GACX,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,KAAK,IAAI;YACrE,CAAC,CAAE,QAAQ,CAAC,SAAS,CAA4B;YACjD,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,gBAAgB,GACpB,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC;YAChE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;QAE1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE9E,qFAAqF;QACrF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;QAEnE,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;gBAC/B,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACjE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;gBACtC,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;gBAC3C,MAAM,IAAI,mBAAmB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,CAAC;YACD,sEAAsE;YACtE,IAAI,KAAK,YAAY,SAAS,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxD,MAAM,IAAI,uBAAuB,CAC/B,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACxF,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,mFAAmF;IACnF,OAAO,CACL,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QACtC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QACnC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CACvC,CAAC;AACJ,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class BastionUnreachableError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'BastionUnreachableError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class BastionBlockedError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'BastionBlockedError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default } from './plugin.js';
|
|
2
|
+
export { BASTION_FETCH_TOOL_NAME } from './plugin.js';
|
|
3
|
+
export type { BastionPluginConfig, BastionFetchResponse, BastionFetchToolInput, InterceptionRule, SecretValue, InjectionConfig, } from './types.js';
|
|
4
|
+
export { BastionUnreachableError, BastionBlockedError } from './errors.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAStD,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,iBAAiB,EAKlB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAqHvD,wBAA8B,aAAa,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgFjF"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { resolveSecret } from './secretRef.js';
|
|
2
|
+
import { compileRules, matchRule, matchRuleByUrl } from './ruleEngine.js';
|
|
3
|
+
import { adaptToolResult } from './responseAdapter.js';
|
|
4
|
+
import { BastionBridge } from './bastionBridge.js';
|
|
5
|
+
import { BastionUnreachableError, BastionBlockedError } from './errors.js';
|
|
6
|
+
export const BASTION_FETCH_TOOL_NAME = 'bastion_fetch';
|
|
7
|
+
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
|
|
8
|
+
const BASTION_FETCH_TOOL_SCHEMA = {
|
|
9
|
+
type: 'object',
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
required: ['url'],
|
|
12
|
+
properties: {
|
|
13
|
+
url: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Absolute URL to request through Bastion.',
|
|
16
|
+
},
|
|
17
|
+
method: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
enum: [...HTTP_METHODS],
|
|
20
|
+
description: 'HTTP method. Defaults to GET.',
|
|
21
|
+
},
|
|
22
|
+
headers: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
description: 'Optional request headers.',
|
|
25
|
+
additionalProperties: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
body: {
|
|
30
|
+
description: 'Optional JSON request body forwarded through Bastion.',
|
|
31
|
+
},
|
|
32
|
+
timeout: {
|
|
33
|
+
type: 'number',
|
|
34
|
+
minimum: 1,
|
|
35
|
+
description: 'Optional per-request timeout in milliseconds.',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
function validateConfig(config) {
|
|
40
|
+
if (!config || typeof config !== 'object') {
|
|
41
|
+
throw new Error('Bastion plugin: config is required');
|
|
42
|
+
}
|
|
43
|
+
const c = config;
|
|
44
|
+
if (!c['serverUrl'] || typeof c['serverUrl'] !== 'string') {
|
|
45
|
+
throw new Error('Bastion plugin: config.serverUrl is required and must be a string');
|
|
46
|
+
}
|
|
47
|
+
if (c['agentSecret'] === undefined || c['agentSecret'] === null) {
|
|
48
|
+
throw new Error('Bastion plugin: config.agentSecret is required');
|
|
49
|
+
}
|
|
50
|
+
if (!Array.isArray(c['rules']) || c['rules'].length === 0) {
|
|
51
|
+
throw new Error('Bastion plugin: config.rules must be a non-empty array');
|
|
52
|
+
}
|
|
53
|
+
return c;
|
|
54
|
+
}
|
|
55
|
+
function assertRecord(value, label) {
|
|
56
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
57
|
+
throw new Error(`Bastion plugin: ${label} must be an object`);
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
function normalizeFetchInput(params) {
|
|
62
|
+
const input = assertRecord(params, 'tool params');
|
|
63
|
+
if (typeof input['url'] !== 'string' || input['url'].length === 0) {
|
|
64
|
+
throw new Error('Bastion plugin: url is required');
|
|
65
|
+
}
|
|
66
|
+
const method = input['method'] ? String(input['method']).toUpperCase() : 'GET';
|
|
67
|
+
if (!HTTP_METHODS.includes(method)) {
|
|
68
|
+
throw new Error(`Bastion plugin: method must be one of ${HTTP_METHODS.join(', ')}`);
|
|
69
|
+
}
|
|
70
|
+
let headers;
|
|
71
|
+
if (input['headers'] !== undefined) {
|
|
72
|
+
const rawHeaders = assertRecord(input['headers'], 'headers');
|
|
73
|
+
headers = {};
|
|
74
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
75
|
+
if (typeof value !== 'string') {
|
|
76
|
+
throw new Error(`Bastion plugin: headers["${key}"] must be a string`);
|
|
77
|
+
}
|
|
78
|
+
headers[key] = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let timeout;
|
|
82
|
+
if (input['timeout'] !== undefined) {
|
|
83
|
+
if (typeof input['timeout'] !== 'number' || input['timeout'] <= 0) {
|
|
84
|
+
throw new Error('Bastion plugin: timeout must be a positive number');
|
|
85
|
+
}
|
|
86
|
+
timeout = input['timeout'];
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
url: input['url'],
|
|
90
|
+
method,
|
|
91
|
+
headers,
|
|
92
|
+
body: input['body'],
|
|
93
|
+
timeout,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function toToolArgs(input) {
|
|
97
|
+
return input;
|
|
98
|
+
}
|
|
99
|
+
function normalizeExecutionError(error) {
|
|
100
|
+
if (error instanceof BastionBlockedError) {
|
|
101
|
+
return new Error(`Blocked by Bastion policy: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
if (error instanceof BastionUnreachableError) {
|
|
104
|
+
return new Error('Bastion server unreachable. Request blocked (fail-closed).');
|
|
105
|
+
}
|
|
106
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
107
|
+
}
|
|
108
|
+
export default async function bastionPlugin(api) {
|
|
109
|
+
// 1. Validate config from the released OpenClaw plugin API surface.
|
|
110
|
+
const pluginConfig = validateConfig(api.pluginConfig);
|
|
111
|
+
// 2. Resolve agent secret
|
|
112
|
+
const agentSecret = await resolveSecret(pluginConfig.agentSecret);
|
|
113
|
+
// 3. Compile rules (glob → regex) at startup
|
|
114
|
+
const compiledRules = compileRules(pluginConfig.rules);
|
|
115
|
+
// 4. Instantiate Bastion bridge
|
|
116
|
+
const bridge = new BastionBridge(pluginConfig.serverUrl, agentSecret, pluginConfig.timeout);
|
|
117
|
+
// 5. Non-blocking health check at startup — just warn if unreachable
|
|
118
|
+
bridge
|
|
119
|
+
.healthCheck()
|
|
120
|
+
.then((ok) => {
|
|
121
|
+
if (!ok) {
|
|
122
|
+
api.logger.warn(`Bastion plugin: server at ${pluginConfig.serverUrl} is unreachable. ` +
|
|
123
|
+
`${BASTION_FETCH_TOOL_NAME} requests will fail closed.`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
api.logger.info(`Bastion plugin: connected to ${pluginConfig.serverUrl}`);
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
.catch(() => {
|
|
130
|
+
// healthCheck already catches internally — this is just a safety net
|
|
131
|
+
});
|
|
132
|
+
// 6. Register the Bastion-backed tool for current OpenClaw releases.
|
|
133
|
+
api.registerTool({
|
|
134
|
+
name: BASTION_FETCH_TOOL_NAME,
|
|
135
|
+
label: 'Bastion Fetch',
|
|
136
|
+
description: 'Execute outbound HTTP requests through Bastion using configured URL rules.',
|
|
137
|
+
parameters: BASTION_FETCH_TOOL_SCHEMA,
|
|
138
|
+
async execute(_toolCallId, params) {
|
|
139
|
+
const input = normalizeFetchInput(params);
|
|
140
|
+
const toolArgs = toToolArgs(input);
|
|
141
|
+
const rule = matchRuleByUrl(toolArgs, compiledRules);
|
|
142
|
+
if (!rule) {
|
|
143
|
+
throw new Error(`No Bastion rule matches ${input.url}`);
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const result = await bridge.executeProxy(rule, toolArgs);
|
|
147
|
+
return adaptToolResult(result, input.url);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw normalizeExecutionError(error);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
// 7. Register a bypass-blocking hook for tools explicitly listed in rules.
|
|
155
|
+
api.on('before_tool_call', (event) => {
|
|
156
|
+
const { toolName, params } = event;
|
|
157
|
+
if (toolName === BASTION_FETCH_TOOL_NAME) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const rule = matchRule(toolName, params, compiledRules);
|
|
161
|
+
if (!rule) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
const url = typeof params['url'] === 'string' ? params['url'] : 'the requested URL';
|
|
165
|
+
return {
|
|
166
|
+
block: true,
|
|
167
|
+
blockReason: `Requests to ${url} must use ${BASTION_FETCH_TOOL_NAME} so Bastion can enforce policy and inject credentials.`,
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
// 8. Register service for lifecycle awareness
|
|
171
|
+
api.registerService({
|
|
172
|
+
id: 'bastion-fetch',
|
|
173
|
+
start: () => api.logger.info('Bastion fetch plugin active'),
|
|
174
|
+
stop: () => api.logger.info('Bastion fetch plugin stopped'),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProxyExecuteResult } from '@bastion-ai/sdk';
|
|
2
|
+
import type { BastionFetchResponse, OpenClawToolResult } from './types.js';
|
|
3
|
+
export declare function buildBastionResponse(result: ProxyExecuteResult, originalUrl: string): BastionFetchResponse;
|
|
4
|
+
/**
|
|
5
|
+
* Adapt the Bastion response into a standard OpenClaw tool result.
|
|
6
|
+
* `details` keeps the structured payload, while `content` gives the model a
|
|
7
|
+
* readable JSON summary in the same turn.
|
|
8
|
+
*/
|
|
9
|
+
export declare function adaptToolResult(result: ProxyExecuteResult, originalUrl: string): OpenClawToolResult;
|
|
10
|
+
//# sourceMappingURL=responseAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseAdapter.d.ts","sourceRoot":"","sources":["../src/responseAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE3E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,MAAM,GAClB,oBAAoB,CAgBtB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,MAAM,GAClB,kBAAkB,CAYpB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function buildBastionResponse(result, originalUrl) {
|
|
2
|
+
return {
|
|
3
|
+
status: result.upstream.status,
|
|
4
|
+
headers: result.upstream.headers,
|
|
5
|
+
body: result.upstream.body ?? null,
|
|
6
|
+
url: originalUrl,
|
|
7
|
+
_bastion: {
|
|
8
|
+
credentialId: result.meta.credentialId,
|
|
9
|
+
action: result.meta.action,
|
|
10
|
+
policyDecision: result.meta.policyDecision,
|
|
11
|
+
durationMs: result.meta.durationMs,
|
|
12
|
+
...(result.meta.hitlRequestId !== undefined
|
|
13
|
+
? { hitlRequestId: result.meta.hitlRequestId }
|
|
14
|
+
: {}),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Adapt the Bastion response into a standard OpenClaw tool result.
|
|
20
|
+
* `details` keeps the structured payload, while `content` gives the model a
|
|
21
|
+
* readable JSON summary in the same turn.
|
|
22
|
+
*/
|
|
23
|
+
export function adaptToolResult(result, originalUrl) {
|
|
24
|
+
const payload = buildBastionResponse(result, originalUrl);
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: JSON.stringify(payload, null, 2),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
details: payload,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=responseAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseAdapter.js","sourceRoot":"","sources":["../src/responseAdapter.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,oBAAoB,CAClC,MAA0B,EAC1B,WAAmB;IAEnB,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;QAC9B,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO;QAChC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI;QAClC,GAAG,EAAE,WAAW;QAChB,QAAQ,EAAE;YACR,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY;YACtC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM;YAC1B,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,cAAc;YAC1C,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU;YAClC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,KAAK,SAAS;gBACzC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE;gBAC9C,CAAC,CAAC,EAAE,CAAC;SACR;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA0B,EAC1B,WAAmB;IAEnB,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAE1D,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;aACvC;SACF;QACD,OAAO,EAAE,OAAO;KACjB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InterceptionRule, ParamsMapping } from './types.js';
|
|
2
|
+
export interface CompiledRule extends InterceptionRule {
|
|
3
|
+
urlRegex: RegExp;
|
|
4
|
+
}
|
|
5
|
+
/** Compile all rules, converting urlPattern globs to RegExp at startup. */
|
|
6
|
+
export declare function compileRules(rules: InterceptionRule[]): CompiledRule[];
|
|
7
|
+
/**
|
|
8
|
+
* Find the first matching rule for this tool call.
|
|
9
|
+
* Only rules with an explicit `tool` are considered, since this matcher is
|
|
10
|
+
* used for bypass-blocking hooks on built-in tools like `web_fetch`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function matchRule(toolName: string, toolArgs: Record<string, unknown>, rules: CompiledRule[]): CompiledRule | null;
|
|
13
|
+
/**
|
|
14
|
+
* Find the first matching rule by URL only.
|
|
15
|
+
* Used by the plugin's registered `bastion_fetch` tool.
|
|
16
|
+
*/
|
|
17
|
+
export declare function matchRuleByUrl(toolArgs: Record<string, unknown>, rules: CompiledRule[]): CompiledRule | null;
|
|
18
|
+
/** Extract Bastion policy params from tool args using a ParamsMapping. */
|
|
19
|
+
export declare function extractParams(args: Record<string, unknown>, mapping: ParamsMapping): {
|
|
20
|
+
amount?: number;
|
|
21
|
+
ip?: string;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=ruleEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ruleEngine.d.ts","sourceRoot":"","sources":["../src/ruleEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,QAAQ,EAAE,MAAM,CAAC;CAClB;AA+BD,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,YAAY,EAAE,CAKtE;AAMD;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,KAAK,EAAE,YAAY,EAAE,GACpB,YAAY,GAAG,IAAI,CAWrB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,KAAK,EAAE,YAAY,EAAE,GACpB,YAAY,GAAG,IAAI,CAWrB;AAaD,0EAA0E;AAC1E,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,aAAa,GACrB;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBlC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a glob URL pattern to a RegExp.
|
|
3
|
+
* - `**` matches anything (including path separators)
|
|
4
|
+
* - `*` matches any character except `/`
|
|
5
|
+
* - All other characters are escaped
|
|
6
|
+
*/
|
|
7
|
+
function globToRegex(pattern) {
|
|
8
|
+
let regexStr = '';
|
|
9
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
10
|
+
const ch = pattern[i];
|
|
11
|
+
if (ch === '*' && pattern[i + 1] === '*') {
|
|
12
|
+
regexStr += '.*';
|
|
13
|
+
i++; // skip second *
|
|
14
|
+
// skip optional trailing slash after **
|
|
15
|
+
if (pattern[i + 1] === '/') {
|
|
16
|
+
regexStr += '/?';
|
|
17
|
+
i++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else if (ch === '*') {
|
|
21
|
+
regexStr += '[^/]*';
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
regexStr += ch.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return new RegExp(`^${regexStr}$`);
|
|
28
|
+
}
|
|
29
|
+
/** Compile all rules, converting urlPattern globs to RegExp at startup. */
|
|
30
|
+
export function compileRules(rules) {
|
|
31
|
+
return rules.map((rule) => ({
|
|
32
|
+
...rule,
|
|
33
|
+
urlRegex: globToRegex(rule.urlPattern),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
function getUrl(toolArgs) {
|
|
37
|
+
return typeof toolArgs['url'] === 'string' ? toolArgs['url'] : null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Find the first matching rule for this tool call.
|
|
41
|
+
* Only rules with an explicit `tool` are considered, since this matcher is
|
|
42
|
+
* used for bypass-blocking hooks on built-in tools like `web_fetch`.
|
|
43
|
+
*/
|
|
44
|
+
export function matchRule(toolName, toolArgs, rules) {
|
|
45
|
+
const url = getUrl(toolArgs);
|
|
46
|
+
if (!url)
|
|
47
|
+
return null;
|
|
48
|
+
for (const rule of rules) {
|
|
49
|
+
if (rule.tool === toolName && rule.urlRegex.test(url)) {
|
|
50
|
+
return rule;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find the first matching rule by URL only.
|
|
57
|
+
* Used by the plugin's registered `bastion_fetch` tool.
|
|
58
|
+
*/
|
|
59
|
+
export function matchRuleByUrl(toolArgs, rules) {
|
|
60
|
+
const url = getUrl(toolArgs);
|
|
61
|
+
if (!url)
|
|
62
|
+
return null;
|
|
63
|
+
for (const rule of rules) {
|
|
64
|
+
if (rule.urlRegex.test(url)) {
|
|
65
|
+
return rule;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/** Resolve a dot-path like "body.amount" against an args object. */
|
|
71
|
+
function resolvePath(args, path) {
|
|
72
|
+
const parts = path.split('.');
|
|
73
|
+
let current = args;
|
|
74
|
+
for (const part of parts) {
|
|
75
|
+
if (current === null || typeof current !== 'object')
|
|
76
|
+
return undefined;
|
|
77
|
+
current = current[part];
|
|
78
|
+
}
|
|
79
|
+
return current;
|
|
80
|
+
}
|
|
81
|
+
/** Extract Bastion policy params from tool args using a ParamsMapping. */
|
|
82
|
+
export function extractParams(args, mapping) {
|
|
83
|
+
const result = {};
|
|
84
|
+
if (mapping.amount !== undefined) {
|
|
85
|
+
const raw = resolvePath(args, mapping.amount);
|
|
86
|
+
if (typeof raw === 'number') {
|
|
87
|
+
result.amount = raw;
|
|
88
|
+
}
|
|
89
|
+
else if (typeof raw === 'string') {
|
|
90
|
+
const parsed = parseFloat(raw);
|
|
91
|
+
if (!isNaN(parsed))
|
|
92
|
+
result.amount = parsed;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (mapping.ip !== undefined) {
|
|
96
|
+
const raw = resolvePath(args, mapping.ip);
|
|
97
|
+
if (typeof raw === 'string') {
|
|
98
|
+
result.ip = raw;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=ruleEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ruleEngine.js","sourceRoot":"","sources":["../src/ruleEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SecretValue } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a SecretValue to a plain string at runtime.
|
|
4
|
+
* Supports plain strings, $env, $file, and $exec sources.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveSecret(ref: SecretValue): Promise<string>;
|
|
7
|
+
//# sourceMappingURL=secretRef.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretRef.d.ts","sourceRoot":"","sources":["../src/secretRef.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;GAGG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBrE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a SecretValue to a plain string at runtime.
|
|
5
|
+
* Supports plain strings, $env, $file, and $exec sources.
|
|
6
|
+
*/
|
|
7
|
+
export async function resolveSecret(ref) {
|
|
8
|
+
let value;
|
|
9
|
+
if (typeof ref === 'string') {
|
|
10
|
+
value = ref;
|
|
11
|
+
}
|
|
12
|
+
else if ('$env' in ref) {
|
|
13
|
+
const envValue = process.env[ref.$env];
|
|
14
|
+
if (!envValue) {
|
|
15
|
+
throw new Error(`Environment variable "${ref.$env}" is not set or empty`);
|
|
16
|
+
}
|
|
17
|
+
value = envValue;
|
|
18
|
+
}
|
|
19
|
+
else if ('$file' in ref) {
|
|
20
|
+
const contents = readFileSync(ref.$file, 'utf8');
|
|
21
|
+
value = contents.trim();
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
const stdout = execSync(ref.$exec, { encoding: 'utf8' });
|
|
25
|
+
value = stdout.trim();
|
|
26
|
+
}
|
|
27
|
+
if (!value) {
|
|
28
|
+
throw new Error('Resolved secret is empty');
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=secretRef.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretRef.js","sourceRoot":"","sources":["../src/secretRef.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAgB;IAClD,IAAI,KAAa,CAAC;IAElB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,KAAK,GAAG,GAAG,CAAC;IACd,CAAC;SAAM,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,IAAI,uBAAuB,CAAC,CAAC;QAC5E,CAAC;QACD,KAAK,GAAG,QAAQ,CAAC;IACnB,CAAC;SAAM,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACjD,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACzD,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export interface OpenClawLogger {
|
|
2
|
+
info(msg: string): void;
|
|
3
|
+
warn(msg: string): void;
|
|
4
|
+
error(msg: string): void;
|
|
5
|
+
}
|
|
6
|
+
export interface OpenClawService {
|
|
7
|
+
id: string;
|
|
8
|
+
start?: () => void;
|
|
9
|
+
stop?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface OpenClawToolTextContent {
|
|
12
|
+
type: 'text';
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OpenClawToolResult {
|
|
16
|
+
content: OpenClawToolTextContent[];
|
|
17
|
+
details?: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface OpenClawToolDefinition {
|
|
20
|
+
name: string;
|
|
21
|
+
label?: string;
|
|
22
|
+
description: string;
|
|
23
|
+
parameters: Record<string, unknown>;
|
|
24
|
+
execute(toolCallId: string | undefined, params: unknown): Promise<OpenClawToolResult> | OpenClawToolResult;
|
|
25
|
+
}
|
|
26
|
+
export interface OpenClawPluginApi {
|
|
27
|
+
pluginConfig?: Record<string, unknown>;
|
|
28
|
+
on(event: string, handler: (...args: unknown[]) => unknown): void;
|
|
29
|
+
registerTool(tool: OpenClawToolDefinition): void;
|
|
30
|
+
registerService(service: OpenClawService): void;
|
|
31
|
+
logger: OpenClawLogger;
|
|
32
|
+
}
|
|
33
|
+
export interface BeforeCallEvent {
|
|
34
|
+
toolName: string;
|
|
35
|
+
params: Record<string, unknown>;
|
|
36
|
+
runId?: string;
|
|
37
|
+
toolCallId?: string;
|
|
38
|
+
}
|
|
39
|
+
export type BeforeCallResult = {
|
|
40
|
+
params?: Record<string, unknown>;
|
|
41
|
+
block?: boolean;
|
|
42
|
+
blockReason?: string;
|
|
43
|
+
} | undefined;
|
|
44
|
+
/** Resolves to a secret string at runtime. */
|
|
45
|
+
export type SecretValue = string | {
|
|
46
|
+
$env: string;
|
|
47
|
+
} | {
|
|
48
|
+
$file: string;
|
|
49
|
+
} | {
|
|
50
|
+
$exec: string;
|
|
51
|
+
};
|
|
52
|
+
export interface InjectionConfig {
|
|
53
|
+
location: 'header' | 'query' | 'body';
|
|
54
|
+
key: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Dot-path mappings from tool args into Bastion policy params.
|
|
58
|
+
* e.g. amount: "body.amount" extracts args.body.amount as a number.
|
|
59
|
+
*/
|
|
60
|
+
export interface ParamsMapping {
|
|
61
|
+
amount?: string;
|
|
62
|
+
ip?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface InterceptionRule {
|
|
65
|
+
/**
|
|
66
|
+
* Optional tool name to block for this URL pattern.
|
|
67
|
+
* Example: "web_fetch" to prevent bypassing `bastion_fetch`.
|
|
68
|
+
*/
|
|
69
|
+
tool?: string;
|
|
70
|
+
/** Glob pattern for the URL (e.g. "https://api.stripe.com/**"). */
|
|
71
|
+
urlPattern: string;
|
|
72
|
+
/** Bastion credential ID to use for this call. */
|
|
73
|
+
credentialId: string;
|
|
74
|
+
/** Bastion action name (e.g. "stripe.charges"). */
|
|
75
|
+
action: string;
|
|
76
|
+
/** Override default credential injection location. */
|
|
77
|
+
injection?: InjectionConfig;
|
|
78
|
+
/** Extract policy params from tool args. */
|
|
79
|
+
params?: ParamsMapping;
|
|
80
|
+
}
|
|
81
|
+
export interface BastionPluginConfig {
|
|
82
|
+
/** URL of the Bastion server (e.g. "http://localhost:3000"). */
|
|
83
|
+
serverUrl: string;
|
|
84
|
+
/** Agent secret (bst_... token) — supports SecretRef pattern. */
|
|
85
|
+
agentSecret: SecretValue;
|
|
86
|
+
/** Ordered list of protected routes. First match wins. */
|
|
87
|
+
rules: InterceptionRule[];
|
|
88
|
+
/** Request timeout in ms. Default: 30000. */
|
|
89
|
+
timeout?: number;
|
|
90
|
+
}
|
|
91
|
+
export interface BastionFetchToolInput {
|
|
92
|
+
url: string;
|
|
93
|
+
method?: string;
|
|
94
|
+
headers?: Record<string, string>;
|
|
95
|
+
body?: unknown;
|
|
96
|
+
timeout?: number;
|
|
97
|
+
}
|
|
98
|
+
export interface BastionFetchResponse {
|
|
99
|
+
status: number;
|
|
100
|
+
headers: Record<string, string>;
|
|
101
|
+
body: unknown | null;
|
|
102
|
+
url: string;
|
|
103
|
+
_bastion: {
|
|
104
|
+
credentialId: string;
|
|
105
|
+
action: string;
|
|
106
|
+
policyDecision: string;
|
|
107
|
+
durationMs: number;
|
|
108
|
+
hitlRequestId?: string;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gFAAgF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "bastion-fetch",
|
|
3
|
+
"name": "Bastion Fetch",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Secure outbound HTTP requests with Bastion — credential vault, policy engine, HITL gate, audit trail",
|
|
6
|
+
"uiHints": {
|
|
7
|
+
"serverUrl": {
|
|
8
|
+
"label": "Bastion Server URL",
|
|
9
|
+
"placeholder": "http://localhost:3000"
|
|
10
|
+
},
|
|
11
|
+
"agentSecret": {
|
|
12
|
+
"label": "Agent Secret",
|
|
13
|
+
"sensitive": true
|
|
14
|
+
},
|
|
15
|
+
"rules": {
|
|
16
|
+
"label": "Protected Routes"
|
|
17
|
+
},
|
|
18
|
+
"timeout": {
|
|
19
|
+
"label": "Default Timeout (ms)",
|
|
20
|
+
"advanced": true
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"configSchema": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"required": ["serverUrl", "agentSecret", "rules"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"serverUrl": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "URL of your Bastion server (e.g. http://localhost:3000)"
|
|
31
|
+
},
|
|
32
|
+
"agentSecret": {
|
|
33
|
+
"description": "Agent secret (bst_... token). Supports $env, $file, $exec SecretRef."
|
|
34
|
+
},
|
|
35
|
+
"rules": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"minItems": 1,
|
|
38
|
+
"description": "Ordered list of protected routes. First match wins.",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"required": ["urlPattern", "credentialId", "action"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"tool": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Optional built-in tool to block for matching URLs (e.g. web_fetch)"
|
|
47
|
+
},
|
|
48
|
+
"urlPattern": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
},
|
|
51
|
+
"credentialId": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
},
|
|
54
|
+
"action": {
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
57
|
+
"injection": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"location": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"enum": ["header", "query", "body"]
|
|
64
|
+
},
|
|
65
|
+
"key": {
|
|
66
|
+
"type": "string"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"params": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"properties": {
|
|
74
|
+
"amount": {
|
|
75
|
+
"type": "string"
|
|
76
|
+
},
|
|
77
|
+
"ip": {
|
|
78
|
+
"type": "string"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"timeout": {
|
|
86
|
+
"type": "number",
|
|
87
|
+
"default": 30000,
|
|
88
|
+
"description": "Default request timeout in milliseconds"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bastion-ai/openclaw-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenClaw plugin for Bastion — adds a Bastion-backed HTTP tool and blocks direct bypasses for protected URLs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"homepage": "https://github.com/Matthieuhakim/Bastion#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/Matthieuhakim/Bastion.git",
|
|
13
|
+
"directory": "packages/openclaw-plugin"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/Matthieuhakim/Bastion/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"agents",
|
|
21
|
+
"security",
|
|
22
|
+
"proxy",
|
|
23
|
+
"audit",
|
|
24
|
+
"openclaw",
|
|
25
|
+
"plugin",
|
|
26
|
+
"typescript"
|
|
27
|
+
],
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"openclaw": {
|
|
35
|
+
"extensions": ["./dist/index.js"],
|
|
36
|
+
"install": {
|
|
37
|
+
"npmSpec": "@bastion-ai/openclaw-plugin",
|
|
38
|
+
"defaultChoice": "npm"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": ["dist", "openclaw.plugin.json"],
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22.0.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"openclaw": ">=2026.3.13"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"clean": "node --eval \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
54
|
+
"build": "npm run clean && tsc",
|
|
55
|
+
"dev": "tsc --watch",
|
|
56
|
+
"prepack": "npm run build",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@bastion-ai/sdk": "^0.1.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"typescript": "^5.7.0",
|
|
65
|
+
"vitest": "^3.0.0"
|
|
66
|
+
}
|
|
67
|
+
}
|