@suveren/gateway 0.36.0 → 0.36.1

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.
@@ -6,7 +6,7 @@
6
6
  "icon": "briefcase",
7
7
  "profile": "sales",
8
8
  "npmPackage": "@humanagencyp/erp-mcp",
9
- "npmVersion": "0.9.2",
9
+ "npmVersion": "0.9.4",
10
10
  "mcp": {
11
11
  "command": "erp-mcp",
12
12
  "args": [],
@@ -113,11 +113,13 @@
113
113
  "requiredValue": "unlimited"
114
114
  },
115
115
  "find_customers": {
116
+ "readAlsoUnder": ["invoicing"],
116
117
  "category": "read",
117
118
  "boundField": "read_access",
118
119
  "requiredValue": "unlimited"
119
120
  },
120
121
  "get_customer": {
122
+ "readAlsoUnder": ["invoicing"],
121
123
  "category": "read",
122
124
  "boundField": "read_access",
123
125
  "requiredValue": "unlimited"
@@ -3,6 +3,13 @@ import {
3
3
  setPromptOverride
4
4
  } from "./chunk-3BNWN6VP.mjs";
5
5
 
6
+ // src/lib/no-foreign-framing.ts
7
+ function noForeignFraming(_req, res, next) {
8
+ res.setHeader("X-Frame-Options", "SAMEORIGIN");
9
+ res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");
10
+ next();
11
+ }
12
+
6
13
  // src/index.ts
7
14
  import { randomBytes as randomBytes3 } from "crypto";
8
15
  import { existsSync as existsSync13, readFileSync as readFileSync18, renameSync as renameSync6 } from "fs";
@@ -5398,6 +5405,7 @@ var shownSnapshotStore = new ShownSnapshotStore(DATA_DIR);
5398
5405
  var internalSecret2 = process.env.SUVEREN_INTERNAL_SECRET ?? randomBytes3(32).toString("hex");
5399
5406
  setInternalSecret(internalSecret2);
5400
5407
  var app = express();
5408
+ app.use(noForeignFraming);
5401
5409
  var jsonParser = express.json();
5402
5410
  var loginAttempts = /* @__PURE__ */ new Map();
5403
5411
  var LOGIN_WINDOW_MS = 6e4;
@@ -4067,6 +4067,20 @@ function invalidMessageReason(toolName, entry) {
4067
4067
  }
4068
4068
  return null;
4069
4069
  }
4070
+ function invalidManifestReadAlsoUnderReason(manifest) {
4071
+ const overrides = manifest.toolGating?.overrides ?? {};
4072
+ for (const [toolName, entry] of Object.entries(overrides)) {
4073
+ const extra = entry?.readAlsoUnder;
4074
+ if (extra === void 0) continue;
4075
+ const at = `override "${toolName}".readAlsoUnder`;
4076
+ if (!Array.isArray(extra) || extra.length === 0 || !extra.every((p) => typeof p === "string" && p.length > 0)) {
4077
+ return `${at} must be a non-empty list of area names`;
4078
+ }
4079
+ if (entry?.category !== "read") return `${at} is allowed on read tools only (category "read")`;
4080
+ if (entry?.read !== void 0) return `${at} cannot be combined with a read adapter (age window)`;
4081
+ }
4082
+ return null;
4083
+ }
4070
4084
  function invalidManifestMessageReason(manifest) {
4071
4085
  const overrides = manifest.toolGating?.overrides ?? {};
4072
4086
  for (const [toolName, entry] of Object.entries(overrides)) {
@@ -4504,6 +4518,11 @@ function loadManifests(integrationsDir) {
4504
4518
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${internalReason}`);
4505
4519
  continue;
4506
4520
  }
4521
+ const readAlsoReason = invalidManifestReadAlsoUnderReason(manifest);
4522
+ if (readAlsoReason) {
4523
+ console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${readAlsoReason}`);
4524
+ continue;
4525
+ }
4507
4526
  const messageReason = invalidManifestMessageReason(manifest);
4508
4527
  if (messageReason) {
4509
4528
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${messageReason}`);
@@ -6354,9 +6373,10 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
6354
6373
  };
6355
6374
  }
6356
6375
  const { profile, executionMapping, staticExecution, category, boundField: toolBoundField, requiredValue: toolRequiredValue } = tool.gating;
6376
+ const toolAreas = category === "read" ? [profile, ...tool.gating.readAlsoUnder ?? []] : [profile];
6357
6377
  const eligibleMandates = () => {
6358
6378
  const refusals = [];
6359
- const matching = state2.getEnrichedAuthorizations().filter((a) => a.complete && profileMatches(a.profileId, profile)).filter((a) => {
6379
+ const matching = state2.getEnrichedAuthorizations().filter((a) => a.complete && toolAreas.some((p) => profileMatches(a.profileId, p))).filter((a) => {
6360
6380
  const refusal = integrationManager2.mandateRefusal?.(tool, a) ?? null;
6361
6381
  if (refusal) refusals.push(refusal);
6362
6382
  return !refusal;
@@ -7124,7 +7144,8 @@ function buildProxiedToolDescription(tool) {
7124
7144
  return `[Suveren: no gating config] ${tool.description}`;
7125
7145
  }
7126
7146
  const profile = tool.gating.profile;
7127
- const condition = `works only under a ${shortProfile(profile)} mandate, check list-authorizations`;
7147
+ const areas = tool.gating.category === "read" ? [shortProfile(profile), ...(tool.gating.readAlsoUnder ?? []).map(shortProfile)] : [shortProfile(profile)];
7148
+ const condition = `works only under a ${areas.join(" or ")} mandate, check list-authorizations`;
7128
7149
  if (tool.gating.category === "read") {
7129
7150
  return `[Suveren: ${profile} \u2014 read; ${condition}] ${tool.description}`;
7130
7151
  }
@@ -7342,7 +7363,8 @@ function listedWithoutMandate(authorizations, integrationManager2) {
7342
7363
  for (const tool of integrationManager2.getAllTools()) {
7343
7364
  if (!toolAlwaysListed(tool, isBuiltin, builtinAlwaysListed)) continue;
7344
7365
  const profile = tool.gating.profile;
7345
- if (authorizations.some((a) => a.complete && profileMatches(a.profileId, profile))) continue;
7366
+ const areas = tool.gating.category === "read" ? [profile, ...tool.gating.readAlsoUnder ?? []] : [profile];
7367
+ if (authorizations.some((a) => a.complete && areas.some((p) => profileMatches(a.profileId, p)))) continue;
7346
7368
  const short = shortProfileName2(profile);
7347
7369
  byProfile.set(short, [...byProfile.get(short) ?? [], tool.namespacedName]);
7348
7370
  }
@@ -9541,7 +9563,9 @@ var IntegrationManager = class {
9541
9563
  hideUnlessAuthorized: ext.hideUnlessAuthorized,
9542
9564
  simulationOnly: ext.simulationOnly,
9543
9565
  approvalView: ext.approvalView,
9544
- internal: ext.internal
9566
+ internal: ext.internal,
9567
+ // Further areas whose mandates may authorize this read (manifest).
9568
+ readAlsoUnder: ext.readAlsoUnder
9545
9569
  };
9546
9570
  }
9547
9571
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suveren/gateway",
3
- "version": "0.36.0",
3
+ "version": "0.36.1",
4
4
  "description": "Suveren gateway — local agent gateway built in compliance with the Human Agency Protocol (HAP). Runs the UI, control plane, and MCP server in one Node process.",
5
5
  "type": "module",
6
6
  "main": "server.js",