@seanmozeik/tripwire 0.6.7 → 0.7.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/LICENSE +21 -0
- package/README.md +152 -141
- package/dist/tripwire +0 -0
- package/dist/tripwire-pi.js +4 -0
- package/package.json +50 -22
- package/scripts/tripwire-cli +12 -0
- package/src/cli.ts +64 -57
- package/src/dispatch.ts +322 -114
- package/src/index.ts +1 -1
- package/src/lib/bash.ts +106 -62
- package/src/lib/config.ts +93 -46
- package/src/lib/cursor.ts +336 -0
- package/src/lib/diff.ts +5 -2
- package/src/lib/event.ts +20 -21
- package/src/lib/install.ts +508 -136
- package/src/lib/log.ts +2 -3
- package/src/lib/secrets.ts +151 -88
- package/src/main.ts +31 -0
- package/src/pi-extension.ts +337 -0
- package/src/rules/bash-deny.ts +13 -3
- package/src/rules/bash-git.ts +32 -45
- package/src/rules/bash-network-install.ts +6 -3
- package/src/rules/bash-redirect.ts +5 -5
- package/src/rules/bash-scoped-rm.ts +1 -1
- package/src/rules/bash-tar-explosion.ts +16 -15
- package/src/rules/config-custom.ts +21 -15
- package/src/rules/lazy-code.ts +1 -1
- package/src/rules/path-protect.ts +73 -10
- package/src/rules/post-secret-scrub.ts +17 -6
- package/src/rules/read-protect.ts +5 -15
- package/src/rules/tool-policy.ts +54 -0
- package/dist/tripwire-cli.js +0 -11
- package/dist/tripwire-cli.js.jsc +0 -0
- package/dist/tripwire.js +0 -96
- package/dist/tripwire.js.jsc +0 -0
- package/src/rules/bash-tool-policy.ts +0 -146
package/src/cli.ts
CHANGED
|
@@ -1,49 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// Bun src/
|
|
10
|
-
// Bun src/
|
|
11
|
-
// Bun src/
|
|
12
|
-
// Bun src/cli.ts install claude
|
|
13
|
-
// Bun src/cli.ts install codex
|
|
14
|
-
// Bun src/cli.ts install pi
|
|
15
|
-
// Bun src/cli.ts install all
|
|
16
|
-
|
|
17
|
-
// oxlint-disable-next-line unicorn/import-style
|
|
18
|
-
import { dirname } from 'node:path';
|
|
2
|
+
// CLI commands for testing policy and installing agent integrations.
|
|
3
|
+
// Usage examples:
|
|
4
|
+
// Bun src/main.ts test 'rm -rf /etc'
|
|
5
|
+
// Bun src/main.ts test --tool=Read --path=.env
|
|
6
|
+
// Bun src/main.ts test --post --tool=Bash --stdout='ghp_<token>'
|
|
7
|
+
// Bun src/main.ts install claude
|
|
8
|
+
// Bun src/main.ts install codex
|
|
9
|
+
// Bun src/main.ts install cursor
|
|
10
|
+
// Bun src/main.ts install pi
|
|
11
|
+
// Bun src/main.ts install all
|
|
19
12
|
|
|
20
13
|
import { BunServices } from '@effect/platform-bun';
|
|
21
|
-
import { file } from 'bun';
|
|
22
14
|
import { Effect, Option } from 'effect';
|
|
23
15
|
import { Argument, Command, Flag } from 'effect/unstable/cli';
|
|
24
16
|
|
|
25
17
|
import pkg from '../package.json' with { type: 'json' };
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
18
|
+
import {
|
|
19
|
+
installAll,
|
|
20
|
+
installClaude,
|
|
21
|
+
installCodex,
|
|
22
|
+
installCursor,
|
|
23
|
+
installOhMyPi,
|
|
24
|
+
installPi,
|
|
25
|
+
} from './lib/install';
|
|
34
26
|
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const installedPath = `${cliDir}/tripwire-hook`;
|
|
40
|
-
try {
|
|
41
|
-
await file(installedPath).text();
|
|
42
|
-
return installedPath;
|
|
43
|
-
} catch {
|
|
44
|
-
// Fallback to development scenario: tripwire-hook in ../dist relative to CLI
|
|
45
|
-
return `${cliDir}/tripwire.js`;
|
|
27
|
+
const hookCommand = (): string[] => {
|
|
28
|
+
const isBunCli = /\/bun(?<ext>\.exe)?$/.test(process.execPath);
|
|
29
|
+
if (isBunCli) {
|
|
30
|
+
return [process.execPath, new URL('main.ts', import.meta.url).pathname, '--tripwire-hook'];
|
|
46
31
|
}
|
|
32
|
+
return [process.execPath, '--tripwire-hook'];
|
|
47
33
|
};
|
|
48
34
|
|
|
49
35
|
interface BuiltEvent {
|
|
@@ -60,7 +46,7 @@ const buildToolInput = (
|
|
|
60
46
|
command: string | undefined,
|
|
61
47
|
path: string | undefined,
|
|
62
48
|
content: string | undefined,
|
|
63
|
-
)
|
|
49
|
+
) => {
|
|
64
50
|
if (tool === 'Bash') {
|
|
65
51
|
return { command: command ?? '' };
|
|
66
52
|
}
|
|
@@ -73,7 +59,7 @@ const buildToolInput = (
|
|
|
73
59
|
if (tool === 'Edit' || tool === 'MultiEdit') {
|
|
74
60
|
return { file_path: path ?? '', old_string: '', new_string: content ?? '' };
|
|
75
61
|
}
|
|
76
|
-
return
|
|
62
|
+
return null;
|
|
77
63
|
};
|
|
78
64
|
|
|
79
65
|
interface EventParams {
|
|
@@ -89,12 +75,13 @@ interface EventParams {
|
|
|
89
75
|
const buildEvent = (params: EventParams): BuiltEvent => {
|
|
90
76
|
const { tool, post, command, path, stdout, stderr, content } = params;
|
|
91
77
|
const eventName = post ? 'PostToolUse' : 'PreToolUse';
|
|
78
|
+
const toolInput = buildToolInput(tool, command, path, content);
|
|
92
79
|
const event: BuiltEvent = {
|
|
93
80
|
hook_event_name: eventName,
|
|
94
81
|
tool_name: tool,
|
|
95
82
|
cwd: process.cwd(),
|
|
96
83
|
session_id: 'tripwire-cli-test',
|
|
97
|
-
tool_input:
|
|
84
|
+
...(toolInput !== null && { tool_input: toolInput }),
|
|
98
85
|
};
|
|
99
86
|
if (post) {
|
|
100
87
|
event.tool_response =
|
|
@@ -103,6 +90,14 @@ const buildEvent = (params: EventParams): BuiltEvent => {
|
|
|
103
90
|
return event;
|
|
104
91
|
};
|
|
105
92
|
|
|
93
|
+
const prettyJson = (output: string): string => {
|
|
94
|
+
try {
|
|
95
|
+
return JSON.stringify(JSON.parse(output) as unknown, null, 2);
|
|
96
|
+
} catch {
|
|
97
|
+
return output;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
106
101
|
const runTest = (config: {
|
|
107
102
|
readonly command: string | undefined;
|
|
108
103
|
readonly content: string | undefined;
|
|
@@ -112,11 +107,10 @@ const runTest = (config: {
|
|
|
112
107
|
readonly stdout: string | undefined;
|
|
113
108
|
readonly tool: string;
|
|
114
109
|
}): Effect.Effect<void> =>
|
|
115
|
-
Effect.
|
|
110
|
+
Effect.sync(() => {
|
|
116
111
|
const { command, content, path, post, stderr, stdout, tool } = config;
|
|
117
112
|
const event = buildEvent({ tool, post, command, path, stdout, stderr, content });
|
|
118
|
-
const
|
|
119
|
-
const result = Bun.spawnSync([bin], {
|
|
113
|
+
const result = Bun.spawnSync(hookCommand(), {
|
|
120
114
|
stdin: new TextEncoder().encode(JSON.stringify(event)),
|
|
121
115
|
timeout: 10_000,
|
|
122
116
|
stdout: 'pipe',
|
|
@@ -128,12 +122,7 @@ const runTest = (config: {
|
|
|
128
122
|
process.exit(1);
|
|
129
123
|
}
|
|
130
124
|
const output = new TextDecoder().decode(result.stdout);
|
|
131
|
-
|
|
132
|
-
const parsed = JSON.parse(output) as unknown;
|
|
133
|
-
console.log(JSON.stringify(parsed, null, 2));
|
|
134
|
-
} catch {
|
|
135
|
-
console.log(output);
|
|
136
|
-
}
|
|
125
|
+
console.log(prettyJson(output));
|
|
137
126
|
});
|
|
138
127
|
|
|
139
128
|
const testCommand = Command.make(
|
|
@@ -151,7 +140,10 @@ const testCommand = Command.make(
|
|
|
151
140
|
Flag.optional,
|
|
152
141
|
Flag.withDescription('File path for Read/Write/Edit tools'),
|
|
153
142
|
),
|
|
154
|
-
post: Flag.boolean('post').pipe(
|
|
143
|
+
post: Flag.boolean('post').pipe(
|
|
144
|
+
Flag.withDefault(false),
|
|
145
|
+
Flag.withDescription('Test PostToolUse instead of PreToolUse'),
|
|
146
|
+
),
|
|
155
147
|
stderr: Flag.string('stderr').pipe(
|
|
156
148
|
Flag.optional,
|
|
157
149
|
Flag.withDescription('Stderr for PostToolUse Bash'),
|
|
@@ -178,10 +170,10 @@ const testCommand = Command.make(
|
|
|
178
170
|
).pipe(Command.withDescription('Test a synthetic hook event'));
|
|
179
171
|
|
|
180
172
|
const runInstall = (target: string): Effect.Effect<void> =>
|
|
181
|
-
Effect.gen(function* () {
|
|
182
|
-
if (!['claude', 'codex', 'pi', 'all'].includes(target)) {
|
|
173
|
+
Effect.gen(function* runInstallEffect() {
|
|
174
|
+
if (!['claude', 'codex', 'cursor', 'pi', 'oh-my-pi', 'omp', 'all'].includes(target)) {
|
|
183
175
|
console.error(`error: unknown target "${target}"`);
|
|
184
|
-
console.error('Valid targets: claude, codex, pi, all');
|
|
176
|
+
console.error('Valid targets: claude, codex, cursor, pi, oh-my-pi, all');
|
|
185
177
|
process.exit(1);
|
|
186
178
|
}
|
|
187
179
|
|
|
@@ -206,6 +198,17 @@ const runInstall = (target: string): Effect.Effect<void> =>
|
|
|
206
198
|
results = [{ target: 'pi', result }];
|
|
207
199
|
break;
|
|
208
200
|
}
|
|
201
|
+
case 'oh-my-pi':
|
|
202
|
+
case 'omp': {
|
|
203
|
+
const result = yield* Effect.promise(() => installOhMyPi());
|
|
204
|
+
results = [{ target: 'oh-my-pi', result }];
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 'cursor': {
|
|
208
|
+
const result = yield* Effect.promise(() => installCursor());
|
|
209
|
+
results = [{ target: 'cursor', result }];
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
209
212
|
case 'all': {
|
|
210
213
|
const installResults = yield* Effect.promise(() => installAll());
|
|
211
214
|
results = installResults.map((r) => ({ target: r.target, result: r }));
|
|
@@ -237,7 +240,7 @@ const installCommand = Command.make(
|
|
|
237
240
|
'install',
|
|
238
241
|
{
|
|
239
242
|
target: Argument.string('target').pipe(
|
|
240
|
-
Argument.withDescription('Target agent (claude, codex, pi, or all)'),
|
|
243
|
+
Argument.withDescription('Target agent (claude, codex, cursor, pi, oh-my-pi, or all)'),
|
|
241
244
|
),
|
|
242
245
|
},
|
|
243
246
|
({ target }) => runInstall(target),
|
|
@@ -250,7 +253,7 @@ const app = Command.make('tripwire').pipe(
|
|
|
250
253
|
|
|
251
254
|
const program = Command.run(app, { version: pkg.version });
|
|
252
255
|
|
|
253
|
-
const
|
|
256
|
+
const runCli = async (): Promise<void> => {
|
|
254
257
|
try {
|
|
255
258
|
await Effect.runPromise(program.pipe(Effect.provide(BunServices.layer)));
|
|
256
259
|
} catch (error) {
|
|
@@ -260,5 +263,9 @@ const main = async (): Promise<void> => {
|
|
|
260
263
|
}
|
|
261
264
|
};
|
|
262
265
|
|
|
263
|
-
|
|
264
|
-
void
|
|
266
|
+
if (import.meta.main) {
|
|
267
|
+
// oxlint-disable-next-line no-void -- runCli reports failures through process.exitCode.
|
|
268
|
+
void runCli();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { runCli };
|