my-pi 0.0.11 → 0.0.13
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 +1 -1
- package/dist/{api-L4-Ei2xx.js → api-CWEizv2k.js} +2058 -2046
- package/dist/api-CWEizv2k.js.map +1 -0
- package/dist/api.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/extensions/filter-output.test.ts +133 -18
- package/src/extensions/filter-output.ts +14 -6
- package/src/extensions/{otel.test.ts → telemetry.test.ts} +1 -1
- package/dist/api-L4-Ei2xx.js.map +0 -1
- /package/src/extensions/{otel.ts → telemetry.ts} +0 -0
package/dist/api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as create_my_pi, r as runPrintMode, t as InteractiveMode } from "./api-
|
|
1
|
+
import { n as create_my_pi, r as runPrintMode, t as InteractiveMode } from "./api-CWEizv2k.js";
|
|
2
2
|
export { InteractiveMode, create_my_pi, runPrintMode };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as create_my_pi } from "./api-
|
|
2
|
+
import { n as create_my_pi } from "./api-CWEizv2k.js";
|
|
3
3
|
import { InteractiveMode, runPrintMode } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { defineCommand, runMain } from "citty";
|
|
5
5
|
import { readFileSync } from "node:fs";
|
package/package.json
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
|
-
// Extract redact function for direct testing
|
|
4
|
-
// (mirrors the logic in filter-output.ts)
|
|
5
3
|
interface SecretPattern {
|
|
6
4
|
name: string;
|
|
7
5
|
pattern: RegExp;
|
|
@@ -9,10 +7,11 @@ interface SecretPattern {
|
|
|
9
7
|
|
|
10
8
|
const SECRET_PATTERNS: SecretPattern[] = [
|
|
11
9
|
{ name: 'AWS Access Key', pattern: /AKIA[A-Z0-9]{16}/g },
|
|
10
|
+
{ name: 'AWS Temp Access Key', pattern: /ASIA[A-Z0-9]{16}/g },
|
|
12
11
|
{
|
|
13
12
|
name: 'AWS Secret Key',
|
|
14
13
|
pattern:
|
|
15
|
-
|
|
14
|
+
/\b(?:AWS_SECRET_ACCESS_KEY|aws_secret_access_key|secret_access_key|SecretAccessKey)\b\s*[:=]\s*["']?[A-Za-z0-9/+=]{40,}["']?/g,
|
|
16
15
|
},
|
|
17
16
|
{
|
|
18
17
|
name: 'Bearer Token',
|
|
@@ -30,18 +29,42 @@ const SECRET_PATTERNS: SecretPattern[] = [
|
|
|
30
29
|
name: 'Stripe Test Key',
|
|
31
30
|
pattern: /sk_test_[a-zA-Z0-9]{20,}/g,
|
|
32
31
|
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Hetzner Token',
|
|
34
|
+
pattern:
|
|
35
|
+
/(?:HCLOUD_TOKEN|hcloud_token|token)\s*[:=]\s*["']?[a-f0-9]{64}\b/g,
|
|
36
|
+
},
|
|
33
37
|
{
|
|
34
38
|
name: 'Private Key',
|
|
35
|
-
pattern:
|
|
39
|
+
pattern:
|
|
40
|
+
/-----BEGIN\s+[\w\s]*PRIVATE\s+KEY-----[\s\S]*?-----END\s+[\w\s]*PRIVATE\s+KEY-----/g,
|
|
36
41
|
},
|
|
37
42
|
{
|
|
38
43
|
name: 'Connection String with Password',
|
|
39
44
|
pattern: /:\/\/[^:]+:[^@]+@/g,
|
|
40
45
|
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Generic Password Field',
|
|
48
|
+
pattern:
|
|
49
|
+
/\b[\w-]*(?:password|passwd|secret|token|api[_-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9._:/+=@!-]{8,}/gi,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Generic Secret Phrase',
|
|
53
|
+
pattern:
|
|
54
|
+
/\b(?:password|passwd|secret|token|api[_-]?key)\b(?:\s+(?:is|was|seen|value|header))?\s*[:=]?\s+[A-Za-z0-9._:/+=@!-]{8,}/gi,
|
|
55
|
+
},
|
|
41
56
|
{
|
|
42
57
|
name: 'Tavily API Key',
|
|
43
58
|
pattern: /tvly-[a-zA-Z0-9_-]{20,}/g,
|
|
44
59
|
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Kagi API Key',
|
|
62
|
+
pattern: /[a-zA-Z0-9_-]{40,}\.[a-zA-Z0-9_-]{40,}/g,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Brave API Key',
|
|
66
|
+
pattern: /BSA[A-Z0-9]{20,}/g,
|
|
67
|
+
},
|
|
45
68
|
{
|
|
46
69
|
name: 'Firecrawl API Key',
|
|
47
70
|
pattern: /fc-[a-f0-9]{32}/g,
|
|
@@ -50,6 +73,10 @@ const SECRET_PATTERNS: SecretPattern[] = [
|
|
|
50
73
|
name: 'GitHub Token',
|
|
51
74
|
pattern: /gh[pousr]_[a-zA-Z0-9]{36,}/g,
|
|
52
75
|
},
|
|
76
|
+
{
|
|
77
|
+
name: 'GitHub Fine-grained PAT',
|
|
78
|
+
pattern: /github_pat_[a-zA-Z0-9_]{20,}/g,
|
|
79
|
+
},
|
|
53
80
|
];
|
|
54
81
|
|
|
55
82
|
function redact(text: string): { redacted: string; count: number } {
|
|
@@ -70,16 +97,46 @@ function redact(text: string): { redacted: string; count: number } {
|
|
|
70
97
|
|
|
71
98
|
describe('redact', () => {
|
|
72
99
|
it('redacts AWS access keys', () => {
|
|
73
|
-
const input = 'key:
|
|
100
|
+
const input = 'key: AKIA1234567890CANARY';
|
|
74
101
|
const { redacted, count } = redact(input);
|
|
75
102
|
expect(count).toBe(1);
|
|
76
103
|
expect(redacted).toContain('[REDACTED:AWS Access Key]');
|
|
77
|
-
expect(redacted).not.toContain('
|
|
104
|
+
expect(redacted).not.toContain('AKIA1234567890CANARY');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('redacts AWS temp access keys', () => {
|
|
108
|
+
const input = 'key: ASIA1234567890CANARY';
|
|
109
|
+
const { redacted, count } = redact(input);
|
|
110
|
+
expect(count).toBe(1);
|
|
111
|
+
expect(redacted).toContain('[REDACTED:AWS Temp Access Key]');
|
|
112
|
+
expect(redacted).not.toContain('ASIA1234567890CANARY');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('redacts uppercase AWS secret env vars', () => {
|
|
116
|
+
const input =
|
|
117
|
+
'AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG+bPxRfiCYCANARYKEY01';
|
|
118
|
+
const { redacted, count } = redact(input);
|
|
119
|
+
expect(count).toBe(1);
|
|
120
|
+
expect(redacted).toContain('[REDACTED:AWS Secret Key]');
|
|
121
|
+
expect(redacted).not.toContain(
|
|
122
|
+
'wJalrXUtnFEMI/K7MDENG+bPxRfiCYCANARYKEY01',
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('redacts lower-case secret_access_key assignments', () => {
|
|
127
|
+
const input =
|
|
128
|
+
'secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYCANARYKEY01"';
|
|
129
|
+
const { redacted, count } = redact(input);
|
|
130
|
+
expect(count).toBe(1);
|
|
131
|
+
expect(redacted).toContain('[REDACTED:AWS Secret Key]');
|
|
132
|
+
expect(redacted).not.toContain(
|
|
133
|
+
'wJalrXUtnFEMI/K7MDENG+bPxRfiCYCANARYKEY01',
|
|
134
|
+
);
|
|
78
135
|
});
|
|
79
136
|
|
|
80
137
|
it('redacts bearer tokens', () => {
|
|
81
138
|
const input =
|
|
82
|
-
'Authorization: Bearer
|
|
139
|
+
'Authorization: Bearer canaryBearerTokenValueAlphaNum1234567890ZZ';
|
|
83
140
|
const { redacted, count } = redact(input);
|
|
84
141
|
expect(count).toBe(1);
|
|
85
142
|
expect(redacted).toContain('[REDACTED:Bearer Token]');
|
|
@@ -87,10 +144,13 @@ describe('redact', () => {
|
|
|
87
144
|
|
|
88
145
|
it('redacts OpenAI/Anthropic API keys', () => {
|
|
89
146
|
const input =
|
|
90
|
-
'ANTHROPIC_API_KEY=sk-ant-
|
|
147
|
+
'ANTHROPIC_API_KEY=sk-ant-api-key-example-123456789012345';
|
|
91
148
|
const { redacted, count } = redact(input);
|
|
92
149
|
expect(count).toBe(1);
|
|
93
150
|
expect(redacted).toContain('[REDACTED:OpenAI/Anthropic API Key]');
|
|
151
|
+
expect(redacted).not.toContain(
|
|
152
|
+
'[REDACTED:Generic Password Field]',
|
|
153
|
+
);
|
|
94
154
|
});
|
|
95
155
|
|
|
96
156
|
it('redacts Stripe live keys', () => {
|
|
@@ -100,15 +160,26 @@ describe('redact', () => {
|
|
|
100
160
|
expect(redacted).toContain('[REDACTED:Stripe Live Key]');
|
|
101
161
|
});
|
|
102
162
|
|
|
103
|
-
it('redacts
|
|
104
|
-
const input = '
|
|
163
|
+
it('redacts Hetzner tokens', () => {
|
|
164
|
+
const input = `HCLOUD_TOKEN=${'a'.repeat(64)}`;
|
|
165
|
+
const { redacted, count } = redact(input);
|
|
166
|
+
expect(count).toBe(1);
|
|
167
|
+
expect(redacted).toContain('[REDACTED:Hetzner Token]');
|
|
168
|
+
expect(redacted).not.toContain('a'.repeat(64));
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('redacts full private key blocks', () => {
|
|
172
|
+
const input = `-----BEGIN PRIVATE KEY-----\nQ0FOQVJZX1BSSVZBVEVfS0VZX0JMT0NLX0xJTkVfMDAx\nQ0FOQVJZX1BSSVZBVEVfS0VZX0JMT0NLX0xJTkVfMDAy\n-----END PRIVATE KEY-----`;
|
|
105
173
|
const { redacted, count } = redact(input);
|
|
106
174
|
expect(count).toBe(1);
|
|
107
175
|
expect(redacted).toContain('[REDACTED:Private Key]');
|
|
176
|
+
expect(redacted).not.toContain(
|
|
177
|
+
'Q0FOQVJZX1BSSVZBVEVfS0VZX0JMT0NLX0xJTkVfMDAx',
|
|
178
|
+
);
|
|
108
179
|
});
|
|
109
180
|
|
|
110
181
|
it('redacts connection strings with passwords', () => {
|
|
111
|
-
const input = 'postgres://user:
|
|
182
|
+
const input = 'postgres://user:supersecretpass@localhost:5432/db';
|
|
112
183
|
const { redacted, count } = redact(input);
|
|
113
184
|
expect(count).toBe(1);
|
|
114
185
|
expect(redacted).toContain(
|
|
@@ -116,34 +187,78 @@ describe('redact', () => {
|
|
|
116
187
|
);
|
|
117
188
|
});
|
|
118
189
|
|
|
119
|
-
it('redacts
|
|
190
|
+
it('redacts prefixed generic secret fields', () => {
|
|
191
|
+
const input =
|
|
192
|
+
'OPAQUE_SECRET=cnyr_ZmFrZVNlY3JldFZhbHVlX1JlZGFjdGlvbl9TdWl0ZV8wMDE';
|
|
193
|
+
const { redacted, count } = redact(input);
|
|
194
|
+
expect(count).toBe(1);
|
|
195
|
+
expect(redacted).toContain('[REDACTED:Generic Password Field]');
|
|
196
|
+
expect(redacted).not.toContain(
|
|
197
|
+
'cnyr_ZmFrZVNlY3JldFZhbHVlX1JlZGFjdGlvbl9TdWl0ZV8wMDE',
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('redacts freeform secret phrases in logs', () => {
|
|
120
202
|
const input =
|
|
121
|
-
'
|
|
203
|
+
'2026-04-19T09:00:02Z INFO opaque fallback secret cnyr_ZmFrZVNlY3JldFZhbHVlX1JlZGFjdGlvbl9TdWl0ZV8wMDE';
|
|
204
|
+
const { redacted, count } = redact(input);
|
|
205
|
+
expect(count).toBe(1);
|
|
206
|
+
expect(redacted).toContain('[REDACTED:Generic Secret Phrase]');
|
|
207
|
+
expect(redacted).not.toContain(
|
|
208
|
+
'cnyr_ZmFrZVNlY3JldFZhbHVlX1JlZGFjdGlvbl9TdWl0ZV8wMDE',
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('redacts Tavily API keys', () => {
|
|
213
|
+
const input = 'tvly-canary-redaction-suite-000000000000000001';
|
|
122
214
|
const { redacted, count } = redact(input);
|
|
123
215
|
expect(count).toBe(1);
|
|
124
216
|
expect(redacted).toContain('[REDACTED:Tavily API Key]');
|
|
125
217
|
});
|
|
126
218
|
|
|
219
|
+
it('redacts Kagi API keys', () => {
|
|
220
|
+
const input = `${'a'.repeat(40)}.${'b'.repeat(40)}`;
|
|
221
|
+
const { redacted, count } = redact(input);
|
|
222
|
+
expect(count).toBe(1);
|
|
223
|
+
expect(redacted).toContain('[REDACTED:Kagi API Key]');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('redacts Brave API keys', () => {
|
|
227
|
+
const input = 'BSA' + 'A'.repeat(20);
|
|
228
|
+
const { redacted, count } = redact(input);
|
|
229
|
+
expect(count).toBe(1);
|
|
230
|
+
expect(redacted).toContain('[REDACTED:Brave API Key]');
|
|
231
|
+
});
|
|
232
|
+
|
|
127
233
|
it('redacts Firecrawl API keys', () => {
|
|
128
|
-
const input = 'fc-
|
|
234
|
+
const input = 'fc-e3b0c44298fc1c149afbf4c8996fb924';
|
|
129
235
|
const { redacted, count } = redact(input);
|
|
130
236
|
expect(count).toBe(1);
|
|
131
237
|
expect(redacted).toContain('[REDACTED:Firecrawl API Key]');
|
|
132
238
|
});
|
|
133
239
|
|
|
134
240
|
it('redacts GitHub tokens', () => {
|
|
135
|
-
const input =
|
|
241
|
+
const input =
|
|
242
|
+
'ghp_CanaryRedactionSuite000000000000000000000001ABCD';
|
|
136
243
|
const { redacted, count } = redact(input);
|
|
137
244
|
expect(count).toBe(1);
|
|
138
245
|
expect(redacted).toContain('[REDACTED:GitHub Token]');
|
|
139
246
|
});
|
|
140
247
|
|
|
248
|
+
it('redacts GitHub fine-grained PATs', () => {
|
|
249
|
+
const input = 'github_pat_' + 'A'.repeat(30);
|
|
250
|
+
const { redacted, count } = redact(input);
|
|
251
|
+
expect(count).toBe(1);
|
|
252
|
+
expect(redacted).toContain('[REDACTED:GitHub Fine-grained PAT]');
|
|
253
|
+
});
|
|
254
|
+
|
|
141
255
|
it('redacts multiple secrets in one string', () => {
|
|
142
256
|
const input =
|
|
143
|
-
'aws:
|
|
257
|
+
'aws: AKIA1234567890CANARY, SERVICE_PASSWORD=CanaryPassword-Redaction-001!';
|
|
144
258
|
const { redacted, count } = redact(input);
|
|
145
259
|
expect(count).toBe(2);
|
|
146
|
-
expect(redacted).not.toContain('
|
|
260
|
+
expect(redacted).not.toContain('AKIA1234567890CANARY');
|
|
261
|
+
expect(redacted).not.toContain('CanaryPassword-Redaction-001!');
|
|
147
262
|
});
|
|
148
263
|
|
|
149
264
|
it('leaves clean text unchanged', () => {
|
|
@@ -154,7 +269,7 @@ describe('redact', () => {
|
|
|
154
269
|
});
|
|
155
270
|
|
|
156
271
|
it('preserves prefix in redacted output', () => {
|
|
157
|
-
const input = '
|
|
272
|
+
const input = 'AKIA1234567890CANARY';
|
|
158
273
|
const { redacted } = redact(input);
|
|
159
274
|
expect(redacted).toMatch(/^AKIA/);
|
|
160
275
|
});
|
|
@@ -10,10 +10,11 @@ interface SecretPattern {
|
|
|
10
10
|
|
|
11
11
|
const SECRET_PATTERNS: SecretPattern[] = [
|
|
12
12
|
{ name: 'AWS Access Key', pattern: /AKIA[A-Z0-9]{16}/g },
|
|
13
|
+
{ name: 'AWS Temp Access Key', pattern: /ASIA[A-Z0-9]{16}/g },
|
|
13
14
|
{
|
|
14
15
|
name: 'AWS Secret Key',
|
|
15
16
|
pattern:
|
|
16
|
-
|
|
17
|
+
/\b(?:AWS_SECRET_ACCESS_KEY|aws_secret_access_key|secret_access_key|SecretAccessKey)\b\s*[:=]\s*["']?[A-Za-z0-9/+=]{40,}["']?/g,
|
|
17
18
|
},
|
|
18
19
|
{
|
|
19
20
|
name: 'Bearer Token',
|
|
@@ -38,7 +39,8 @@ const SECRET_PATTERNS: SecretPattern[] = [
|
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
name: 'Private Key',
|
|
41
|
-
pattern:
|
|
42
|
+
pattern:
|
|
43
|
+
/-----BEGIN\s+[\w\s]*PRIVATE\s+KEY-----[\s\S]*?-----END\s+[\w\s]*PRIVATE\s+KEY-----/g,
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
46
|
name: 'Connection String with Password',
|
|
@@ -47,7 +49,12 @@ const SECRET_PATTERNS: SecretPattern[] = [
|
|
|
47
49
|
{
|
|
48
50
|
name: 'Generic Password Field',
|
|
49
51
|
pattern:
|
|
50
|
-
|
|
52
|
+
/\b[\w-]*(?:password|passwd|secret|token|api[_-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9._:/+=@!-]{8,}/gi,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Generic Secret Phrase',
|
|
56
|
+
pattern:
|
|
57
|
+
/\b(?:password|passwd|secret|token|api[_-]?key)\b(?:\s+(?:is|was|seen|value|header))?\s*[:=]?\s+[A-Za-z0-9._:/+=@!-]{8,}/gi,
|
|
51
58
|
},
|
|
52
59
|
{
|
|
53
60
|
name: 'Tavily API Key',
|
|
@@ -69,6 +76,10 @@ const SECRET_PATTERNS: SecretPattern[] = [
|
|
|
69
76
|
name: 'GitHub Token',
|
|
70
77
|
pattern: /gh[pousr]_[a-zA-Z0-9]{36,}/g,
|
|
71
78
|
},
|
|
79
|
+
{
|
|
80
|
+
name: 'GitHub Fine-grained PAT',
|
|
81
|
+
pattern: /github_pat_[a-zA-Z0-9_]{20,}/g,
|
|
82
|
+
},
|
|
72
83
|
];
|
|
73
84
|
|
|
74
85
|
function redact(text: string): { redacted: string; count: number } {
|
|
@@ -76,7 +87,6 @@ function redact(text: string): { redacted: string; count: number } {
|
|
|
76
87
|
let result = text;
|
|
77
88
|
|
|
78
89
|
for (const sp of SECRET_PATTERNS) {
|
|
79
|
-
// Reset lastIndex for global regexes
|
|
80
90
|
sp.pattern.lastIndex = 0;
|
|
81
91
|
result = result.replace(sp.pattern, (match) => {
|
|
82
92
|
count++;
|
|
@@ -88,11 +98,9 @@ function redact(text: string): { redacted: string; count: number } {
|
|
|
88
98
|
return { redacted: result, count };
|
|
89
99
|
}
|
|
90
100
|
|
|
91
|
-
// Default export for Pi Package / additionalExtensionPaths loading
|
|
92
101
|
export default async function filter_output(pi: ExtensionAPI) {
|
|
93
102
|
let totalRedacted = 0;
|
|
94
103
|
|
|
95
|
-
// Intercept tool results to redact secrets before the LLM sees them
|
|
96
104
|
pi.on('tool_result' as const, async (event: any) => {
|
|
97
105
|
if (!event.content) return;
|
|
98
106
|
|