@letterblack/lbe-exec 1.2.2 → 1.2.4

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/index.js CHANGED
@@ -252,7 +252,7 @@ function savePolicyState(statePath, stateObj) {
252
252
  }
253
253
  function validateAndUpdatePolicyVersionState({
254
254
  policyObj,
255
- statePath = path2.resolve("data/policy.state.json"),
255
+ statePath = path2.resolve(".lbe/data/policy.state.json"),
256
256
  maxCreatedAtSkewSec = 31536e3,
257
257
  nowSec = Math.floor(Date.now() / 1e3),
258
258
  persist = true
@@ -1071,7 +1071,7 @@ import fs5 from "fs";
1071
1071
  import path6 from "path";
1072
1072
  import crypto2 from "crypto";
1073
1073
  function createBackup(filePath, backupDir) {
1074
- const dir = backupDir || path6.resolve("data/backups");
1074
+ const dir = backupDir || path6.resolve(".lbe/data/backups");
1075
1075
  if (!fs5.existsSync(dir)) {
1076
1076
  fs5.mkdirSync(dir, { recursive: true });
1077
1077
  }
@@ -1491,7 +1491,7 @@ import fs8 from "fs";
1491
1491
  import path9 from "path";
1492
1492
  import crypto4 from "crypto";
1493
1493
  var POLICY_FILE = "lbe.policy.json";
1494
- var AUDIT_FILE = "lbe.audit.jsonl";
1494
+ var AUDIT_FILE = ".lbe/audit.jsonl";
1495
1495
  function glob(pattern) {
1496
1496
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
1497
1497
  return new RegExp("^" + escaped.replace(/\*\*\//g, "(?:.*/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
@@ -1605,6 +1605,26 @@ function underRoot(candidate, root) {
1605
1605
  const resolvedRoot = physicalPath2(root);
1606
1606
  return target === resolvedRoot || target.startsWith(resolvedRoot + path10.sep);
1607
1607
  }
1608
+ var FORBIDDEN_CONTENT = [
1609
+ /\beval\s*\(/i,
1610
+ /\bFunction\s*\(/i,
1611
+ /\bexec\s*\(/i,
1612
+ /\brequire\s*\(/,
1613
+ /\bimport\s*\(/,
1614
+ /\bchild_process\b/,
1615
+ /\b__proto__\b/,
1616
+ /\bconstructor\s*\[/,
1617
+ /evalScript/i
1618
+ ];
1619
+ function scanContent(value, fieldName) {
1620
+ if (typeof value !== "string") return null;
1621
+ for (const pattern of FORBIDDEN_CONTENT) {
1622
+ if (pattern.test(value)) {
1623
+ return error("PAYLOAD_CONTENT_REJECTED", `Forbidden pattern in ${fieldName}: ${pattern}`);
1624
+ }
1625
+ }
1626
+ return null;
1627
+ }
1608
1628
  function normalize(rootDir, request, shell = {}) {
1609
1629
  if (!request || typeof request !== "object") return { error: error("REQUEST_INVALID", "request must be an object") };
1610
1630
  const detail = INTENTS[request.intent];
@@ -1618,6 +1638,8 @@ function normalize(rootDir, request, shell = {}) {
1618
1638
  if (["write_file", "patch_file"].includes(request.intent) && typeof request.content !== "string") {
1619
1639
  return { error: error("CONTENT_REQUIRED", "content is required for write and patch") };
1620
1640
  }
1641
+ const contentScan = scanContent(request.content, "content");
1642
+ if (contentScan) return { error: contentScan };
1621
1643
  }
1622
1644
  let command = null;
1623
1645
  if (detail.adapter === "shell") {
@@ -1702,7 +1724,7 @@ function createLocalExecutor(options = {}) {
1702
1724
  return prepared.error;
1703
1725
  }
1704
1726
  if (prepared.local.policy.mode === "observe") {
1705
- appendAudit(path10.join(rootDir, "lbe.audit.jsonl"), {
1727
+ appendAudit(path10.join(rootDir, ".lbe/audit.jsonl"), {
1706
1728
  kind: "local_execution",
1707
1729
  commandId: prepared.proposal.commandId,
1708
1730
  requesterId: prepared.normalized.actor,
@@ -1722,7 +1744,7 @@ function createLocalExecutor(options = {}) {
1722
1744
  const requester = prepared.policy.requesters[prepared.normalized.actor];
1723
1745
  const adapterResult = await executeAdapter(prepared.normalized.detail.adapter, prepared.proposal, prepared.policy, requester);
1724
1746
  const ok = adapterResult.status === "completed";
1725
- const audit = appendAudit(path10.join(rootDir, "lbe.audit.jsonl"), {
1747
+ const audit = appendAudit(path10.join(rootDir, ".lbe/audit.jsonl"), {
1726
1748
  kind: "local_execution",
1727
1749
  commandId: prepared.proposal.commandId,
1728
1750
  requesterId: prepared.normalized.actor,
@@ -1741,8 +1763,20 @@ function createLocalExecutor(options = {}) {
1741
1763
  ...ok ? {} : { error: { code: adapterResult.errorCode || "EXECUTION_FAILED", message: adapterResult.error || "Execution failed", recoverable: true } }
1742
1764
  };
1743
1765
  }
1766
+ const writeFile = (target, content) => execute({ intent: "write_file", target, content });
1767
+ const readFile = (target) => execute({ intent: "read_file", target });
1768
+ const patchFile = (target, content) => execute({ intent: "patch_file", target, content });
1769
+ const deleteFile = (target) => execute({ intent: "delete_file", target });
1770
+ const runShell = (cmd, args = [], opts = {}) => execute({ intent: "run_shell", command: { cmd, args, ...opts } });
1744
1771
  return {
1745
1772
  rootDir,
1773
+ // High-level API — use these
1774
+ writeFile,
1775
+ readFile,
1776
+ patchFile,
1777
+ deleteFile,
1778
+ runShell,
1779
+ // Low-level API — for advanced use
1746
1780
  validate: async (request) => {
1747
1781
  const preview = await dryRun(request);
1748
1782
  return { ...preview, dryRun: false, executed: false };
@@ -1754,7 +1788,7 @@ function createLocalExecutor(options = {}) {
1754
1788
  proposeRule: proposePolicyRule,
1755
1789
  addRule: (rule) => addLocalPolicyRule(rootDir, rule, options.mode || "enforce")
1756
1790
  },
1757
- audit: { verify: () => verifyAuditLogIntegrity(path10.join(rootDir, "lbe.audit.jsonl")) }
1791
+ audit: { verify: () => verifyAuditLogIntegrity(path10.join(rootDir, ".lbe/audit.jsonl")) }
1758
1792
  };
1759
1793
  }
1760
1794
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterblack/lbe-exec",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Local host-signed execution layer for LetterBlack LBE.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,8 +11,12 @@
11
11
  "default": "./dist/index.js"
12
12
  }
13
13
  },
14
+ "bin": {
15
+ "lbe-exec": "dist/cli.js"
16
+ },
14
17
  "files": [
15
18
  "dist/",
19
+ "assets/",
16
20
  "README.md",
17
21
  "types.d.ts",
18
22
  "LICENSE"
package/types.d.ts CHANGED
@@ -1,19 +1,50 @@
1
- export type LBEExecutionIntent = 'read_file' | 'write_file' | 'patch_file' | 'delete_file' | 'run_shell';
2
- export interface LBERequest {
3
- id?: string; actor?: string; intent: LBEExecutionIntent; target?: string; content?: string; patch?: unknown;
4
- command?: { cmd: string; args: string[]; cwd?: string; timeoutMs?: number; maxOutputBytes?: number }; reason?: string;
5
- }
6
1
  export interface LBEResult {
7
- ok: boolean; decision: 'allow' | 'deny' | 'observe'; executed: boolean; dryRun: boolean;
8
- error?: { code: string; message: string; recoverable: boolean }; matchedRules?: string[]; auditId?: string;
2
+ ok: boolean;
3
+ decision: 'allow' | 'deny' | 'observe';
4
+ executed: boolean;
5
+ dryRun: boolean;
6
+ error?: { code: string; message: string; recoverable: boolean };
7
+ matchedRules?: string[];
8
+ auditId?: string;
9
9
  rollback?: { available: boolean; performed: boolean; backupId?: string };
10
10
  }
11
+
12
+ export interface LBEPolicyRule {
13
+ effect: 'allow' | 'deny';
14
+ type: 'path' | 'command';
15
+ pattern: string;
16
+ from: string;
17
+ }
18
+
11
19
  export interface LocalExecutor {
12
20
  rootDir: string;
13
- validate(request: LBERequest): Promise<LBEResult>;
14
- dryRun(request: LBERequest): Promise<LBEResult>;
15
- execute(request: LBERequest): Promise<LBEResult>;
16
- policy: { read(): unknown; proposeRule(rule: { effect: 'allow' | 'deny'; type: 'path' | 'command'; pattern: string; from: string }): unknown; addRule(rule: { effect: 'allow' | 'deny'; type: 'path' | 'command'; pattern: string; from: string }): unknown };
21
+
22
+ // High-level API — use these in agent code
23
+ writeFile(target: string, content: string): Promise<LBEResult>;
24
+ readFile(target: string): Promise<LBEResult>;
25
+ patchFile(target: string, content: string): Promise<LBEResult>;
26
+ deleteFile(target: string): Promise<LBEResult>;
27
+ runShell(cmd: string, args?: string[], opts?: { cwd?: string; timeoutMs?: number; maxOutputBytes?: number }): Promise<LBEResult>;
28
+
29
+ // Policy management
30
+ policy: {
31
+ read(): unknown;
32
+ proposeRule(rule: LBEPolicyRule): unknown;
33
+ addRule(rule: LBEPolicyRule): unknown;
34
+ };
35
+
36
+ // Audit
17
37
  audit: { verify(): unknown };
38
+
39
+ // Low-level — for advanced / non-standard use only
40
+ validate(request: unknown): Promise<LBEResult>;
41
+ dryRun(request: unknown): Promise<LBEResult>;
42
+ execute(request: unknown): Promise<LBEResult>;
18
43
  }
19
- export function createLocalExecutor(options?: { rootDir?: string; keyId?: string; mode?: 'observe' | 'enforce'; shell?: { allowCommands?: string[]; denyCommands?: string[]; maxRequests?: number } }): LocalExecutor;
44
+
45
+ export function createLocalExecutor(options?: {
46
+ rootDir?: string;
47
+ keyId?: string;
48
+ mode?: 'observe' | 'enforce';
49
+ shell?: { allowCommands?: string[]; denyCommands?: string[]; maxRequests?: number };
50
+ }): LocalExecutor;