@letterblack/lbe-exec 1.2.17 → 1.2.19

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.
@@ -1,476 +1,5 @@
1
- 'use strict';
2
- // LBE Agent Bridge CJS preload hook
3
- // Load with: node --require @letterblack/lbe-exec/hooks/register.cjs agent.js
4
- // Or via: npx lbe-exec run-node [--mode observe|enforce] agent.js
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const { EventEmitter } = require('events');
9
- const { Readable } = require('stream');
10
-
11
- const ROOT_DIR = process.env.LBE_ROOT || process.cwd();
12
- const MODE = process.env.LBE_MODE || 'observe';
13
-
14
- // ── Policy loader (inline CJS — ESM cannot be require()'d synchronously) ────
15
-
16
- function loadPolicy() {
17
- // .lbe/policy.json is canonical. Fall back to legacy lbe.policy.json in root.
18
- const policyPath = fs.existsSync(path.join(ROOT_DIR, '.lbe', 'policy.json'))
19
- ? path.join(ROOT_DIR, '.lbe', 'policy.json')
20
- : path.join(ROOT_DIR, 'lbe.policy.json');
21
- try {
22
- if (fs.existsSync(policyPath)) {
23
- return JSON.parse(fs.readFileSync(policyPath, 'utf8'));
24
- }
25
- } catch (e) { /* fall through to default */ }
26
- return { version: 1, mode: MODE, workspace: ROOT_DIR, rules: [] };
27
- }
28
-
29
- function globToRegex(pattern) {
30
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
31
- return new RegExp('^' + escaped
32
- .replace(/\*\*\//g, '(?:.*/)?')
33
- .replace(/\*\*/g, '.*')
34
- .replace(/\*/g, '[^/]*') + '$');
35
- }
36
-
37
- function evaluatePolicy(action) {
38
- const policy = loadPolicy();
39
- const mode = policy.mode || MODE;
40
- const rules = Array.isArray(policy.rules) ? policy.rules : [];
41
-
42
- if (action.path) {
43
- try {
44
- const abs = path.resolve(ROOT_DIR, action.path);
45
- const sep = path.sep;
46
- if (!abs.startsWith(ROOT_DIR + sep) && abs !== ROOT_DIR) {
47
- return { decision: 'deny', deny: true, reason: 'PATH_OUTSIDE_ROOT', matchedRules: ['path:outside_root'], mode, enforced: mode === 'enforce' };
48
- }
49
- const rel = path.relative(ROOT_DIR, abs).split(sep).join('/');
50
- const matched = rules.filter(r => r.type === 'path' && globToRegex(r.pattern).test(rel));
51
- const denied = matched.filter(r => r.effect === 'deny');
52
- const isDeny = denied.length > 0;
53
- return {
54
- decision: isDeny ? 'deny' : 'allow', deny: isDeny,
55
- matchedRules: (isDeny ? denied : matched.filter(r => r.effect === 'allow')).map(r => r.id),
56
- mode, enforced: mode === 'enforce',
57
- };
58
- } catch (e) {
59
- if (mode === 'enforce') {
60
- return { decision: 'deny', deny: true, reason: 'PATH_RESOLUTION_ERROR', matchedRules: [], mode, enforced: true };
61
- }
62
- }
63
- }
64
-
65
- if (action.cmd) {
66
- const matched = rules.filter(r => r.type === 'command' && globToRegex(r.pattern).test(String(action.cmd)));
67
- const denied = matched.filter(r => r.effect === 'deny');
68
- const isDeny = denied.length > 0;
69
- return {
70
- decision: isDeny ? 'deny' : 'allow', deny: isDeny,
71
- matchedRules: (isDeny ? denied : matched.filter(r => r.effect === 'allow')).map(r => r.id),
72
- mode, enforced: mode === 'enforce',
73
- };
74
- }
75
-
76
- return { decision: 'allow', deny: false, matchedRules: [], mode, enforced: mode === 'enforce' };
77
- }
78
-
79
- // ── Audit writer ──────────────────────────────────────────────────────────────
80
- // fs.appendFileSync → fs.writeFileSync (Node.js internal) → would recurse.
81
- // fs.openSync/writeSync/closeSync are low-level bindings — never call back into JS.
82
- // Re-entrant guard prevents recursion from any code path we missed.
83
-
84
- var _auditInFlight = false;
85
-
86
- function auditEvent(entry) {
87
- if (_auditInFlight) return;
88
- _auditInFlight = true;
89
- try {
90
- var eventsPath = path.join(ROOT_DIR, '.lbe', 'events.jsonl');
91
- var dir = path.dirname(eventsPath);
92
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
93
- var line = JSON.stringify({ ts: Math.floor(Date.now() / 1000), ...entry }) + '\n';
94
- // Use open/write/close directly — bypasses all JS wrappers including writeFileSync
95
- var fd = fs.openSync(eventsPath, 'a');
96
- try { fs.writeSync(fd, line); } finally { fs.closeSync(fd); }
97
- } catch (e) {
98
- console.warn('[lbe] audit write failed:', e.message);
99
- } finally {
100
- _auditInFlight = false;
101
- }
102
- }
103
-
104
- class LBEPermissionError extends Error {
105
- constructor(decision, action) {
106
- const target = action.path || action.cmd || 'unknown';
107
- super('[LBE:' + decision.mode + '] DENIED ' + action.action + ' on ' + target);
108
- this.name = 'LBEPermissionError';
109
- this.code = 'LBE_PERMISSION_DENIED';
110
- this.lbeDecision = decision;
111
- }
112
- }
113
-
114
- // ── Originals — captured BEFORE any patch is applied ────────────────────────
115
-
116
- const origFs = {
117
- writeFile: fs.writeFile.bind(fs),
118
- writeFileSync: fs.writeFileSync.bind(fs),
119
- rm: fs.rm ? fs.rm.bind(fs) : null,
120
- rmSync: fs.rmSync ? fs.rmSync.bind(fs) : null,
121
- unlink: fs.unlink.bind(fs),
122
- unlinkSync: fs.unlinkSync.bind(fs),
123
- rename: fs.rename.bind(fs),
124
- renameSync: fs.renameSync.bind(fs),
125
- };
126
-
127
- const origPromises = {
128
- writeFile: fs.promises.writeFile.bind(fs.promises),
129
- rm: fs.promises.rm ? fs.promises.rm.bind(fs.promises) : null,
130
- unlink: fs.promises.unlink.bind(fs.promises),
131
- rename: fs.promises.rename.bind(fs.promises),
132
- };
133
-
134
- const cp = require('child_process');
135
- const origCp = {
136
- spawn: cp.spawn.bind(cp),
137
- spawnSync: cp.spawnSync.bind(cp),
138
- exec: cp.exec.bind(cp),
139
- execSync: cp.execSync.bind(cp),
140
- };
141
-
142
- // ── Denied spawn stub — EventEmitter-compatible ───────────────────────────────
143
-
144
- function makeFailedChildProcess(err) {
145
- const emitter = new EventEmitter();
146
- emitter.pid = null;
147
- emitter.killed = false;
148
- emitter.exitCode = 1;
149
- emitter.signalCode = null;
150
- emitter.stdout = new Readable({ read() {} });
151
- emitter.stderr = new Readable({ read() {} });
152
- emitter.stdin = { write() { return false; }, end() {}, destroy() {} };
153
- emitter.kill = () => false;
154
- emitter.ref = () => emitter;
155
- emitter.unref = () => emitter;
156
- process.nextTick(function () {
157
- emitter.stdout.push(null);
158
- emitter.stderr.push(null);
159
- emitter.emit('error', err);
160
- emitter.emit('close', 1, null);
161
- emitter.emit('exit', 1, null);
162
- });
163
- return emitter;
164
- }
165
-
166
- // ── Decision + pre-block audit ────────────────────────────────────────────────
167
-
168
- function decide(action) {
169
- var decision;
170
- try {
171
- decision = evaluatePolicy(action);
172
- } catch (e) {
173
- if (MODE === 'enforce') {
174
- var failErr = new LBEPermissionError({ mode: 'enforce', enforced: true }, action);
175
- try { auditEvent({ action: action.action, path: action.path, cmd: action.cmd, actor: 'agent:lbe-hooks', decision: 'deny', mode: 'enforce', enforced: true, executed: false, matched_rules: [], error: e.message }); } catch (_) {}
176
- return { blocked: true, error: failErr };
177
- }
178
- console.warn('[lbe] policy evaluation failed (observe mode, allowing):', e.message);
179
- return { blocked: false, decision: { decision: 'allow', deny: false, matchedRules: [], mode: 'observe', enforced: false } };
180
- }
181
-
182
- if (decision.deny && decision.enforced) {
183
- var err = new LBEPermissionError(decision, action);
184
- try { auditEvent({ action: action.action, path: action.path, cmd: action.cmd, actor: 'agent:lbe-hooks', decision: 'deny', mode: decision.mode, enforced: true, executed: false, matched_rules: decision.matchedRules }); } catch (_) {}
185
- return { blocked: true, error: err };
186
- }
187
-
188
- return { blocked: false, decision: decision };
189
- }
190
-
191
- function postAudit(action, decision, executed, extra) {
192
- try {
193
- auditEvent(Object.assign({ action: action.action, path: action.path, cmd: action.cmd, actor: 'agent:lbe-hooks', decision: decision.decision, mode: decision.mode, enforced: decision.enforced, executed: executed, matched_rules: decision.matchedRules }, extra || {}));
194
- } catch (e) {
195
- console.warn('[lbe] post-action audit failed (result unaffected):', e.message);
196
- }
197
- }
198
-
199
- // ── Patch fs callbacks ────────────────────────────────────────────────────────
200
-
201
- fs.writeFile = function lbeWriteFile(filePath, data, options, callback) {
202
- if (typeof options === 'function') { callback = options; options = undefined; }
203
- var action = { action: 'file_write', path: String(filePath) };
204
- var check = decide(action);
205
- if (check.blocked) { process.nextTick(function () { callback(check.error); }); return; }
206
- function done(err) {
207
- postAudit(action, check.decision, !err, err ? { error: err.message } : null);
208
- callback(err);
209
- }
210
- if (options !== undefined) { origFs.writeFile(filePath, data, options, done); }
211
- else { origFs.writeFile(filePath, data, done); }
212
- };
213
-
214
- fs.writeFileSync = function lbeWriteFileSync(filePath, data, options) {
215
- var action = { action: 'file_write', path: String(filePath) };
216
- var check = decide(action);
217
- if (check.blocked) { throw check.error; }
218
- try {
219
- var result = options !== undefined ? origFs.writeFileSync(filePath, data, options) : origFs.writeFileSync(filePath, data);
220
- postAudit(action, check.decision, true);
221
- return result;
222
- } catch (e) {
223
- postAudit(action, check.decision, false, { error: e.message });
224
- throw e;
225
- }
226
- };
227
-
228
- if (origFs.rm) {
229
- fs.rm = function lbeRm(filePath, options, callback) {
230
- if (typeof options === 'function') { callback = options; options = undefined; }
231
- var action = { action: 'file_delete', path: String(filePath) };
232
- var check = decide(action);
233
- if (check.blocked) { process.nextTick(function () { callback(check.error); }); return; }
234
- function done(err) {
235
- postAudit(action, check.decision, !err, err ? { error: err.message } : null);
236
- callback(err);
237
- }
238
- if (options !== undefined) { origFs.rm(filePath, options, done); }
239
- else { origFs.rm(filePath, done); }
240
- };
241
- }
242
-
243
- if (origFs.rmSync) {
244
- fs.rmSync = function lbeRmSync(filePath, options) {
245
- var action = { action: 'file_delete', path: String(filePath) };
246
- var check = decide(action);
247
- if (check.blocked) { throw check.error; }
248
- try {
249
- var result = options !== undefined ? origFs.rmSync(filePath, options) : origFs.rmSync(filePath);
250
- postAudit(action, check.decision, true);
251
- return result;
252
- } catch (e) {
253
- postAudit(action, check.decision, false, { error: e.message });
254
- throw e;
255
- }
256
- };
257
- }
258
-
259
- fs.unlink = function lbeUnlink(filePath, options, callback) {
260
- if (typeof options === 'function') { callback = options; options = undefined; }
261
- var action = { action: 'file_delete', path: String(filePath) };
262
- var check = decide(action);
263
- if (check.blocked) { process.nextTick(function () { callback(check.error); }); return; }
264
- origFs.unlink(filePath, function done(err) {
265
- postAudit(action, check.decision, !err, err ? { error: err.message } : null);
266
- callback(err);
267
- });
268
- };
269
-
270
- fs.unlinkSync = function lbeUnlinkSync(filePath) {
271
- var action = { action: 'file_delete', path: String(filePath) };
272
- var check = decide(action);
273
- if (check.blocked) { throw check.error; }
274
- try {
275
- var result = origFs.unlinkSync(filePath);
276
- postAudit(action, check.decision, true);
277
- return result;
278
- } catch (e) {
279
- postAudit(action, check.decision, false, { error: e.message });
280
- throw e;
281
- }
282
- };
283
-
284
- fs.rename = function lbeRename(oldPath, newPath, options, callback) {
285
- if (typeof options === 'function') { callback = options; options = undefined; }
286
- var action = { action: 'file_rename', path: String(oldPath), dest: String(newPath) };
287
- var check = decide(action);
288
- if (check.blocked) { process.nextTick(function () { callback(check.error); }); return; }
289
- origFs.rename(oldPath, newPath, function done(err) {
290
- postAudit(action, check.decision, !err, err ? { error: err.message } : null);
291
- callback(err);
292
- });
293
- };
294
-
295
- fs.renameSync = function lbeRenameSync(oldPath, newPath) {
296
- var action = { action: 'file_rename', path: String(oldPath), dest: String(newPath) };
297
- var check = decide(action);
298
- if (check.blocked) { throw check.error; }
299
- try {
300
- var result = origFs.renameSync(oldPath, newPath);
301
- postAudit(action, check.decision, true);
302
- return result;
303
- } catch (e) {
304
- postAudit(action, check.decision, false, { error: e.message });
305
- throw e;
306
- }
307
- };
308
-
309
- // ── Patch fs.promises ─────────────────────────────────────────────────────────
310
-
311
- fs.promises.writeFile = async function lbePromisesWriteFile(filePath, data, options) {
312
- var action = { action: 'file_write', path: String(filePath) };
313
- var check = decide(action);
314
- if (check.blocked) { throw check.error; }
315
- try {
316
- var result = options !== undefined
317
- ? await origPromises.writeFile(filePath, data, options)
318
- : await origPromises.writeFile(filePath, data);
319
- postAudit(action, check.decision, true);
320
- return result;
321
- } catch (e) {
322
- postAudit(action, check.decision, false, { error: e.message });
323
- throw e;
324
- }
325
- };
326
-
327
- if (origPromises.rm) {
328
- fs.promises.rm = async function lbePromisesRm(filePath, options) {
329
- var action = { action: 'file_delete', path: String(filePath) };
330
- var check = decide(action);
331
- if (check.blocked) { throw check.error; }
332
- try {
333
- var result = options !== undefined
334
- ? await origPromises.rm(filePath, options)
335
- : await origPromises.rm(filePath);
336
- postAudit(action, check.decision, true);
337
- return result;
338
- } catch (e) {
339
- postAudit(action, check.decision, false, { error: e.message });
340
- throw e;
341
- }
342
- };
343
- }
344
-
345
- fs.promises.unlink = async function lbePromisesUnlink(filePath) {
346
- var action = { action: 'file_delete', path: String(filePath) };
347
- var check = decide(action);
348
- if (check.blocked) { throw check.error; }
349
- try {
350
- var result = await origPromises.unlink(filePath);
351
- postAudit(action, check.decision, true);
352
- return result;
353
- } catch (e) {
354
- postAudit(action, check.decision, false, { error: e.message });
355
- throw e;
356
- }
357
- };
358
-
359
- fs.promises.rename = async function lbePromisesRename(oldPath, newPath) {
360
- var action = { action: 'file_rename', path: String(oldPath), dest: String(newPath) };
361
- var check = decide(action);
362
- if (check.blocked) { throw check.error; }
363
- try {
364
- var result = await origPromises.rename(oldPath, newPath);
365
- postAudit(action, check.decision, true);
366
- return result;
367
- } catch (e) {
368
- postAudit(action, check.decision, false, { error: e.message });
369
- throw e;
370
- }
371
- };
372
-
373
- // ── Patch child_process ───────────────────────────────────────────────────────
374
-
375
- cp.spawn = function lbeSpawn(cmd, args, options) {
376
- if (args && !Array.isArray(args)) { options = args; args = []; }
377
- var cwd = options && options.cwd ? String(options.cwd) : ROOT_DIR;
378
- var shell = !!(options && options.shell);
379
- var action = { action: 'run_shell', cmd: String(cmd), args: args || [], cwd: cwd, shell: shell };
380
- var check = decide(action);
381
- if (check.blocked) { return makeFailedChildProcess(check.error); }
382
- var child = origCp.spawn(cmd, args || [], options || {});
383
- child.on('close', function (code) { postAudit(action, check.decision, code === 0, { exit_code: code }); });
384
- return child;
385
- };
386
-
387
- cp.spawnSync = function lbeSpawnSync(cmd, args, options) {
388
- if (args && !Array.isArray(args)) { options = args; args = []; }
389
- var cwd = options && options.cwd ? String(options.cwd) : ROOT_DIR;
390
- var shell = !!(options && options.shell);
391
- var action = { action: 'run_shell', cmd: String(cmd), args: args || [], cwd: cwd, shell: shell };
392
- var check = decide(action);
393
- if (check.blocked) {
394
- return { pid: 0, output: [null, Buffer.alloc(0), Buffer.alloc(0)], stdout: Buffer.alloc(0), stderr: Buffer.alloc(0), status: 1, signal: null, error: check.error };
395
- }
396
- var result = origCp.spawnSync(cmd, args || [], options || {});
397
- postAudit(action, check.decision, result.status === 0, { exit_code: result.status });
398
- return result;
399
- };
400
-
401
- cp.exec = function lbeExec(command, options, callback) {
402
- if (typeof options === 'function') { callback = options; options = undefined; }
403
- var cwd = options && options.cwd ? String(options.cwd) : ROOT_DIR;
404
- var action = { action: 'run_shell', cmd: String(command), args: [], cwd: cwd, shell: true };
405
- var check = decide(action);
406
- if (check.blocked) {
407
- process.nextTick(function () { if (callback) callback(check.error, '', ''); });
408
- return makeFailedChildProcess(check.error);
409
- }
410
- var cb = function done(err, stdout, stderr) {
411
- postAudit(action, check.decision, !err, err ? { error: err.message } : null);
412
- if (callback) callback(err, stdout, stderr);
413
- };
414
- return options !== undefined ? origCp.exec(command, options, cb) : origCp.exec(command, cb);
415
- };
416
-
417
- cp.execSync = function lbeExecSync(command, options) {
418
- var cwd = options && options.cwd ? String(options.cwd) : ROOT_DIR;
419
- var action = { action: 'run_shell', cmd: String(command), args: [], cwd: cwd, shell: true };
420
- var check = decide(action);
421
- if (check.blocked) { throw check.error; }
422
- try {
423
- var result = options !== undefined ? origCp.execSync(command, options) : origCp.execSync(command);
424
- postAudit(action, check.decision, true);
425
- return result;
426
- } catch (e) {
427
- postAudit(action, check.decision, false, { error: e.message });
428
- throw e;
429
- }
430
- };
431
-
432
- // ── Write hook-status.json (uses origFs — captured before patching) ───────────
433
-
434
- process.env.LBE_HOOK_ACTIVE = '1';
435
-
436
- try {
437
- var statusDir = path.join(ROOT_DIR, '.lbe', 'runtime');
438
- if (!fs.existsSync(statusDir)) fs.mkdirSync(statusDir, { recursive: true });
439
- var status = {
440
- active: true,
441
- pid: process.pid,
442
- started_at: new Date().toISOString(),
443
- mode: MODE,
444
- root: ROOT_DIR,
445
- patched: {
446
- 'fs.writeFile': true,
447
- 'fs.writeFileSync': true,
448
- 'fs.rm': !!origFs.rm,
449
- 'fs.rmSync': !!origFs.rmSync,
450
- 'fs.unlink': true,
451
- 'fs.unlinkSync': true,
452
- 'fs.rename': true,
453
- 'fs.renameSync': true,
454
- 'fs.promises.writeFile': true,
455
- 'fs.promises.rm': !!origPromises.rm,
456
- 'fs.promises.unlink': true,
457
- 'fs.promises.rename': true,
458
- 'child_process.spawn': true,
459
- 'child_process.spawnSync': true,
460
- 'child_process.exec': true,
461
- 'child_process.execSync': true,
462
- }
463
- };
464
- // Use original writeFileSync (pre-patch) to avoid triggering our own hook
465
- origFs.writeFileSync(path.join(statusDir, 'hook-status.json'), JSON.stringify(status, null, 2) + '\n', 'utf8');
466
- } catch (e) {
467
- console.warn('[lbe] could not write hook-status.json:', e.message);
468
- }
469
-
470
- // ── Banner ────────────────────────────────────────────────────────────────────
471
-
472
- if (MODE === 'observe') {
473
- process.stderr.write('[lbe] OBSERVE mode — no actions blocked, all writes logged to .lbe/events.jsonl\n');
474
- } else if (MODE === 'enforce') {
475
- process.stderr.write('[lbe] ENFORCE mode — policy denials will block execution\n');
476
- }
1
+ "use strict";var t=require("fs"),S=require("path"),{EventEmitter:T}=require("events"),{Readable:R}=require("stream"),m=process.env.LBE_ROOT||process.cwd(),p=process.env.LBE_MODE||"observe";function A(){let o=t.existsSync(S.join(m,".lbe","policy.json"))?S.join(m,".lbe","policy.json"):S.join(m,"lbe.policy.json");try{if(t.existsSync(o))return JSON.parse(t.readFileSync(o,"utf8"))}catch{}return{version:1,mode:p,workspace:m,rules:[]}}function E(o){let e=o.replace(/[.+^${}()|[\]\\]/g,"\\$&");return new RegExp("^"+e.replace(/\*\*\//g,"(?:.*/)?").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*")+"$")}function B(o){let e=A(),r=e.mode||p,n=Array.isArray(e.rules)?e.rules:[];if(o.path)try{let c=S.resolve(m,o.path),i=S.sep;if(!c.startsWith(m+i)&&c!==m)return{decision:"deny",deny:!0,reason:"PATH_OUTSIDE_ROOT",matchedRules:["path:outside_root"],mode:r,enforced:r==="enforce"};let s=S.relative(m,c).split(i).join("/"),l=n.filter(w=>w.type==="path"&&E(w.pattern).test(s)),d=l.filter(w=>w.effect==="deny"),h=d.length>0;return{decision:h?"deny":"allow",deny:h,matchedRules:(h?d:l.filter(w=>w.effect==="allow")).map(w=>w.id),mode:r,enforced:r==="enforce"}}catch{if(r==="enforce")return{decision:"deny",deny:!0,reason:"PATH_RESOLUTION_ERROR",matchedRules:[],mode:r,enforced:!0}}if(o.cmd){let c=n.filter(l=>l.type==="command"&&E(l.pattern).test(String(o.cmd))),i=c.filter(l=>l.effect==="deny"),s=i.length>0;return{decision:s?"deny":"allow",deny:s,matchedRules:(s?i:c.filter(l=>l.effect==="allow")).map(l=>l.id),mode:r,enforced:r==="enforce"}}return{decision:"allow",deny:!1,matchedRules:[],mode:r,enforced:r==="enforce"}}var _=!1;function x(o){if(!_){_=!0;try{var e=S.join(m,".lbe","events.jsonl"),r=S.dirname(e);t.existsSync(r)||t.mkdirSync(r,{recursive:!0});var n=JSON.stringify({ts:Math.floor(Date.now()/1e3),...o})+`
2
+ `,c=t.openSync(e,"a");try{t.writeSync(c,n)}finally{t.closeSync(c)}}catch(i){console.warn("[lbe] audit write failed:",i.message)}finally{_=!1}}}var k=class extends Error{constructor(e,r){let n=r.path||r.cmd||"unknown";super("[LBE:"+e.mode+"] DENIED "+r.action+" on "+n),this.name="LBEPermissionError",this.code="LBE_PERMISSION_DENIED",this.lbeDecision=e}},u={writeFile:t.writeFile.bind(t),writeFileSync:t.writeFileSync.bind(t),rm:t.rm?t.rm.bind(t):null,rmSync:t.rmSync?t.rmSync.bind(t):null,unlink:t.unlink.bind(t),unlinkSync:t.unlinkSync.bind(t),rename:t.rename.bind(t),renameSync:t.renameSync.bind(t)},v={writeFile:t.promises.writeFile.bind(t.promises),rm:t.promises.rm?t.promises.rm.bind(t.promises):null,unlink:t.promises.unlink.bind(t.promises),rename:t.promises.rename.bind(t.promises)},y=require("child_process"),b={spawn:y.spawn.bind(y),spawnSync:y.spawnSync.bind(y),exec:y.exec.bind(y),execSync:y.execSync.bind(y)};function O(o){let e=new T;return e.pid=null,e.killed=!1,e.exitCode=1,e.signalCode=null,e.stdout=new R({read(){}}),e.stderr=new R({read(){}}),e.stdin={write(){return!1},end(){},destroy(){}},e.kill=()=>!1,e.ref=()=>e,e.unref=()=>e,process.nextTick(function(){e.stdout.push(null),e.stderr.push(null),e.emit("error",o),e.emit("close",1,null),e.emit("exit",1,null)}),e}function f(o){var e;try{e=B(o)}catch(c){if(p==="enforce"){var r=new k({mode:"enforce",enforced:!0},o);try{x({action:o.action,path:o.path,cmd:o.cmd,actor:"agent:lbe-hooks",decision:"deny",mode:"enforce",enforced:!0,executed:!1,matched_rules:[],error:c.message})}catch{}return{blocked:!0,error:r}}return console.warn("[lbe] policy evaluation failed (observe mode, allowing):",c.message),{blocked:!1,decision:{decision:"allow",deny:!1,matchedRules:[],mode:"observe",enforced:!1}}}if(e.deny&&e.enforced){var n=new k(e,o);try{x({action:o.action,path:o.path,cmd:o.cmd,actor:"agent:lbe-hooks",decision:"deny",mode:e.mode,enforced:!0,executed:!1,matched_rules:e.matchedRules})}catch{}return{blocked:!0,error:n}}return{blocked:!1,decision:e}}function a(o,e,r,n){try{x(Object.assign({action:o.action,path:o.path,cmd:o.cmd,actor:"agent:lbe-hooks",decision:e.decision,mode:e.mode,enforced:e.enforced,executed:r,matched_rules:e.matchedRules},n||{}))}catch(c){console.warn("[lbe] post-action audit failed (result unaffected):",c.message)}}t.writeFile=function(e,r,n,c){typeof n=="function"&&(c=n,n=void 0);var i={action:"file_write",path:String(e)},s=f(i);if(s.blocked){process.nextTick(function(){c(s.error)});return}function l(d){a(i,s.decision,!d,d?{error:d.message}:null),c(d)}n!==void 0?u.writeFile(e,r,n,l):u.writeFile(e,r,l)};t.writeFileSync=function(e,r,n){var c={action:"file_write",path:String(e)},i=f(c);if(i.blocked)throw i.error;try{var s=n!==void 0?u.writeFileSync(e,r,n):u.writeFileSync(e,r);return a(c,i.decision,!0),s}catch(l){throw a(c,i.decision,!1,{error:l.message}),l}};u.rm&&(t.rm=function(e,r,n){typeof r=="function"&&(n=r,r=void 0);var c={action:"file_delete",path:String(e)},i=f(c);if(i.blocked){process.nextTick(function(){n(i.error)});return}function s(l){a(c,i.decision,!l,l?{error:l.message}:null),n(l)}r!==void 0?u.rm(e,r,s):u.rm(e,s)});u.rmSync&&(t.rmSync=function(e,r){var n={action:"file_delete",path:String(e)},c=f(n);if(c.blocked)throw c.error;try{var i=r!==void 0?u.rmSync(e,r):u.rmSync(e);return a(n,c.decision,!0),i}catch(s){throw a(n,c.decision,!1,{error:s.message}),s}});t.unlink=function(e,r,n){typeof r=="function"&&(n=r,r=void 0);var c={action:"file_delete",path:String(e)},i=f(c);if(i.blocked){process.nextTick(function(){n(i.error)});return}u.unlink(e,function(l){a(c,i.decision,!l,l?{error:l.message}:null),n(l)})};t.unlinkSync=function(e){var r={action:"file_delete",path:String(e)},n=f(r);if(n.blocked)throw n.error;try{var c=u.unlinkSync(e);return a(r,n.decision,!0),c}catch(i){throw a(r,n.decision,!1,{error:i.message}),i}};t.rename=function(e,r,n,c){typeof n=="function"&&(c=n,n=void 0);var i={action:"file_rename",path:String(e),dest:String(r)},s=f(i);if(s.blocked){process.nextTick(function(){c(s.error)});return}u.rename(e,r,function(d){a(i,s.decision,!d,d?{error:d.message}:null),c(d)})};t.renameSync=function(e,r){var n={action:"file_rename",path:String(e),dest:String(r)},c=f(n);if(c.blocked)throw c.error;try{var i=u.renameSync(e,r);return a(n,c.decision,!0),i}catch(s){throw a(n,c.decision,!1,{error:s.message}),s}};t.promises.writeFile=async function(e,r,n){var c={action:"file_write",path:String(e)},i=f(c);if(i.blocked)throw i.error;try{var s=n!==void 0?await v.writeFile(e,r,n):await v.writeFile(e,r);return a(c,i.decision,!0),s}catch(l){throw a(c,i.decision,!1,{error:l.message}),l}};v.rm&&(t.promises.rm=async function(e,r){var n={action:"file_delete",path:String(e)},c=f(n);if(c.blocked)throw c.error;try{var i=r!==void 0?await v.rm(e,r):await v.rm(e);return a(n,c.decision,!0),i}catch(s){throw a(n,c.decision,!1,{error:s.message}),s}});t.promises.unlink=async function(e){var r={action:"file_delete",path:String(e)},n=f(r);if(n.blocked)throw n.error;try{var c=await v.unlink(e);return a(r,n.decision,!0),c}catch(i){throw a(r,n.decision,!1,{error:i.message}),i}};t.promises.rename=async function(e,r){var n={action:"file_rename",path:String(e),dest:String(r)},c=f(n);if(c.blocked)throw c.error;try{var i=await v.rename(e,r);return a(n,c.decision,!0),i}catch(s){throw a(n,c.decision,!1,{error:s.message}),s}};y.spawn=function(e,r,n){r&&!Array.isArray(r)&&(n=r,r=[]);var c=n&&n.cwd?String(n.cwd):m,i=!!(n&&n.shell),s={action:"run_shell",cmd:String(e),args:r||[],cwd:c,shell:i},l=f(s);if(l.blocked)return O(l.error);var d=b.spawn(e,r||[],n||{});return d.on("close",function(h){a(s,l.decision,h===0,{exit_code:h})}),d};y.spawnSync=function(e,r,n){r&&!Array.isArray(r)&&(n=r,r=[]);var c=n&&n.cwd?String(n.cwd):m,i=!!(n&&n.shell),s={action:"run_shell",cmd:String(e),args:r||[],cwd:c,shell:i},l=f(s);if(l.blocked)return{pid:0,output:[null,Buffer.alloc(0),Buffer.alloc(0)],stdout:Buffer.alloc(0),stderr:Buffer.alloc(0),status:1,signal:null,error:l.error};var d=b.spawnSync(e,r||[],n||{});return a(s,l.decision,d.status===0,{exit_code:d.status}),d};y.exec=function(e,r,n){typeof r=="function"&&(n=r,r=void 0);var c=r&&r.cwd?String(r.cwd):m,i={action:"run_shell",cmd:String(e),args:[],cwd:c,shell:!0},s=f(i);if(s.blocked)return process.nextTick(function(){n&&n(s.error,"","")}),O(s.error);var l=function(h,w,j){a(i,s.decision,!h,h?{error:h.message}:null),n&&n(h,w,j)};return r!==void 0?b.exec(e,r,l):b.exec(e,l)};y.execSync=function(e,r){var n=r&&r.cwd?String(r.cwd):m,c={action:"run_shell",cmd:String(e),args:[],cwd:n,shell:!0},i=f(c);if(i.blocked)throw i.error;try{var s=r!==void 0?b.execSync(e,r):b.execSync(e);return a(c,i.decision,!0),s}catch(l){throw a(c,i.decision,!1,{error:l.message}),l}};process.env.LBE_HOOK_ACTIVE="1";try{g=S.join(m,".lbe","runtime"),t.existsSync(g)||t.mkdirSync(g,{recursive:!0}),F={active:!0,pid:process.pid,started_at:new Date().toISOString(),mode:p,root:m,patched:{"fs.writeFile":!0,"fs.writeFileSync":!0,"fs.rm":!!u.rm,"fs.rmSync":!!u.rmSync,"fs.unlink":!0,"fs.unlinkSync":!0,"fs.rename":!0,"fs.renameSync":!0,"fs.promises.writeFile":!0,"fs.promises.rm":!!v.rm,"fs.promises.unlink":!0,"fs.promises.rename":!0,"child_process.spawn":!0,"child_process.spawnSync":!0,"child_process.exec":!0,"child_process.execSync":!0}},u.writeFileSync(S.join(g,"hook-status.json"),JSON.stringify(F,null,2)+`
3
+ `,"utf8")}catch(o){console.warn("[lbe] could not write hook-status.json:",o.message)}var g,F;p==="observe"?process.stderr.write(`[lbe] OBSERVE mode \u2014 no actions blocked, all writes logged to .lbe/events.jsonl
4
+ `):p==="enforce"&&process.stderr.write(`[lbe] ENFORCE mode \u2014 policy denials will block execution
5
+ `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterblack/lbe-exec",
3
- "version": "1.2.17",
3
+ "version": "1.2.19",
4
4
  "description": "Local host-signed execution layer for LetterBlack LBE.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,6 +20,7 @@
20
20
  "hooks/",
21
21
  "assets/",
22
22
  "README.md",
23
+ "TRUST.md",
23
24
  "types.d.ts",
24
25
  "LICENSE"
25
26
  ],