@agenshield/daemon 0.6.1 → 0.6.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.
package/acl.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * macOS ACL utilities for filesystem policies.
3
3
  *
4
- * Uses `chmod +a / -a` to grant/revoke group-level ACLs on paths
4
+ * Uses `chmod +a / -a` to grant/revoke user-level ACLs on paths
5
5
  * derived from policy patterns. Failures are logged but never thrown.
6
6
  */
7
7
  import type { PolicyConfig } from '@agenshield/ipc';
@@ -21,24 +21,27 @@ export declare function stripGlobToBasePath(pattern: string): string;
21
21
  */
22
22
  export declare function operationsToAclPerms(operations: string[]): string;
23
23
  /**
24
- * Add a group ACL entry to a path.
24
+ * Add a user ACL entry to a path.
25
25
  */
26
- export declare function addGroupAcl(targetPath: string, groupName: string, permissions: string, log?: Logger): void;
26
+ export declare function addUserAcl(targetPath: string, userName: string, permissions: string, log?: Logger): void;
27
27
  /**
28
- * Remove all ACL entries for a group from a path.
28
+ * Remove all ACL entries for a user from a path.
29
29
  *
30
- * Reads current ACL entries via `ls -le`, finds entries matching the group,
31
- * and removes them by index (highest-first so indices stay valid).
30
+ * Reads current ACL entries via `ls -le`, finds entries matching the user
31
+ * (both allow and deny), and removes them by index (highest-first so indices
32
+ * stay valid). This ensures a clean slate before reapplying permissions.
32
33
  */
33
- export declare function removeGroupAcl(targetPath: string, groupName: string, log?: Logger): void;
34
+ export declare function removeUserAcl(targetPath: string, userName: string, log?: Logger): void;
34
35
  /**
35
36
  * Synchronise filesystem policy ACLs after a config change.
36
37
  *
37
- * Compares old and new policy arrays, and for filesystem-target policies:
38
- * - Removed policies revoke ACLs for each pattern
39
- * - Added policies → apply ACLs for each pattern
40
- * - Changed policies → revoke old patterns, apply new ones
38
+ * For every path in the union of old and new ACL maps:
39
+ * 1. Remove all existing user ACLs (clean slate)
40
+ * 2. Reapply permissions if the path is in the new map
41
+ *
42
+ * This "wipe then reapply" strategy avoids stale permission accumulation
43
+ * and the deny+allow conflict where layering ACLs produces wrong results.
41
44
  */
42
- export declare function syncFilesystemPolicyAcls(oldPolicies: PolicyConfig[], newPolicies: PolicyConfig[], groupName: string, logger?: Logger): void;
45
+ export declare function syncFilesystemPolicyAcls(oldPolicies: PolicyConfig[], newPolicies: PolicyConfig[], userName: string, logger?: Logger): void;
43
46
  export {};
44
47
  //# sourceMappingURL=acl.d.ts.map
package/acl.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../src/acl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAC7C;AAoBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAe3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CASjE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAE,MAAa,GAAG,IAAI,CAahH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAa,GAAG,IAAI,CAoC9F;AAmED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,YAAY,EAAE,EAC3B,WAAW,EAAE,YAAY,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CAoBN"}
1
+ {"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../src/acl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAC7C;AAoBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAe3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CASjE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAE,MAAa,GAAG,IAAI,CAa9G;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAa,GAAG,IAAI,CAqC5F;AAmED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,YAAY,EAAE,EAC3B,WAAW,EAAE,YAAY,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CAsBN"}
package/index.js CHANGED
@@ -975,21 +975,21 @@ function operationsToAclPerms(operations) {
975
975
  }
976
976
  return perms.join(",");
977
977
  }
978
- function addGroupAcl(targetPath, groupName, permissions, log = noop) {
978
+ function addUserAcl(targetPath, userName, permissions, log = noop) {
979
979
  try {
980
980
  if (!fs4.existsSync(targetPath)) {
981
981
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
982
982
  return;
983
983
  }
984
984
  execSync2(
985
- `sudo chmod +a "group:${groupName} allow ${permissions}" "${targetPath}"`,
985
+ `sudo chmod +a "user:${userName} allow ${permissions}" "${targetPath}"`,
986
986
  { stdio: "pipe" }
987
987
  );
988
988
  } catch (err) {
989
989
  log.warn(`[acl] failed to add ACL on ${targetPath}: ${err.message}`);
990
990
  }
991
991
  }
992
- function removeGroupAcl(targetPath, groupName, log = noop) {
992
+ function removeUserAcl(targetPath, userName, log = noop) {
993
993
  try {
994
994
  if (!fs4.existsSync(targetPath)) {
995
995
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
@@ -1001,8 +1001,8 @@ function removeGroupAcl(targetPath, groupName, log = noop) {
1001
1001
  });
1002
1002
  const indices = [];
1003
1003
  for (const line of output.split("\n")) {
1004
- const match = line.match(/^\s*(\d+):\s+group:(\S+)\s+allow/);
1005
- if (match && match[2] === groupName) {
1004
+ const match = line.match(/^\s*(\d+):\s+user:(\S+)\s+/);
1005
+ if (match && match[2] === userName) {
1006
1006
  indices.push(Number(match[1]));
1007
1007
  }
1008
1008
  }
@@ -1065,20 +1065,20 @@ function computeAclMap(policies) {
1065
1065
  }
1066
1066
  return aclMap;
1067
1067
  }
1068
- function syncFilesystemPolicyAcls(oldPolicies, newPolicies, groupName, logger) {
1068
+ function syncFilesystemPolicyAcls(oldPolicies, newPolicies, userName, logger) {
1069
1069
  const log = logger ?? noop;
1070
1070
  const oldFs = oldPolicies.filter((p) => p.target === "filesystem");
1071
1071
  const newFs = newPolicies.filter((p) => p.target === "filesystem");
1072
1072
  const oldAclMap = computeAclMap(oldFs);
1073
1073
  const newAclMap = computeAclMap(newFs);
1074
- for (const oldPath of oldAclMap.keys()) {
1075
- if (!newAclMap.has(oldPath)) {
1076
- removeGroupAcl(oldPath, groupName, log);
1074
+ const allPaths = /* @__PURE__ */ new Set([...oldAclMap.keys(), ...newAclMap.keys()]);
1075
+ for (const targetPath of allPaths) {
1076
+ removeUserAcl(targetPath, userName, log);
1077
+ const newPerms = newAclMap.get(targetPath);
1078
+ if (newPerms) {
1079
+ addUserAcl(targetPath, userName, newPerms, log);
1077
1080
  }
1078
1081
  }
1079
- for (const [targetPath, perms] of newAclMap) {
1080
- addGroupAcl(targetPath, groupName, perms, log);
1081
- }
1082
1082
  }
1083
1083
 
1084
1084
  // libs/shield-daemon/src/routes/config.ts
@@ -1100,9 +1100,9 @@ async function configRoutes(app) {
1100
1100
  const updated = updateConfig(request.body);
1101
1101
  if (request.body.policies) {
1102
1102
  const state = loadState();
1103
- const wsGroup = state.groups.find((g) => g.type === "workspace");
1104
- if (wsGroup) {
1105
- syncFilesystemPolicyAcls(oldPolicies, updated.policies, wsGroup.name, app.log);
1103
+ const agentUser = state.users.find((u) => u.type === "agent");
1104
+ if (agentUser) {
1105
+ syncFilesystemPolicyAcls(oldPolicies, updated.policies, agentUser.username, app.log);
1106
1106
  }
1107
1107
  syncCommandPoliciesAndWrappers(updated.policies, state, app.log);
1108
1108
  }
@@ -1125,9 +1125,9 @@ async function configRoutes(app) {
1125
1125
  try {
1126
1126
  const oldConfig = loadConfig();
1127
1127
  const state = loadState();
1128
- const wsGroup = state.groups.find((g) => g.type === "workspace");
1129
- if (wsGroup) {
1130
- syncFilesystemPolicyAcls(oldConfig.policies, [], wsGroup.name, app.log);
1128
+ const agentUser = state.users.find((u) => u.type === "agent");
1129
+ if (agentUser) {
1130
+ syncFilesystemPolicyAcls(oldConfig.policies, [], agentUser.username, app.log);
1131
1131
  }
1132
1132
  syncCommandPoliciesAndWrappers([], state, app.log);
1133
1133
  saveConfig(getDefaultConfig());
package/main.js CHANGED
@@ -966,21 +966,21 @@ function operationsToAclPerms(operations) {
966
966
  }
967
967
  return perms.join(",");
968
968
  }
969
- function addGroupAcl(targetPath, groupName, permissions, log = noop) {
969
+ function addUserAcl(targetPath, userName, permissions, log = noop) {
970
970
  try {
971
971
  if (!fs4.existsSync(targetPath)) {
972
972
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
973
973
  return;
974
974
  }
975
975
  execSync2(
976
- `sudo chmod +a "group:${groupName} allow ${permissions}" "${targetPath}"`,
976
+ `sudo chmod +a "user:${userName} allow ${permissions}" "${targetPath}"`,
977
977
  { stdio: "pipe" }
978
978
  );
979
979
  } catch (err) {
980
980
  log.warn(`[acl] failed to add ACL on ${targetPath}: ${err.message}`);
981
981
  }
982
982
  }
983
- function removeGroupAcl(targetPath, groupName, log = noop) {
983
+ function removeUserAcl(targetPath, userName, log = noop) {
984
984
  try {
985
985
  if (!fs4.existsSync(targetPath)) {
986
986
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
@@ -992,8 +992,8 @@ function removeGroupAcl(targetPath, groupName, log = noop) {
992
992
  });
993
993
  const indices = [];
994
994
  for (const line of output.split("\n")) {
995
- const match = line.match(/^\s*(\d+):\s+group:(\S+)\s+allow/);
996
- if (match && match[2] === groupName) {
995
+ const match = line.match(/^\s*(\d+):\s+user:(\S+)\s+/);
996
+ if (match && match[2] === userName) {
997
997
  indices.push(Number(match[1]));
998
998
  }
999
999
  }
@@ -1056,20 +1056,20 @@ function computeAclMap(policies) {
1056
1056
  }
1057
1057
  return aclMap;
1058
1058
  }
1059
- function syncFilesystemPolicyAcls(oldPolicies, newPolicies, groupName, logger) {
1059
+ function syncFilesystemPolicyAcls(oldPolicies, newPolicies, userName, logger) {
1060
1060
  const log = logger ?? noop;
1061
1061
  const oldFs = oldPolicies.filter((p) => p.target === "filesystem");
1062
1062
  const newFs = newPolicies.filter((p) => p.target === "filesystem");
1063
1063
  const oldAclMap = computeAclMap(oldFs);
1064
1064
  const newAclMap = computeAclMap(newFs);
1065
- for (const oldPath of oldAclMap.keys()) {
1066
- if (!newAclMap.has(oldPath)) {
1067
- removeGroupAcl(oldPath, groupName, log);
1065
+ const allPaths = /* @__PURE__ */ new Set([...oldAclMap.keys(), ...newAclMap.keys()]);
1066
+ for (const targetPath of allPaths) {
1067
+ removeUserAcl(targetPath, userName, log);
1068
+ const newPerms = newAclMap.get(targetPath);
1069
+ if (newPerms) {
1070
+ addUserAcl(targetPath, userName, newPerms, log);
1068
1071
  }
1069
1072
  }
1070
- for (const [targetPath, perms] of newAclMap) {
1071
- addGroupAcl(targetPath, groupName, perms, log);
1072
- }
1073
1073
  }
1074
1074
 
1075
1075
  // libs/shield-daemon/src/routes/config.ts
@@ -1091,9 +1091,9 @@ async function configRoutes(app) {
1091
1091
  const updated = updateConfig(request.body);
1092
1092
  if (request.body.policies) {
1093
1093
  const state = loadState();
1094
- const wsGroup = state.groups.find((g) => g.type === "workspace");
1095
- if (wsGroup) {
1096
- syncFilesystemPolicyAcls(oldPolicies, updated.policies, wsGroup.name, app.log);
1094
+ const agentUser = state.users.find((u) => u.type === "agent");
1095
+ if (agentUser) {
1096
+ syncFilesystemPolicyAcls(oldPolicies, updated.policies, agentUser.username, app.log);
1097
1097
  }
1098
1098
  syncCommandPoliciesAndWrappers(updated.policies, state, app.log);
1099
1099
  }
@@ -1116,9 +1116,9 @@ async function configRoutes(app) {
1116
1116
  try {
1117
1117
  const oldConfig = loadConfig();
1118
1118
  const state = loadState();
1119
- const wsGroup = state.groups.find((g) => g.type === "workspace");
1120
- if (wsGroup) {
1121
- syncFilesystemPolicyAcls(oldConfig.policies, [], wsGroup.name, app.log);
1119
+ const agentUser = state.users.find((u) => u.type === "agent");
1120
+ if (agentUser) {
1121
+ syncFilesystemPolicyAcls(oldConfig.policies, [], agentUser.username, app.log);
1122
1122
  }
1123
1123
  syncCommandPoliciesAndWrappers([], state, app.log);
1124
1124
  saveConfig(getDefaultConfig());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenshield/daemon",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "type": "module",
5
5
  "description": "AgenShield HTTP daemon server with embedded UI",
6
6
  "main": "./index.js",
@@ -24,9 +24,9 @@
24
24
  ],
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
- "@agenshield/ipc": "0.6.1",
28
- "@agenshield/broker": "0.6.1",
29
- "@agenshield/sandbox": "0.6.1",
27
+ "@agenshield/ipc": "0.6.2",
28
+ "@agenshield/broker": "0.6.2",
29
+ "@agenshield/sandbox": "0.6.2",
30
30
  "@modelcontextprotocol/sdk": "^1.26.0",
31
31
  "fastify": "^5.7.0",
32
32
  "zod": "^4.3.6",