@wrongstack/plugins 0.289.0 → 0.291.1
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/catalog.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2135 -1985
- package/dist/process-guard/index.d.ts +41 -0
- package/dist/process-guard/index.d.ts.map +1 -0
- package/dist/process-guard.d.ts +2 -0
- package/dist/process-guard.js +150 -0
- package/dist/spec-linker.js +2134 -1985
- package/package.json +10 -6
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* process-guard plugin — prevents the LLM/coding agent from killing active
|
|
3
|
+
* WrongStack processes or their host terminals.
|
|
4
|
+
*
|
|
5
|
+
* Two-layer defense:
|
|
6
|
+
* 1. **Tool-level guards** (primary): `bash-kill-guard.ts` in the bash tool
|
|
7
|
+
* and `exec-kill-guard.ts` in the exec tool intercept kill commands at
|
|
8
|
+
* the tool-execution level, consulting the cross-instance persistent
|
|
9
|
+
* process registry (`~/.wrongstack/process-registry.json`).
|
|
10
|
+
* 2. **This plugin** (observability + configuration layer): provides the
|
|
11
|
+
* `process_guard_status` diagnostic tool, logs all blocked kill attempts,
|
|
12
|
+
* and surfaces protection state to the user via `/process-guard status`.
|
|
13
|
+
*
|
|
14
|
+
* The tool-level guards handle:
|
|
15
|
+
* - taskkill /F /IM node.exe, taskkill /PID X
|
|
16
|
+
* - PowerShell Stop-Process / kill alias
|
|
17
|
+
* - WMIC process ... delete
|
|
18
|
+
* - Script-based kills (kill*.ps1, kill*.sh, etc.)
|
|
19
|
+
* - POSIX kill, pkill, killall
|
|
20
|
+
* - node -e "process.kill(...)" (via exec-kill-guard.ts)
|
|
21
|
+
*
|
|
22
|
+
* Cross-instance protection: the persistent process registry at
|
|
23
|
+
* `~/.wrongstack/process-registry.json` tracks every WrongStack instance's
|
|
24
|
+
* PID, and the tool-level guards read it to determine which PIDs/names are
|
|
25
|
+
* protected. This plugin registers the current process + parent terminal at
|
|
26
|
+
* setup.
|
|
27
|
+
*
|
|
28
|
+
* Config (`config.extensions['process-guard']`):
|
|
29
|
+
* ```jsonc
|
|
30
|
+
* {
|
|
31
|
+
* "enabled": true,
|
|
32
|
+
* "mode": "block" // "block" | "warn" | "off"
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
import type { Plugin } from '@wrongstack/core';
|
|
39
|
+
declare const plugin: Plugin;
|
|
40
|
+
export default plugin;
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/process-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAiD/C,QAAA,MAAM,MAAM,EAAE,MAoKb,CAAC;eAEa,MAAM"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// src/process-guard/index.ts
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
var state = {
|
|
4
|
+
invocations: 0,
|
|
5
|
+
detections: 0,
|
|
6
|
+
warns: 0,
|
|
7
|
+
lastDetection: null,
|
|
8
|
+
hookUnregister: null
|
|
9
|
+
};
|
|
10
|
+
var DEFAULTS = {
|
|
11
|
+
enabled: true,
|
|
12
|
+
mode: "block"
|
|
13
|
+
};
|
|
14
|
+
function readConfig(raw) {
|
|
15
|
+
if (!raw || typeof raw !== "object") return { ...DEFAULTS };
|
|
16
|
+
const r = raw;
|
|
17
|
+
return {
|
|
18
|
+
enabled: r["enabled"] !== false,
|
|
19
|
+
mode: r["mode"] === "warn" ? "warn" : r["mode"] === "off" ? "off" : "block"
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
var plugin = {
|
|
23
|
+
name: "process-guard",
|
|
24
|
+
version: "0.1.0",
|
|
25
|
+
description: "Blocks kill commands (taskkill, Stop-Process, kill, pkill, wmic) that target active WrongStack processes or their host terminals.",
|
|
26
|
+
apiVersion: "^0.1.10",
|
|
27
|
+
capabilities: { tools: true, hooks: true },
|
|
28
|
+
defaultConfig: { ...DEFAULTS },
|
|
29
|
+
configSchema: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
enabled: { type: "boolean", default: true, description: "Master switch." },
|
|
33
|
+
mode: {
|
|
34
|
+
type: "string",
|
|
35
|
+
enum: ["block", "warn", "off"],
|
|
36
|
+
default: "block",
|
|
37
|
+
description: "block = refuse the operation; warn = inject context; off = disable."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
setup(api) {
|
|
42
|
+
state.invocations = 0;
|
|
43
|
+
state.detections = 0;
|
|
44
|
+
state.warns = 0;
|
|
45
|
+
state.lastDetection = null;
|
|
46
|
+
if (state.hookUnregister) {
|
|
47
|
+
try {
|
|
48
|
+
state.hookUnregister();
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
state.hookUnregister = null;
|
|
52
|
+
}
|
|
53
|
+
const cfg = readConfig(api.config.extensions?.["process-guard"]);
|
|
54
|
+
if (cfg.mode === "off") {
|
|
55
|
+
api.log.info("[process-guard] loaded but mode=off \u2014 protection disabled");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const hook = (input) => {
|
|
59
|
+
if (!cfg.enabled || cfg.mode === "off") return;
|
|
60
|
+
state.invocations += 1;
|
|
61
|
+
const toolName = input.toolName ?? "";
|
|
62
|
+
if (toolName !== "bash" && toolName !== "exec") return;
|
|
63
|
+
const ti = input.toolInput ?? {};
|
|
64
|
+
const command = typeof ti["command"] === "string" ? ti["command"] : "";
|
|
65
|
+
if (!command) return;
|
|
66
|
+
const cmdLower = command.toLowerCase();
|
|
67
|
+
const isKillRelated = cmdLower.includes("kill") || cmdLower.includes("taskkill") || cmdLower.includes("stop-process") || cmdLower.includes("tskill") || cmdLower.includes("pkill") || cmdLower.includes("killall") || cmdLower.includes("wmic");
|
|
68
|
+
if (!isKillRelated) return;
|
|
69
|
+
state.detections += 1;
|
|
70
|
+
state.lastDetection = {
|
|
71
|
+
target: command.slice(0, 100),
|
|
72
|
+
tool: toolName,
|
|
73
|
+
when: (/* @__PURE__ */ new Date()).toISOString()
|
|
74
|
+
};
|
|
75
|
+
api.metrics.counter("detections");
|
|
76
|
+
api.log.warn?.(
|
|
77
|
+
"[process-guard] kill-related command detected; tool-level guard will evaluate it",
|
|
78
|
+
{
|
|
79
|
+
tool: toolName,
|
|
80
|
+
command: command.slice(0, 200)
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
state.hookUnregister = api.registerHook("PreToolUse", "bash|exec", hook, {
|
|
85
|
+
name: "process-guard",
|
|
86
|
+
stage: "validate",
|
|
87
|
+
failurePolicy: "closed",
|
|
88
|
+
policy: true
|
|
89
|
+
});
|
|
90
|
+
api.tools.register({
|
|
91
|
+
name: "process_guard_status",
|
|
92
|
+
description: "Reports process-guard state: mode, counters, and last detected kill-related command.",
|
|
93
|
+
inputSchema: { type: "object", properties: {} },
|
|
94
|
+
permission: "auto",
|
|
95
|
+
category: "Diagnostics",
|
|
96
|
+
mutating: false,
|
|
97
|
+
async execute() {
|
|
98
|
+
return {
|
|
99
|
+
ok: true,
|
|
100
|
+
enabled: cfg.enabled,
|
|
101
|
+
mode: cfg.mode,
|
|
102
|
+
platform: os.platform(),
|
|
103
|
+
selfPid: process.pid,
|
|
104
|
+
parentPid: process.ppid,
|
|
105
|
+
counters: {
|
|
106
|
+
invocations: state.invocations,
|
|
107
|
+
detections: state.detections,
|
|
108
|
+
warns: state.warns
|
|
109
|
+
},
|
|
110
|
+
lastDetection: state.lastDetection
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
api.log.info("[process-guard] loaded", {
|
|
115
|
+
version: "0.1.0",
|
|
116
|
+
enabled: cfg.enabled,
|
|
117
|
+
mode: cfg.mode,
|
|
118
|
+
selfPid: process.pid,
|
|
119
|
+
parentPid: process.ppid
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
teardown(api) {
|
|
123
|
+
if (state.hookUnregister) {
|
|
124
|
+
try {
|
|
125
|
+
state.hookUnregister();
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
state.hookUnregister = null;
|
|
129
|
+
}
|
|
130
|
+
const final = {
|
|
131
|
+
invocations: state.invocations,
|
|
132
|
+
detections: state.detections,
|
|
133
|
+
warns: state.warns
|
|
134
|
+
};
|
|
135
|
+
state.invocations = 0;
|
|
136
|
+
state.detections = 0;
|
|
137
|
+
state.warns = 0;
|
|
138
|
+
api.log.info("[process-guard] teardown complete", { final });
|
|
139
|
+
},
|
|
140
|
+
async health() {
|
|
141
|
+
return {
|
|
142
|
+
ok: true,
|
|
143
|
+
message: state.lastDetection === null ? `process-guard: ${state.invocations} invocation(s), ${state.detections} detection(s), ${state.warns} warn(s)` : `process-guard: last detection on "${state.lastDetection.tool}" at ${state.lastDetection.when}`
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var process_guard_default = plugin;
|
|
148
|
+
export {
|
|
149
|
+
process_guard_default as default
|
|
150
|
+
};
|