@amaster.ai/pi-shared 0.1.6 → 0.1.8
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/__tests__/network-transport.test.d.ts +2 -0
- package/dist/__tests__/network-transport.test.d.ts.map +1 -0
- package/dist/__tests__/network-transport.test.js +47 -0
- package/dist/__tests__/network-transport.test.js.map +1 -0
- package/dist/__tests__/network.test.d.ts +2 -0
- package/dist/__tests__/network.test.d.ts.map +1 -0
- package/dist/__tests__/network.test.js +76 -0
- package/dist/__tests__/network.test.js.map +1 -0
- package/dist/__tests__/settings.test.js +98 -5
- package/dist/__tests__/settings.test.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/network.d.ts +11 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +219 -0
- package/dist/network.js.map +1 -0
- package/dist/settings.d.ts +12 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +46 -12
- package/dist/settings.js.map +1 -1
- package/dist/threat-patterns.d.ts +34 -0
- package/dist/threat-patterns.d.ts.map +1 -0
- package/dist/threat-patterns.js +211 -0
- package/dist/threat-patterns.js.map +1 -0
- package/package.json +4 -4
- package/dist/__tests__/scheduler.test.d.ts +0 -2
- package/dist/__tests__/scheduler.test.d.ts.map +0 -1
- package/dist/__tests__/scheduler.test.js +0 -219
- package/dist/__tests__/scheduler.test.js.map +0 -1
- package/dist/scheduler.d.ts +0 -14
- package/dist/scheduler.d.ts.map +0 -1
- package/dist/scheduler.js +0 -235
- package/dist/scheduler.js.map +0 -1
package/dist/network.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { lookup as nodeLookup } from 'node:dns/promises';
|
|
2
|
+
import { request as httpRequest } from 'node:http';
|
|
3
|
+
import { request as httpsRequest } from 'node:https';
|
|
4
|
+
import { BlockList, isIP } from 'node:net';
|
|
5
|
+
import { Readable } from 'node:stream';
|
|
6
|
+
const defaultLookup = async (hostname) => nodeLookup(hostname, { all: true, verbatim: true });
|
|
7
|
+
const wellKnownNat64Addresses = new BlockList();
|
|
8
|
+
wellKnownNat64Addresses.addSubnet('64:ff9b::', 96, 'ipv6');
|
|
9
|
+
const globalIpv6Addresses = new BlockList();
|
|
10
|
+
globalIpv6Addresses.addSubnet('2000::', 3, 'ipv6');
|
|
11
|
+
const nonGlobalIpv6Addresses = new BlockList();
|
|
12
|
+
nonGlobalIpv6Addresses.addSubnet('2001::', 23, 'ipv6');
|
|
13
|
+
nonGlobalIpv6Addresses.addSubnet('2001:db8::', 32, 'ipv6');
|
|
14
|
+
nonGlobalIpv6Addresses.addSubnet('2002::', 16, 'ipv6');
|
|
15
|
+
nonGlobalIpv6Addresses.addSubnet('3fff::', 20, 'ipv6');
|
|
16
|
+
// IANA marks 2001::/23 non-global except for these more-specific allocations.
|
|
17
|
+
const globalIetfProtocolAddresses = new BlockList();
|
|
18
|
+
globalIetfProtocolAddresses.addAddress('2001:1::1', 'ipv6');
|
|
19
|
+
globalIetfProtocolAddresses.addAddress('2001:1::2', 'ipv6');
|
|
20
|
+
globalIetfProtocolAddresses.addAddress('2001:1::3', 'ipv6');
|
|
21
|
+
globalIetfProtocolAddresses.addSubnet('2001:3::', 32, 'ipv6');
|
|
22
|
+
globalIetfProtocolAddresses.addSubnet('2001:4:112::', 48, 'ipv6');
|
|
23
|
+
globalIetfProtocolAddresses.addSubnet('2001:20::', 28, 'ipv6');
|
|
24
|
+
globalIetfProtocolAddresses.addSubnet('2001:30::', 28, 'ipv6');
|
|
25
|
+
function isPublicIpv4(address) {
|
|
26
|
+
const octets = address.split('.').map(Number);
|
|
27
|
+
if (octets.length !== 4 || octets.some((part) => !Number.isInteger(part)))
|
|
28
|
+
return false;
|
|
29
|
+
const [a, b, c] = octets;
|
|
30
|
+
if (a === 0 ||
|
|
31
|
+
a === 10 ||
|
|
32
|
+
a === 127 ||
|
|
33
|
+
a >= 224 ||
|
|
34
|
+
(a === 100 && b >= 64 && b <= 127) ||
|
|
35
|
+
(a === 169 && b === 254) ||
|
|
36
|
+
(a === 172 && b >= 16 && b <= 31) ||
|
|
37
|
+
(a === 192 && b === 168) ||
|
|
38
|
+
(a === 192 && b === 0 && c === 0) ||
|
|
39
|
+
(a === 192 && b === 0 && c === 2) ||
|
|
40
|
+
(a === 192 && b === 88 && c === 99) ||
|
|
41
|
+
(a === 198 && (b === 18 || b === 19 || (b === 51 && c === 100))) ||
|
|
42
|
+
(a === 203 && b === 0 && c === 113)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function mappedIpv4(address) {
|
|
48
|
+
const dotted = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(address);
|
|
49
|
+
if (dotted)
|
|
50
|
+
return dotted[1];
|
|
51
|
+
const hex = /^(?:::ffff:|(?:0{1,4}:){5}ffff:)([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(address);
|
|
52
|
+
if (!hex)
|
|
53
|
+
return undefined;
|
|
54
|
+
const value = Number.parseInt(hex[1], 16) * 65_536 + Number.parseInt(hex[2], 16);
|
|
55
|
+
return [value >>> 24, (value >>> 16) & 255, (value >>> 8) & 255, value & 255].join('.');
|
|
56
|
+
}
|
|
57
|
+
function nat64Ipv4(address) {
|
|
58
|
+
if (!wellKnownNat64Addresses.check(address, 'ipv6'))
|
|
59
|
+
return undefined;
|
|
60
|
+
let canonical;
|
|
61
|
+
try {
|
|
62
|
+
canonical = new URL(`http://[${address}]/`).hostname.slice(1, -1);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const hex = /^64:ff9b::([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(canonical);
|
|
68
|
+
if (!hex)
|
|
69
|
+
return undefined;
|
|
70
|
+
const value = Number.parseInt(hex[1], 16) * 65_536 + Number.parseInt(hex[2], 16);
|
|
71
|
+
return [value >>> 24, (value >>> 16) & 255, (value >>> 8) & 255, value & 255].join('.');
|
|
72
|
+
}
|
|
73
|
+
function isPublicIp(address) {
|
|
74
|
+
const normalized = address.toLowerCase().replace(/^\[|\]$/g, '');
|
|
75
|
+
const family = isIP(normalized);
|
|
76
|
+
if (family === 4)
|
|
77
|
+
return isPublicIpv4(normalized);
|
|
78
|
+
if (family !== 6)
|
|
79
|
+
return false;
|
|
80
|
+
const embeddedIpv4 = mappedIpv4(normalized) ?? nat64Ipv4(normalized);
|
|
81
|
+
if (embeddedIpv4)
|
|
82
|
+
return isPublicIpv4(embeddedIpv4);
|
|
83
|
+
if (!globalIpv6Addresses.check(normalized, 'ipv6'))
|
|
84
|
+
return false;
|
|
85
|
+
return (globalIetfProtocolAddresses.check(normalized, 'ipv6') ||
|
|
86
|
+
!nonGlobalIpv6Addresses.check(normalized, 'ipv6'));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse an outbound URL once using Node's WHATWG parser and require every DNS
|
|
90
|
+
* answer to be globally routable. Call this again for every redirect target.
|
|
91
|
+
*/
|
|
92
|
+
async function resolvePublicHttpUrl(value, lookup = defaultLookup) {
|
|
93
|
+
let url;
|
|
94
|
+
try {
|
|
95
|
+
url = value instanceof URL ? new URL(value) : new URL(value);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new Error('Outbound URL must use a public HTTP(S) destination.');
|
|
99
|
+
}
|
|
100
|
+
if ((url.protocol !== 'http:' && url.protocol !== 'https:') ||
|
|
101
|
+
url.username ||
|
|
102
|
+
url.password ||
|
|
103
|
+
!url.hostname ||
|
|
104
|
+
url.hostname === 'localhost' ||
|
|
105
|
+
url.hostname.endsWith('.localhost')) {
|
|
106
|
+
throw new Error('Outbound URL must use a public HTTP(S) destination.');
|
|
107
|
+
}
|
|
108
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, '');
|
|
109
|
+
const literalFamily = isIP(hostname);
|
|
110
|
+
const addresses = literalFamily
|
|
111
|
+
? [{ address: hostname, family: literalFamily }]
|
|
112
|
+
: await lookup(hostname).catch(() => []);
|
|
113
|
+
if (addresses.length === 0 || addresses.some(({ address }) => !isPublicIp(address))) {
|
|
114
|
+
throw new Error('Outbound URL must use a public HTTP(S) destination.');
|
|
115
|
+
}
|
|
116
|
+
return { url, addresses };
|
|
117
|
+
}
|
|
118
|
+
export async function assertPublicHttpUrl(value, lookup = defaultLookup) {
|
|
119
|
+
return (await resolvePublicHttpUrl(value, lookup)).url;
|
|
120
|
+
}
|
|
121
|
+
export async function safeFetch(value, init = {}, options = {}) {
|
|
122
|
+
const lookup = options.lookup ?? defaultLookup;
|
|
123
|
+
const maxRedirects = options.maxRedirects ?? 5;
|
|
124
|
+
let resolved = await resolvePublicHttpUrl(value, lookup);
|
|
125
|
+
for (let redirects = 0;; redirects++) {
|
|
126
|
+
const response = await pinnedFetch(resolved.url, resolved.addresses[0], init);
|
|
127
|
+
if (![301, 302, 303, 307, 308].includes(response.status))
|
|
128
|
+
return response;
|
|
129
|
+
const location = response.headers.get('location');
|
|
130
|
+
if (!location || redirects >= maxRedirects) {
|
|
131
|
+
await response.body?.cancel().catch(() => { });
|
|
132
|
+
throw new Error('Outbound request exceeded the redirect limit.');
|
|
133
|
+
}
|
|
134
|
+
const next = new URL(location, resolved.url);
|
|
135
|
+
await response.body?.cancel().catch(() => { });
|
|
136
|
+
resolved = await resolvePublicHttpUrl(next, lookup);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function pinnedFetch(url, target, init) {
|
|
140
|
+
const method = init.method?.toUpperCase() ?? 'GET';
|
|
141
|
+
if ((method === 'GET' || method === 'HEAD') && init.body) {
|
|
142
|
+
throw new Error(`${method} outbound requests cannot include a body.`);
|
|
143
|
+
}
|
|
144
|
+
if (init.body !== undefined &&
|
|
145
|
+
init.body !== null &&
|
|
146
|
+
typeof init.body !== 'string' &&
|
|
147
|
+
!(init.body instanceof Uint8Array)) {
|
|
148
|
+
throw new Error('Safe outbound fetch requires a replayable string or byte body.');
|
|
149
|
+
}
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
const headers = Object.fromEntries(new Headers(init.headers).entries());
|
|
152
|
+
const request = (url.protocol === 'https:' ? httpsRequest : httpRequest)(url, {
|
|
153
|
+
method,
|
|
154
|
+
headers,
|
|
155
|
+
signal: init.signal ?? undefined,
|
|
156
|
+
family: target.family,
|
|
157
|
+
lookup(_hostname, _options, callback) {
|
|
158
|
+
callback(null, target.address, target.family);
|
|
159
|
+
},
|
|
160
|
+
}, (incoming) => {
|
|
161
|
+
const responseHeaders = new Headers();
|
|
162
|
+
for (const [name, value] of Object.entries(incoming.headers)) {
|
|
163
|
+
if (Array.isArray(value)) {
|
|
164
|
+
for (const item of value)
|
|
165
|
+
responseHeaders.append(name, item);
|
|
166
|
+
}
|
|
167
|
+
else if (value !== undefined) {
|
|
168
|
+
responseHeaders.set(name, value);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const body = method === 'HEAD' || incoming.statusCode === 204 || incoming.statusCode === 304
|
|
172
|
+
? null
|
|
173
|
+
: Readable.toWeb(incoming);
|
|
174
|
+
resolve(new Response(body, {
|
|
175
|
+
status: incoming.statusCode ?? 500,
|
|
176
|
+
statusText: incoming.statusMessage ?? '',
|
|
177
|
+
headers: responseHeaders,
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
180
|
+
request.once('error', reject);
|
|
181
|
+
request.end(init.body ?? undefined);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
export async function readResponseBytes(response, maxBytes) {
|
|
185
|
+
const contentLength = Number(response.headers.get('content-length'));
|
|
186
|
+
if (Number.isFinite(contentLength) && contentLength > maxBytes) {
|
|
187
|
+
await response.body?.cancel().catch(() => { });
|
|
188
|
+
throw new Error('Remote response exceeds the size ceiling.');
|
|
189
|
+
}
|
|
190
|
+
if (!response.body)
|
|
191
|
+
return new Uint8Array();
|
|
192
|
+
const reader = response.body.getReader();
|
|
193
|
+
const chunks = [];
|
|
194
|
+
let total = 0;
|
|
195
|
+
try {
|
|
196
|
+
for (;;) {
|
|
197
|
+
const { done, value } = await reader.read();
|
|
198
|
+
if (done)
|
|
199
|
+
break;
|
|
200
|
+
total += value.byteLength;
|
|
201
|
+
if (total > maxBytes) {
|
|
202
|
+
await reader.cancel();
|
|
203
|
+
throw new Error('Remote response exceeds the size ceiling.');
|
|
204
|
+
}
|
|
205
|
+
chunks.push(value);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
reader.releaseLock();
|
|
210
|
+
}
|
|
211
|
+
const result = new Uint8Array(total);
|
|
212
|
+
let offset = 0;
|
|
213
|
+
for (const chunk of chunks) {
|
|
214
|
+
result.set(chunk, offset);
|
|
215
|
+
offset += chunk.byteLength;
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}
|
package/dist/settings.d.ts
CHANGED
|
@@ -2,7 +2,16 @@ export type PiSettingsOptions = {
|
|
|
2
2
|
cwd?: string;
|
|
3
3
|
/** Override for the config directory. If not set, resolveConfigDir() is used. */
|
|
4
4
|
configDir?: string;
|
|
5
|
+
/** Whether project-local settings and policies may be read. Defaults to false. */
|
|
6
|
+
projectTrusted?: boolean;
|
|
7
|
+
/** Whether to expand bare $ENV_VAR references in global and agent-dir settings. Defaults to true. */
|
|
8
|
+
expandBareEnvVars?: boolean;
|
|
9
|
+
/** Refuse malformed/unreadable trusted project settings instead of ignoring them. */
|
|
10
|
+
strictProjectSettings?: boolean;
|
|
5
11
|
};
|
|
12
|
+
export declare function isProjectTrusted(context: {
|
|
13
|
+
isProjectTrusted?: () => boolean;
|
|
14
|
+
} | null | undefined): boolean;
|
|
6
15
|
/**
|
|
7
16
|
* Resolve the user data / runtime home directory.
|
|
8
17
|
* Used for: memories, data, skills, logs.
|
|
@@ -17,8 +26,10 @@ export declare function resolveConfigDir(override?: string): string;
|
|
|
17
26
|
export declare function resolveAgentDir(override?: string): string;
|
|
18
27
|
/**
|
|
19
28
|
* Load extension config from pi-agent settings files.
|
|
20
|
-
* Reads from global (~/.pi/agent/settings.json), agentDir (env/param), and project
|
|
29
|
+
* Reads from global (~/.pi/agent/settings.json), agentDir (env/param), and trusted project
|
|
30
|
+
* (<cwd>/.pi/settings.json) settings.
|
|
21
31
|
* Priority from low to high: global < agentDir < project.
|
|
32
|
+
* Environment variables are expanded only in the global and agentDir layers.
|
|
22
33
|
*/
|
|
23
34
|
export declare function loadPiSettings<T>(key: string, options?: PiSettingsOptions): T;
|
|
24
35
|
export declare function loadJsonProfileDir<T>(dir: string): Record<string, T>;
|
package/dist/settings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qGAAqG;IACrG,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qFAAqF;IACrF,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC/D,OAAO,CAET;AA2DD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,CAAC,CA4B7E;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAsBpE;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAgBtF"}
|
package/dist/settings.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { basename, join, resolve } from 'node:path';
|
|
4
|
-
function
|
|
4
|
+
export function isProjectTrusted(context) {
|
|
5
|
+
return context?.isProjectTrusted?.() === true;
|
|
6
|
+
}
|
|
7
|
+
function resolveEnvVars(value, expandBareEnvVars) {
|
|
5
8
|
if (typeof value === 'string') {
|
|
6
|
-
|
|
9
|
+
const pattern = expandBareEnvVars ? /\$\{([^}]+)\}|\$([A-Z_][A-Z0-9_]*)/g : /\$\{([^}]+)\}/g;
|
|
10
|
+
return value.replace(pattern, (_match, braced, bare) => {
|
|
11
|
+
const expr = braced ?? bare ?? '';
|
|
7
12
|
const [name, ...rest] = expr.split(':-');
|
|
8
13
|
const fallback = rest.join(':-');
|
|
9
14
|
const envVal = process.env[name];
|
|
@@ -13,27 +18,40 @@ function resolveEnvVars(value) {
|
|
|
13
18
|
});
|
|
14
19
|
}
|
|
15
20
|
if (Array.isArray(value)) {
|
|
16
|
-
return value.map(resolveEnvVars);
|
|
21
|
+
return value.map((item) => resolveEnvVars(item, expandBareEnvVars));
|
|
17
22
|
}
|
|
18
23
|
if (value && typeof value === 'object') {
|
|
19
24
|
const result = {};
|
|
20
25
|
for (const [k, v] of Object.entries(value)) {
|
|
21
|
-
result[k] = resolveEnvVars(v);
|
|
26
|
+
result[k] = resolveEnvVars(v, expandBareEnvVars);
|
|
22
27
|
}
|
|
23
28
|
return result;
|
|
24
29
|
}
|
|
25
30
|
return value;
|
|
26
31
|
}
|
|
27
|
-
function readSettingsSection(filePath, key) {
|
|
32
|
+
function readSettingsSection(filePath, key, options) {
|
|
28
33
|
try {
|
|
29
34
|
const raw = readFileSync(filePath, 'utf-8');
|
|
30
35
|
const parsed = JSON.parse(raw);
|
|
31
36
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
32
|
-
|
|
37
|
+
const section = parsed[key] ?? {};
|
|
38
|
+
if (!section || typeof section !== 'object' || Array.isArray(section)) {
|
|
39
|
+
if (options.strict)
|
|
40
|
+
throw new Error('project settings section must be an object');
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
return (options.expandEnv ? resolveEnvVars(section, options.expandBareEnvVars) : section);
|
|
33
44
|
}
|
|
45
|
+
if (options.strict)
|
|
46
|
+
throw new Error('project settings root must be an object');
|
|
34
47
|
return {};
|
|
35
48
|
}
|
|
36
|
-
catch {
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error.code === 'ENOENT')
|
|
51
|
+
return {};
|
|
52
|
+
if (options.strict) {
|
|
53
|
+
throw new Error('Trusted project settings are unreadable or malformed.', { cause: error });
|
|
54
|
+
}
|
|
37
55
|
return {};
|
|
38
56
|
}
|
|
39
57
|
}
|
|
@@ -60,18 +78,32 @@ export function resolveAgentDir(override) {
|
|
|
60
78
|
}
|
|
61
79
|
/**
|
|
62
80
|
* Load extension config from pi-agent settings files.
|
|
63
|
-
* Reads from global (~/.pi/agent/settings.json), agentDir (env/param), and project
|
|
81
|
+
* Reads from global (~/.pi/agent/settings.json), agentDir (env/param), and trusted project
|
|
82
|
+
* (<cwd>/.pi/settings.json) settings.
|
|
64
83
|
* Priority from low to high: global < agentDir < project.
|
|
84
|
+
* Environment variables are expanded only in the global and agentDir layers.
|
|
65
85
|
*/
|
|
66
86
|
export function loadPiSettings(key, options) {
|
|
67
87
|
const globalDir = resolve(join(homedir(), '.pi', 'agent'));
|
|
68
88
|
const configDir = resolveConfigDir(options?.configDir);
|
|
69
89
|
const cwd = options?.cwd ?? process.cwd();
|
|
70
|
-
const globalSettings = readSettingsSection(join(globalDir, 'settings.json'), key
|
|
90
|
+
const globalSettings = readSettingsSection(join(globalDir, 'settings.json'), key, {
|
|
91
|
+
expandEnv: true,
|
|
92
|
+
expandBareEnvVars: options?.expandBareEnvVars !== false,
|
|
93
|
+
});
|
|
71
94
|
const configSettings = configDir === globalDir
|
|
72
95
|
? {}
|
|
73
|
-
: readSettingsSection(join(configDir, 'settings.json'), key
|
|
74
|
-
|
|
96
|
+
: readSettingsSection(join(configDir, 'settings.json'), key, {
|
|
97
|
+
expandEnv: true,
|
|
98
|
+
expandBareEnvVars: options?.expandBareEnvVars !== false,
|
|
99
|
+
});
|
|
100
|
+
const projectSettings = options?.projectTrusted === true
|
|
101
|
+
? readSettingsSection(join(cwd, '.pi', 'settings.json'), key, {
|
|
102
|
+
expandEnv: false,
|
|
103
|
+
expandBareEnvVars: false,
|
|
104
|
+
strict: options?.strictProjectSettings === true,
|
|
105
|
+
})
|
|
106
|
+
: {};
|
|
75
107
|
return { ...globalSettings, ...configSettings, ...projectSettings };
|
|
76
108
|
}
|
|
77
109
|
export function loadJsonProfileDir(dir) {
|
|
@@ -108,7 +140,9 @@ export function loadPiPolicyProfiles(options) {
|
|
|
108
140
|
const configPolicies = configPolicyDir === globalDir
|
|
109
141
|
? {}
|
|
110
142
|
: loadJsonProfileDir(configPolicyDir);
|
|
111
|
-
const projectPolicies =
|
|
143
|
+
const projectPolicies = options?.projectTrusted === true
|
|
144
|
+
? loadJsonProfileDir(projectDir)
|
|
145
|
+
: {};
|
|
112
146
|
return { ...globalPolicies, ...configPolicies, ...projectPolicies };
|
|
113
147
|
}
|
|
114
148
|
//# sourceMappingURL=settings.js.map
|
package/dist/settings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared threat-pattern library for memory content scanning.
|
|
3
|
+
*
|
|
4
|
+
* Covers prompt injection, role hijack, C2 framework names, exfiltration,
|
|
5
|
+
* persistence (SSH backdoor, agent-config edits), and invisible unicode.
|
|
6
|
+
*
|
|
7
|
+
* Pattern philosophy: anchor on attack-specific vocabulary or unambiguous
|
|
8
|
+
* attack behavior, NOT on bossy English. Multi-word filler is allowed via
|
|
9
|
+
* `(?:\w+\s+)*` between key tokens so attackers can't bypass with
|
|
10
|
+
* synonyms ("ignore all prior instructions").
|
|
11
|
+
*/
|
|
12
|
+
type Scope = 'all' | 'context' | 'strict';
|
|
13
|
+
/**
|
|
14
|
+
* Invisible / bidirectional unicode characters used in injection attacks.
|
|
15
|
+
*/
|
|
16
|
+
export declare const INVISIBLE_CHARS: ReadonlySet<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Return matched pattern IDs in `content` at the given scope.
|
|
19
|
+
*
|
|
20
|
+
* - `all`: classic injection + exfil only (lowest false-positive set).
|
|
21
|
+
* - `context`: adds promptware / C2 / role-play patterns.
|
|
22
|
+
* - `strict`: adds persistence / SSH backdoor / exfil-URL patterns.
|
|
23
|
+
*
|
|
24
|
+
* Invisible-unicode hits are reported as `invisible_unicode_U+XXXX`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function scanForThreats(content: string, scope?: Scope): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Return a human-readable error string for the first threat in `content`,
|
|
29
|
+
* or `null` when clean. Convenience wrapper for paths that block on first
|
|
30
|
+
* hit (memory writes).
|
|
31
|
+
*/
|
|
32
|
+
export declare function firstThreatMessage(content: string, scope?: Scope): string | null;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=threat-patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threat-patterns.d.ts","sourceRoot":"","sources":["../src/threat-patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAmH1C;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAkB9C,CAAC;AA0BH;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,KAAiB,GAAG,MAAM,EAAE,CA0BlF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,KAAgB,GAAG,MAAM,GAAG,IAAI,CAW1F"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared threat-pattern library for memory content scanning.
|
|
3
|
+
*
|
|
4
|
+
* Covers prompt injection, role hijack, C2 framework names, exfiltration,
|
|
5
|
+
* persistence (SSH backdoor, agent-config edits), and invisible unicode.
|
|
6
|
+
*
|
|
7
|
+
* Pattern philosophy: anchor on attack-specific vocabulary or unambiguous
|
|
8
|
+
* attack behavior, NOT on bossy English. Multi-word filler is allowed via
|
|
9
|
+
* `(?:\w+\s+)*` between key tokens so attackers can't bypass with
|
|
10
|
+
* synonyms ("ignore all prior instructions").
|
|
11
|
+
*/
|
|
12
|
+
const RAW_PATTERNS = [
|
|
13
|
+
// Classic prompt injection (applies everywhere)
|
|
14
|
+
[
|
|
15
|
+
String.raw `ignore\s+(?:\w+\s+)*(previous|all|above|prior)\s+(?:\w+\s+)*instructions`,
|
|
16
|
+
'prompt_injection',
|
|
17
|
+
'all',
|
|
18
|
+
],
|
|
19
|
+
[String.raw `system\s+prompt\s+override`, 'sys_prompt_override', 'all'],
|
|
20
|
+
[
|
|
21
|
+
String.raw `disregard\s+(?:\w+\s+)*(your|all|any)\s+(?:\w+\s+)*(instructions|rules|guidelines)`,
|
|
22
|
+
'disregard_rules',
|
|
23
|
+
'all',
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
String.raw `act\s+as\s+(if|though)\s+(?:\w+\s+)*you\s+(?:\w+\s+)*(have\s+no|don't\s+have)\s+(?:\w+\s+)*(restrictions|limits|rules)`,
|
|
27
|
+
'bypass_restrictions',
|
|
28
|
+
'all',
|
|
29
|
+
],
|
|
30
|
+
['<!--[^>]*(?:ignore|override|system|secret|hidden)[^>]*-->', 'html_comment_injection', 'all'],
|
|
31
|
+
[String.raw `<\s*div\s+style\s*=\s*["'][\s\S]*?display\s*:\s*none`, 'hidden_div', 'all'],
|
|
32
|
+
[String.raw `translate\s+.*\s+into\s+.*\s+and\s+(execute|run|eval)`, 'translate_execute', 'all'],
|
|
33
|
+
[String.raw `do\s+not\s+(?:\w+\s+)*tell\s+(?:\w+\s+)*the\s+user`, 'deception_hide', 'all'],
|
|
34
|
+
// Role-play / identity hijack (context + strict)
|
|
35
|
+
[String.raw `you\s+are\s+(?:\w+\s+)*now\s+(?:a|an|the)\s+`, 'role_hijack', 'context'],
|
|
36
|
+
[String.raw `pretend\s+(?:\w+\s+)*(you\s+are|to\s+be)\s+`, 'role_pretend', 'context'],
|
|
37
|
+
[String.raw `output\s+(?:\w+\s+)*(system|initial)\s+prompt`, 'leak_system_prompt', 'context'],
|
|
38
|
+
[
|
|
39
|
+
String.raw `(respond|answer|reply)\s+without\s+(?:\w+\s+)*(restrictions|limitations|filters|safety)`,
|
|
40
|
+
'remove_filters',
|
|
41
|
+
'context',
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
String.raw `you\s+have\s+been\s+(?:\w+\s+)*(updated|upgraded|patched)\s+to`,
|
|
45
|
+
'fake_update',
|
|
46
|
+
'context',
|
|
47
|
+
],
|
|
48
|
+
[String.raw `\bname\s+yourself\s+\w+`, 'identity_override', 'context'],
|
|
49
|
+
// C2 / Brainworm-style promptware
|
|
50
|
+
[String.raw `register\s+(as\s+)?a?\s*node`, 'c2_node_registration', 'context'],
|
|
51
|
+
[String.raw `(heartbeat|beacon|check[\s\-]?in)\s+(to|with)\s+`, 'c2_heartbeat', 'context'],
|
|
52
|
+
[String.raw `pull\s+(down\s+)?(?:new\s+)?task(?:ing|s)?\b`, 'c2_task_pull', 'context'],
|
|
53
|
+
[String.raw `connect\s+to\s+the\s+network\b`, 'c2_network_connect', 'context'],
|
|
54
|
+
[
|
|
55
|
+
String.raw `you\s+must\s+(?:\w+\s+){0,3}(register|connect|report|beacon)\b`,
|
|
56
|
+
'forced_action',
|
|
57
|
+
'context',
|
|
58
|
+
],
|
|
59
|
+
[String.raw `only\s+use\s+one[\s\-]?liners?\b`, 'anti_forensic_oneliner', 'context'],
|
|
60
|
+
[
|
|
61
|
+
String.raw `never\s+(?:\w+\s+)*(?:create|write)\s+(?:\w+\s+)*(?:script|file)\s+(?:\w+\s+)*disk`,
|
|
62
|
+
'anti_forensic_disk',
|
|
63
|
+
'context',
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
String.raw `unset\s+\w*(?:CLAUDE|CODEX|HERMES|AGENT|OPENAI|ANTHROPIC|PI)\w*`,
|
|
67
|
+
'env_var_unset_agent',
|
|
68
|
+
'context',
|
|
69
|
+
],
|
|
70
|
+
// Known C2 framework names
|
|
71
|
+
[
|
|
72
|
+
String.raw `\b(?:praxis|cobalt\s*strike|sliver|havoc|mythic|metasploit|brainworm)\b`,
|
|
73
|
+
'known_c2_framework',
|
|
74
|
+
'context',
|
|
75
|
+
],
|
|
76
|
+
[String.raw `\bc2\s+(?:server|channel|infrastructure|beacon)\b`, 'c2_explicit', 'context'],
|
|
77
|
+
[String.raw `\bcommand\s+and\s+control\b`, 'c2_explicit_long', 'context'],
|
|
78
|
+
// Exfiltration via curl/wget/cat with secrets
|
|
79
|
+
[
|
|
80
|
+
String.raw `curl\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)`,
|
|
81
|
+
'exfil_curl',
|
|
82
|
+
'all',
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
String.raw `wget\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)`,
|
|
86
|
+
'exfil_wget',
|
|
87
|
+
'all',
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
String.raw `cat\s+[^\n]*(\.env|credentials|\.netrc|\.pgpass|\.npmrc|\.pypirc)`,
|
|
91
|
+
'read_secrets',
|
|
92
|
+
'all',
|
|
93
|
+
],
|
|
94
|
+
[String.raw `(send|post|upload|transmit)\s+.*\s+(to|at)\s+https?://`, 'send_to_url', 'strict'],
|
|
95
|
+
[
|
|
96
|
+
String.raw `(include|output|print|share)\s+(?:\w+\s+)*(conversation|chat\s+history|previous\s+messages|full\s+context|entire\s+context)`,
|
|
97
|
+
'context_exfil',
|
|
98
|
+
'strict',
|
|
99
|
+
],
|
|
100
|
+
// Persistence / SSH backdoor (strict scope)
|
|
101
|
+
['authorized_keys', 'ssh_backdoor', 'strict'],
|
|
102
|
+
[String.raw `\$HOME/\.ssh|~/\.ssh`, 'ssh_access', 'strict'],
|
|
103
|
+
[String.raw `\$HOME/\.pi/\.env|~/\.pi/\.env`, 'pi_env', 'strict'],
|
|
104
|
+
[
|
|
105
|
+
String.raw `(update|modify|edit|write|change|append|add\s+to)\s+.*(?:AGENTS\.md|CLAUDE\.md|\.cursorrules|\.clinerules)`,
|
|
106
|
+
'agent_config_mod',
|
|
107
|
+
'strict',
|
|
108
|
+
],
|
|
109
|
+
// Hardcoded secrets
|
|
110
|
+
[
|
|
111
|
+
String.raw `(?:api[_-]?key|token|secret|password)\s*[=:]\s*["'][A-Za-z0-9+/=_-]{20,}`,
|
|
112
|
+
'hardcoded_secret',
|
|
113
|
+
'strict',
|
|
114
|
+
],
|
|
115
|
+
];
|
|
116
|
+
/**
|
|
117
|
+
* Invisible / bidirectional unicode characters used in injection attacks.
|
|
118
|
+
*/
|
|
119
|
+
export const INVISIBLE_CHARS = new Set([
|
|
120
|
+
'', // zero-width space
|
|
121
|
+
'', // zero-width non-joiner
|
|
122
|
+
'', // zero-width joiner
|
|
123
|
+
'', // word joiner
|
|
124
|
+
'', // invisible times
|
|
125
|
+
'', // invisible separator
|
|
126
|
+
'', // invisible plus
|
|
127
|
+
'', // zero-width no-break space (BOM)
|
|
128
|
+
'', // left-to-right embedding
|
|
129
|
+
'', // right-to-left embedding
|
|
130
|
+
'', // pop directional formatting
|
|
131
|
+
'', // left-to-right override
|
|
132
|
+
'', // right-to-left override
|
|
133
|
+
'', // left-to-right isolate
|
|
134
|
+
'', // right-to-left isolate
|
|
135
|
+
'', // first strong isolate
|
|
136
|
+
'', // pop directional isolate
|
|
137
|
+
]);
|
|
138
|
+
function buildScopeSets() {
|
|
139
|
+
const all = [];
|
|
140
|
+
const context = [];
|
|
141
|
+
const strict = [];
|
|
142
|
+
for (const [pattern, id, scope] of RAW_PATTERNS) {
|
|
143
|
+
const compiled = [new RegExp(pattern, 'i'), id];
|
|
144
|
+
if (scope === 'all') {
|
|
145
|
+
all.push(compiled);
|
|
146
|
+
context.push(compiled);
|
|
147
|
+
strict.push(compiled);
|
|
148
|
+
}
|
|
149
|
+
else if (scope === 'context') {
|
|
150
|
+
context.push(compiled);
|
|
151
|
+
strict.push(compiled);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
strict.push(compiled);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { all, context, strict };
|
|
158
|
+
}
|
|
159
|
+
const COMPILED = buildScopeSets();
|
|
160
|
+
/**
|
|
161
|
+
* Return matched pattern IDs in `content` at the given scope.
|
|
162
|
+
*
|
|
163
|
+
* - `all`: classic injection + exfil only (lowest false-positive set).
|
|
164
|
+
* - `context`: adds promptware / C2 / role-play patterns.
|
|
165
|
+
* - `strict`: adds persistence / SSH backdoor / exfil-URL patterns.
|
|
166
|
+
*
|
|
167
|
+
* Invisible-unicode hits are reported as `invisible_unicode_U+XXXX`.
|
|
168
|
+
*/
|
|
169
|
+
export function scanForThreats(content, scope = 'context') {
|
|
170
|
+
if (!content) {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
const findings = [];
|
|
174
|
+
for (const ch of content) {
|
|
175
|
+
if (INVISIBLE_CHARS.has(ch)) {
|
|
176
|
+
const code = ch.codePointAt(0).toString(16).toUpperCase().padStart(4, '0');
|
|
177
|
+
const finding = `invisible_unicode_U+${code}`;
|
|
178
|
+
if (!findings.includes(finding)) {
|
|
179
|
+
findings.push(finding);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const patterns = COMPILED[scope];
|
|
184
|
+
if (!patterns) {
|
|
185
|
+
throw new Error(`scanForThreats: unknown scope '${scope}'`);
|
|
186
|
+
}
|
|
187
|
+
for (const [regex, id] of patterns) {
|
|
188
|
+
if (regex.test(content)) {
|
|
189
|
+
findings.push(id);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return findings;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Return a human-readable error string for the first threat in `content`,
|
|
196
|
+
* or `null` when clean. Convenience wrapper for paths that block on first
|
|
197
|
+
* hit (memory writes).
|
|
198
|
+
*/
|
|
199
|
+
export function firstThreatMessage(content, scope = 'strict') {
|
|
200
|
+
const findings = scanForThreats(content, scope);
|
|
201
|
+
if (findings.length === 0) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const id = findings[0];
|
|
205
|
+
if (id.startsWith('invisible_unicode_')) {
|
|
206
|
+
const codepoint = id.replace('invisible_unicode_', '');
|
|
207
|
+
return `Blocked: content contains invisible unicode character ${codepoint} (possible injection).`;
|
|
208
|
+
}
|
|
209
|
+
return `Blocked: content matches threat pattern '${id}'. Content is injected into the system prompt and must not contain injection or exfiltration payloads.`;
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=threat-patterns.js.map
|