@xshieldai/n8n-nodes-kavachos 1.5.1 → 1.5.2
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
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @xshieldai/n8n-nodes-kavachos
|
|
2
2
|
|
|
3
|
-
> **🔍 Verification status (2026-10-12 IST — v1.5.
|
|
4
|
-
> - **Tests:** ⚠️ none inside the package. In the repository
|
|
3
|
+
> **🔍 Verification status (2026-10-12 IST — v1.5.2)**
|
|
4
|
+
> - **Tests:** ⚠️ none inside the package. In the repository: [red-team/n8n-kavach-run.battery.sh](https://github.com/rocketlang/aegis/blob/master/red-team/n8n-kavach-run.battery.sh) drives the Run and Gate nodes outside n8n against an installed copy (35 cases), and [red-team/n8n-inside-n8n.battery.sh](https://github.com/rocketlang/aegis/blob/master/red-team/n8n-inside-n8n.battery.sh) installs the package through n8n's own installer and runs all four nodes in workflows inside n8n 2.42.7 (23 cases; first run on 1.5.1, 12 October 2026). Gate, Budget and Audit run against a real Aegis there. **Kavach Run is tested with a stand-in `kavachos` in both: neither battery launches anything under kernel enforcement.**
|
|
5
5
|
> - **Examples:** ✅ 1 importable workflow, in the repository (not in the npm package): [examples/n8n-governed-agent.json](https://github.com/rocketlang/aegis/blob/master/packages/n8n-nodes-kavachos/examples/n8n-governed-agent.json). Drag into n8n, wire the Aegis API credential pointing at `http://localhost:4850`, run.
|
|
6
6
|
> - **Live demo:** ⚠️ planned (Tier 3)
|
|
7
7
|
> - **Phase-1 limits:** KavachRun is Linux-only; where enforcement is unavailable (non-Linux, or `kavachos` not installed) it **fails closed by default** (`On Non-Linux` = `throw`; `warn`/`skip` are opt-in). KavachGate works on any OS — it's a thin HTTP relay to AEGIS.
|
|
8
8
|
>
|
|
9
|
-
> The KavachGate node's behaviour is end-to-end testable against a live AEGIS instance — start AEGIS, import the example workflow, fire a `rm -rf /` command, watch it block.
|
|
9
|
+
> The KavachGate node's behaviour is end-to-end testable against a live AEGIS instance — start AEGIS, import the example workflow, fire a `rm -rf /` command, watch it block.
|
|
10
10
|
|
|
11
11
|
> **Renamed at 1.5.0.** This package was `@xshieldai/n8n-nodes` through 1.4.1. n8n's community-nodes screen
|
|
12
12
|
> only installs a package whose name starts with `n8n-nodes-` after the scope, and refused the old name. The
|
|
@@ -41,6 +41,9 @@ Wraps a subprocess in kavachos **kernel enforcement** (seccomp-bpf + cgroup BPF
|
|
|
41
41
|
`kavachos run <options> -- <binary> <arguments>`, so the binary's arguments are passed on word for word and
|
|
42
42
|
cannot be read as kavachos options. With an older `kavachos` the node refuses to run. See the changelog.
|
|
43
43
|
|
|
44
|
+
Since 1.5.2 an argument in quotes is one word: `--name 'two words'`. Nothing else in the **Arguments** field is
|
|
45
|
+
read specially (it is not a shell), and a quote that is never closed stops the node before anything runs.
|
|
46
|
+
|
|
44
47
|
Since 1.4.0 the node waits for the command without stopping n8n, stops it at **Time Limit (seconds)**, and
|
|
45
48
|
reports what `kavachos` itself said: a refused launch is an error, and `enforced` is true only when no layer
|
|
46
49
|
was reported missing (`egress_constrained`, `path_jail_loaded`). It reads kavachos's own lines for this, so
|
|
@@ -67,7 +67,7 @@ class KavachRun {
|
|
|
67
67
|
type: "string",
|
|
68
68
|
default: "",
|
|
69
69
|
placeholder: "src/my-agent.ts --port 4900",
|
|
70
|
-
description: "
|
|
70
|
+
description: "Arguments for the binary, separated by spaces. Put quotes around an argument that holds a space: --name 'two words'. Nothing else is read specially (this is not a shell).",
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
displayName: "Trust Mask (hex)",
|
|
@@ -128,6 +128,10 @@ class KavachRun {
|
|
|
128
128
|
for (let i = 0; i < items.length; i++) {
|
|
129
129
|
const binary = this.getNodeParameter("binary", i);
|
|
130
130
|
const args = this.getNodeParameter("args", i).trim();
|
|
131
|
+
const split = (0, shared_1.splitWords)(args);
|
|
132
|
+
if (split.error)
|
|
133
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${split.error}. Nothing was run.`, { itemIndex: i });
|
|
134
|
+
const argWords = split.words;
|
|
131
135
|
const trustMask = this.getNodeParameter("trustMask", i);
|
|
132
136
|
const domain = this.getNodeParameter("domain", i);
|
|
133
137
|
const falco = this.getNodeParameter("falco", i);
|
|
@@ -156,8 +160,7 @@ class KavachRun {
|
|
|
156
160
|
}
|
|
157
161
|
// warn: run unwrapped with a flag in output
|
|
158
162
|
process.stderr.write(`[KavachRun] WARNING: ${reason} — running ${binary} unwrapped\n`);
|
|
159
|
-
const
|
|
160
|
-
const proc = await (0, shared_1.runProcess)(binary, argList, timeoutMs);
|
|
163
|
+
const proc = await (0, shared_1.runProcess)(binary, argWords, timeoutMs);
|
|
161
164
|
if (proc.error) {
|
|
162
165
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${binary} could not be started: ${proc.error}`, { itemIndex: i });
|
|
163
166
|
}
|
|
@@ -198,7 +201,7 @@ class KavachRun {
|
|
|
198
201
|
...(falco ? ["--falco"] : []),
|
|
199
202
|
"--",
|
|
200
203
|
binary,
|
|
201
|
-
...
|
|
204
|
+
...argWords,
|
|
202
205
|
];
|
|
203
206
|
const proc = await (0, shared_1.runProcess)("kavachos", kavachArgs, timeoutMs);
|
|
204
207
|
if (proc.error) {
|
package/dist/nodes/shared.d.ts
CHANGED
|
@@ -26,3 +26,7 @@ export interface JsonReply {
|
|
|
26
26
|
* is shorter than the gate's own wait for a level-1 approval.)
|
|
27
27
|
*/
|
|
28
28
|
export declare function requestJson(method: "GET" | "POST", url: string, headers: Record<string, string>, body: string | undefined, timeoutMs: number): Promise<JsonReply>;
|
|
29
|
+
export declare function splitWords(text: string): {
|
|
30
|
+
words: string[];
|
|
31
|
+
error?: string;
|
|
32
|
+
};
|
package/dist/nodes/shared.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.runProcess = runProcess;
|
|
37
37
|
exports.requestJson = requestJson;
|
|
38
|
+
exports.splitWords = splitWords;
|
|
38
39
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
39
40
|
// Shared by the Kavach nodes: running a command without stopping n8n, and asking Aegis with a time limit.
|
|
40
41
|
const child_process_1 = require("child_process");
|
|
@@ -137,3 +138,51 @@ function requestJson(method, url, headers, body, timeoutMs) {
|
|
|
137
138
|
req.end();
|
|
138
139
|
});
|
|
139
140
|
}
|
|
141
|
+
// The Arguments field as a list of words. Through 1.5.1 the field was cut at every space, so an argument that
|
|
142
|
+
// holds a space could not be passed. Now quotes group: '...' is taken as it stands, and in "..." a backslash
|
|
143
|
+
// before a double quote or another backslash gives that character. Everywhere else every character is itself:
|
|
144
|
+
// this is not a shell, so $, *, ~ and a backslash outside double quotes are not read as anything.
|
|
145
|
+
// A quote that is never closed is an error and not a guess.
|
|
146
|
+
function splitWords(text) {
|
|
147
|
+
const words = [];
|
|
148
|
+
let word = "", inWord = false, quote = null;
|
|
149
|
+
for (let i = 0; i < text.length; i++) {
|
|
150
|
+
const c = text[i];
|
|
151
|
+
if (quote === "'") {
|
|
152
|
+
if (c === "'")
|
|
153
|
+
quote = null;
|
|
154
|
+
else
|
|
155
|
+
word += c;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (quote === '"') {
|
|
159
|
+
if (c === '"')
|
|
160
|
+
quote = null;
|
|
161
|
+
else if (c === "\\" && (text[i + 1] === '"' || text[i + 1] === "\\"))
|
|
162
|
+
word += text[++i];
|
|
163
|
+
else
|
|
164
|
+
word += c;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (c === "'" || c === '"') {
|
|
168
|
+
quote = c;
|
|
169
|
+
inWord = true;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (/\s/.test(c)) {
|
|
173
|
+
if (inWord) {
|
|
174
|
+
words.push(word);
|
|
175
|
+
word = "";
|
|
176
|
+
inWord = false;
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
word += c;
|
|
181
|
+
inWord = true;
|
|
182
|
+
}
|
|
183
|
+
if (quote)
|
|
184
|
+
return { words: [], error: `the arguments have a ${quote === "'" ? "single" : "double"} quote that is never closed` };
|
|
185
|
+
if (inWord)
|
|
186
|
+
words.push(word);
|
|
187
|
+
return { words };
|
|
188
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xshieldai/n8n-nodes-kavachos",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "xShieldAI n8n community nodes — DAN gate, kernel enforcement, budget check, and audit log for AI agents.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "Capt. Anil Sharma <capt.anil.sharma@powerpbox.org>",
|