@astrasyncai/verification-gateway 3.0.0 → 3.2.0
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/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +145 -93
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +145 -93
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +29 -11
- package/dist/adapters/mcp.d.ts +29 -11
- package/dist/adapters/mcp.js +43 -102
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +43 -102
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +126 -56
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +126 -56
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +25 -14
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +25 -14
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +3 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +3 -0
- package/dist/agent/index.mjs.map +1 -1
- package/dist/browser/background.js +18 -21
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +18 -21
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +18 -21
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +18 -21
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/{express-CrfwoNAR.d.ts → express-BowlMHQF.d.ts} +1 -1
- package/dist/{express-ienhAXps.d.mts → express-CeoSdOAZ.d.mts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +18 -21
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +18 -21
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-CEg_WG6y.d.mts → index-B51W8gn8.d.mts} +1 -1
- package/dist/{index-DC5f8eoQ.d.ts → index-DBmlycVm.d.ts} +1 -1
- package/dist/{index-B5e2IDWU.d.mts → index-DtGziFEm.d.mts} +1 -1
- package/dist/{index-CCdZxvAr.d.ts → index-DzXXBuLm.d.ts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +209 -191
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +209 -191
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/{nextjs-DSpisQst.d.mts → nextjs-BW1rzr1I.d.mts} +1 -1
- package/dist/{nextjs-66R1KW8e.d.ts → nextjs-V_K0qlAQ.d.ts} +1 -1
- package/dist/{sdk-5U_CBRpr.d.mts → sdk-ZYgI7G9f.d.ts} +14 -3
- package/dist/{sdk-Bm8np66n.d.ts → sdk-e5jg7sqW.d.mts} +14 -3
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +10 -0
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +10 -0
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-CgDCUfo8.d.mts → types-BNiLZY0i.d.mts} +1 -1
- package/dist/{types-R5N4ET6x.d.ts → types-DJi-u3fz.d.ts} +1 -1
- package/dist/{types-B3USs-Kx.d.mts → types-rFh4VMH4.d.mts} +30 -2
- package/dist/{types-B3USs-Kx.d.ts → types-rFh4VMH4.d.ts} +30 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -192,7 +192,7 @@ function getCapabilities(accessLevel) {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// src/version.ts
|
|
195
|
-
var SDK_VERSION = "3.
|
|
195
|
+
var SDK_VERSION = "3.2.0";
|
|
196
196
|
|
|
197
197
|
// src/well-known.ts
|
|
198
198
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -297,7 +297,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
var verificationCache = /* @__PURE__ */ new Map();
|
|
300
|
-
function getCacheKey(request) {
|
|
300
|
+
function getCacheKey(request, counterpartyId) {
|
|
301
301
|
const c = request.credentials;
|
|
302
302
|
return [
|
|
303
303
|
c.astraId || "",
|
|
@@ -310,6 +310,14 @@ function getCacheKey(request) {
|
|
|
310
310
|
request.jurisdiction || "",
|
|
311
311
|
request.transactionValue ?? "",
|
|
312
312
|
request.currency || "",
|
|
313
|
+
// SECURITY (cross-merchant cache leak): the merchant identity is sent via
|
|
314
|
+
// `config.counterpartyId`, NOT on the request, so it was previously absent
|
|
315
|
+
// from the key — two verifies for the SAME agent/purpose/action/value but
|
|
316
|
+
// DIFFERENT merchants collided, and a grant at a permissive merchant (low
|
|
317
|
+
// trust floor) was served for a stricter one. Same bug class as the
|
|
318
|
+
// duration omission (F-A1-07). counterpartyId affects the backend verdict
|
|
319
|
+
// (trust floor / per-route policy), so it MUST key the cache.
|
|
320
|
+
counterpartyId || "",
|
|
313
321
|
request.counterpartyUrl || "",
|
|
314
322
|
request.counterpartyType || "",
|
|
315
323
|
request.isSubAgentRequest ? "1" : "0",
|
|
@@ -333,8 +341,8 @@ function getCacheKey(request) {
|
|
|
333
341
|
request.callerMetadata?.agentCardUrl || ""
|
|
334
342
|
].join("|");
|
|
335
343
|
}
|
|
336
|
-
function getCachedResult(request) {
|
|
337
|
-
const key = getCacheKey(request);
|
|
344
|
+
function getCachedResult(request, counterpartyId) {
|
|
345
|
+
const key = getCacheKey(request, counterpartyId);
|
|
338
346
|
const cached = verificationCache.get(key);
|
|
339
347
|
if (cached && cached.expiresAt > Date.now()) {
|
|
340
348
|
return cached.result;
|
|
@@ -346,9 +354,9 @@ function getCachedResult(request) {
|
|
|
346
354
|
}
|
|
347
355
|
var DEFAULT_AUTONOMOUS_TTL_SECONDS = 60;
|
|
348
356
|
var DEFAULT_STEP_UP_TTL_SECONDS = 300;
|
|
349
|
-
function cacheResult(request, result, configuredTtl) {
|
|
357
|
+
function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
350
358
|
const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : result.requiresStepUp ? DEFAULT_STEP_UP_TTL_SECONDS : DEFAULT_AUTONOMOUS_TTL_SECONDS;
|
|
351
|
-
const key = getCacheKey(request);
|
|
359
|
+
const key = getCacheKey(request, counterpartyId);
|
|
352
360
|
verificationCache.set(key, {
|
|
353
361
|
result,
|
|
354
362
|
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
@@ -546,7 +554,7 @@ async function verify(config, request) {
|
|
|
546
554
|
);
|
|
547
555
|
}
|
|
548
556
|
if (mergedConfig.cacheTtl !== 0) {
|
|
549
|
-
const cached = getCachedResult(request);
|
|
557
|
+
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
550
558
|
if (cached) {
|
|
551
559
|
if (mergedConfig.debug) {
|
|
552
560
|
console.log("[VerificationGateway] Returning cached result");
|
|
@@ -598,8 +606,8 @@ async function verify(config, request) {
|
|
|
598
606
|
verifiedAt: /* @__PURE__ */ new Date(),
|
|
599
607
|
// Extract sessionId so decisions can be recorded for denials too
|
|
600
608
|
sessionId: apiResponse.sessionId,
|
|
601
|
-
//
|
|
602
|
-
//
|
|
609
|
+
// Anonymous traffic has no session → correlationId is the per-attempt
|
|
610
|
+
// linking key (the sessionId-equivalent for anonymous callers).
|
|
603
611
|
correlationId: apiResponse.correlationId,
|
|
604
612
|
recommendation: apiResponse.recommendation,
|
|
605
613
|
recommendationReasons: apiResponse.recommendationReasons
|
|
@@ -673,17 +681,14 @@ async function verify(config, request) {
|
|
|
673
681
|
};
|
|
674
682
|
} else if (result.recommendation === "step_up_required") {
|
|
675
683
|
result.requiresStepUp = true;
|
|
676
|
-
if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY["read-only"]) {
|
|
677
|
-
result.accessLevel = "read-only";
|
|
678
|
-
}
|
|
679
684
|
result.denialReasons = result.recommendationReasons || ["Step-up verification required"];
|
|
680
685
|
}
|
|
681
686
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny") {
|
|
682
|
-
cacheResult(request, result, mergedConfig.cacheTtl);
|
|
687
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
683
688
|
}
|
|
684
689
|
return result;
|
|
685
690
|
}
|
|
686
|
-
async function recordDecision(config, sessionId, decision, reason
|
|
691
|
+
async function recordDecision(config, sessionId, decision, reason) {
|
|
687
692
|
const headers = { "Content-Type": "application/json" };
|
|
688
693
|
if (config.apiKey) {
|
|
689
694
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
@@ -692,36 +697,7 @@ async function recordDecision(config, sessionId, decision, reason, override) {
|
|
|
692
697
|
await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
|
|
693
698
|
method: "POST",
|
|
694
699
|
headers,
|
|
695
|
-
body: JSON.stringify({
|
|
696
|
-
decision,
|
|
697
|
-
reason,
|
|
698
|
-
...override && {
|
|
699
|
-
overriddenBy: override.overriddenBy,
|
|
700
|
-
toolName: override.toolName,
|
|
701
|
-
requestedLevel: override.requestedLevel,
|
|
702
|
-
grantedLevel: override.grantedLevel
|
|
703
|
-
}
|
|
704
|
-
})
|
|
705
|
-
}).catch(() => {
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
async function recordAnonymousLocalOverride(config, correlationId, override, reason) {
|
|
709
|
-
const headers = { "Content-Type": "application/json" };
|
|
710
|
-
if (config.apiKey) {
|
|
711
|
-
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
712
|
-
headers["X-API-Key"] = config.apiKey;
|
|
713
|
-
}
|
|
714
|
-
await fetch(`${config.apiBaseUrl}/agents/verify-access/local-override`, {
|
|
715
|
-
method: "POST",
|
|
716
|
-
headers,
|
|
717
|
-
body: JSON.stringify({
|
|
718
|
-
correlationId,
|
|
719
|
-
reason,
|
|
720
|
-
overriddenBy: override.overriddenBy,
|
|
721
|
-
toolName: override.toolName,
|
|
722
|
-
requestedLevel: override.requestedLevel,
|
|
723
|
-
grantedLevel: override.grantedLevel
|
|
724
|
-
})
|
|
700
|
+
body: JSON.stringify({ decision, reason })
|
|
725
701
|
}).catch(() => {
|
|
726
702
|
});
|
|
727
703
|
}
|
|
@@ -787,6 +763,9 @@ function setHttpHeaders(headers, credentials) {
|
|
|
787
763
|
if (credentials.pdlss?.purpose) {
|
|
788
764
|
const purposeValue = credentials.pdlss.purpose.action ? `${credentials.pdlss.purpose.category}:${credentials.pdlss.purpose.action}` : credentials.pdlss.purpose.category;
|
|
789
765
|
result[`${HEADER_PREFIX}Purpose`] = purposeValue;
|
|
766
|
+
if (credentials.pdlss.purpose.action) {
|
|
767
|
+
result[`${HEADER_PREFIX}Action`] = credentials.pdlss.purpose.action;
|
|
768
|
+
}
|
|
790
769
|
}
|
|
791
770
|
if (credentials.pdlss?.duration?.maxSessionDuration) {
|
|
792
771
|
result[`${HEADER_PREFIX}Duration`] = String(credentials.pdlss.duration.maxSessionDuration);
|
|
@@ -816,6 +795,13 @@ function extractHttpCredentials(headers) {
|
|
|
816
795
|
purpose: { category, action }
|
|
817
796
|
};
|
|
818
797
|
}
|
|
798
|
+
const astraAction = getValue(`${HEADER_PREFIX}Action`) ?? getValue("x-astra-action");
|
|
799
|
+
if (astraAction) {
|
|
800
|
+
credentials.pdlss = {
|
|
801
|
+
...credentials.pdlss,
|
|
802
|
+
purpose: { category: credentials.pdlss?.purpose?.category ?? "", action: astraAction }
|
|
803
|
+
};
|
|
804
|
+
}
|
|
819
805
|
const duration = getValue(`${HEADER_PREFIX}Duration`) ?? getValue("x-astra-duration");
|
|
820
806
|
if (duration) {
|
|
821
807
|
credentials.pdlss = {
|
|
@@ -833,6 +819,85 @@ function extractHttpCredentials(headers) {
|
|
|
833
819
|
return credentials;
|
|
834
820
|
}
|
|
835
821
|
|
|
822
|
+
// src/adapters/http-pdlss.ts
|
|
823
|
+
var HTTP_METHOD_ACTION_TABLE = {
|
|
824
|
+
GET: "data.read",
|
|
825
|
+
HEAD: "data.read",
|
|
826
|
+
OPTIONS: "data.read",
|
|
827
|
+
POST: "data.write",
|
|
828
|
+
PUT: "data.write",
|
|
829
|
+
PATCH: "data.write",
|
|
830
|
+
DELETE: "data.delete"
|
|
831
|
+
};
|
|
832
|
+
var DEFAULT_HTTP_ACTION = "data.write";
|
|
833
|
+
var DEFAULT_HTTP_PURPOSE = "data";
|
|
834
|
+
function actionForHttpMethod(method) {
|
|
835
|
+
return HTTP_METHOD_ACTION_TABLE[method.toUpperCase()] ?? DEFAULT_HTTP_ACTION;
|
|
836
|
+
}
|
|
837
|
+
function normalizePurposeHeader(value) {
|
|
838
|
+
const colon = value.indexOf(":");
|
|
839
|
+
if (colon >= 0) {
|
|
840
|
+
return { purpose: value.slice(0, colon) };
|
|
841
|
+
}
|
|
842
|
+
const dot = value.indexOf(".");
|
|
843
|
+
if (dot > 0 && dot < value.length - 1) {
|
|
844
|
+
return { purpose: value.slice(0, dot), actionCandidate: value };
|
|
845
|
+
}
|
|
846
|
+
return { purpose: value };
|
|
847
|
+
}
|
|
848
|
+
function resolveHttpPdlss(input) {
|
|
849
|
+
const fromHeader = input.astraPurpose ? normalizePurposeHeader(input.astraPurpose) : void 0;
|
|
850
|
+
let action;
|
|
851
|
+
let actionSource;
|
|
852
|
+
if (input.routeAction) {
|
|
853
|
+
action = input.routeAction;
|
|
854
|
+
actionSource = "route_config";
|
|
855
|
+
} else if (input.hasCustomActionExtractor && input.customAction) {
|
|
856
|
+
action = input.customAction;
|
|
857
|
+
actionSource = "custom_extractor";
|
|
858
|
+
} else if (!input.hasCustomActionExtractor && input.astraAction) {
|
|
859
|
+
action = input.astraAction;
|
|
860
|
+
actionSource = "header";
|
|
861
|
+
} else if (!input.hasCustomActionExtractor && fromHeader?.actionCandidate) {
|
|
862
|
+
action = fromHeader.actionCandidate;
|
|
863
|
+
actionSource = "purpose_header_derived";
|
|
864
|
+
} else {
|
|
865
|
+
action = actionForHttpMethod(input.method);
|
|
866
|
+
actionSource = "method_table";
|
|
867
|
+
}
|
|
868
|
+
let purpose;
|
|
869
|
+
let purposeSource;
|
|
870
|
+
if (input.routePurpose) {
|
|
871
|
+
purpose = input.routePurpose;
|
|
872
|
+
purposeSource = "route_config";
|
|
873
|
+
} else if (input.hasCustomPurposeExtractor) {
|
|
874
|
+
if (input.customPurpose) {
|
|
875
|
+
purpose = input.customPurpose;
|
|
876
|
+
purposeSource = "custom_extractor";
|
|
877
|
+
}
|
|
878
|
+
} else if (fromHeader) {
|
|
879
|
+
purpose = fromHeader.purpose;
|
|
880
|
+
purposeSource = "header";
|
|
881
|
+
} else if (input.legacyPurpose) {
|
|
882
|
+
purpose = input.legacyPurpose;
|
|
883
|
+
purposeSource = "legacy_header";
|
|
884
|
+
} else if (input.queryPurpose) {
|
|
885
|
+
purpose = input.queryPurpose;
|
|
886
|
+
purposeSource = "query";
|
|
887
|
+
}
|
|
888
|
+
if (!purpose) {
|
|
889
|
+
const dot = action.indexOf(".");
|
|
890
|
+
if (dot > 0) {
|
|
891
|
+
purpose = action.slice(0, dot);
|
|
892
|
+
purposeSource = "action_derived";
|
|
893
|
+
} else {
|
|
894
|
+
purpose = DEFAULT_HTTP_PURPOSE;
|
|
895
|
+
purposeSource = "transport_default";
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return { purpose, action, purposeSource, actionSource };
|
|
899
|
+
}
|
|
900
|
+
|
|
836
901
|
// src/pdlss-pre-check.ts
|
|
837
902
|
function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
838
903
|
const failures = [];
|
|
@@ -891,33 +956,25 @@ function defaultExtractCredentials(req) {
|
|
|
891
956
|
function extractAstraSyncCredentials(req) {
|
|
892
957
|
return extractHttpCredentials(req.headers);
|
|
893
958
|
}
|
|
894
|
-
function
|
|
895
|
-
|
|
896
|
-
if (
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
case "PUT":
|
|
914
|
-
case "PATCH":
|
|
915
|
-
return "write_data";
|
|
916
|
-
case "DELETE":
|
|
917
|
-
return "delete_data";
|
|
918
|
-
default:
|
|
919
|
-
return "general";
|
|
920
|
-
}
|
|
959
|
+
function headerValue(value) {
|
|
960
|
+
if (typeof value === "string") return value;
|
|
961
|
+
if (Array.isArray(value)) return value[0];
|
|
962
|
+
return void 0;
|
|
963
|
+
}
|
|
964
|
+
function resolveRequestPdlss(req, routeConfig, customExtractPurpose, customExtractAction) {
|
|
965
|
+
return resolveHttpPdlss({
|
|
966
|
+
method: req.method,
|
|
967
|
+
astraPurpose: headerValue(req.headers["x-astra-purpose"]),
|
|
968
|
+
astraAction: headerValue(req.headers["x-astra-action"]),
|
|
969
|
+
legacyPurpose: headerValue(req.headers["x-purpose"] ?? req.headers["X-Purpose"]),
|
|
970
|
+
queryPurpose: typeof req.query.purpose === "string" ? req.query.purpose : void 0,
|
|
971
|
+
routePurpose: routeConfig?.purpose,
|
|
972
|
+
routeAction: routeConfig?.action,
|
|
973
|
+
hasCustomPurposeExtractor: !!customExtractPurpose,
|
|
974
|
+
customPurpose: customExtractPurpose?.(req),
|
|
975
|
+
hasCustomActionExtractor: !!customExtractAction,
|
|
976
|
+
customAction: customExtractAction?.(req)
|
|
977
|
+
});
|
|
921
978
|
}
|
|
922
979
|
function matchRoute(pattern, path, opts) {
|
|
923
980
|
const regexPattern = pattern.replace(/\*/g, ".*").replace(/\//g, "\\/");
|
|
@@ -977,6 +1034,7 @@ function createMiddleware(options) {
|
|
|
977
1034
|
const {
|
|
978
1035
|
extractCredentials: customExtractCredentials,
|
|
979
1036
|
extractPurpose: customExtractPurpose,
|
|
1037
|
+
extractAction: customExtractAction,
|
|
980
1038
|
skipPaths = [],
|
|
981
1039
|
onDenied = defaultOnDenied,
|
|
982
1040
|
recordDecisions,
|
|
@@ -1062,7 +1120,21 @@ function createMiddleware(options) {
|
|
|
1062
1120
|
}
|
|
1063
1121
|
return next();
|
|
1064
1122
|
}
|
|
1065
|
-
const
|
|
1123
|
+
const pdlssPair = resolveRequestPdlss(
|
|
1124
|
+
req,
|
|
1125
|
+
routeConfig,
|
|
1126
|
+
customExtractPurpose,
|
|
1127
|
+
customExtractAction
|
|
1128
|
+
);
|
|
1129
|
+
const purpose = pdlssPair.purpose;
|
|
1130
|
+
if (config.debug) {
|
|
1131
|
+
console.debug("[express-middleware] pdlss resolved", {
|
|
1132
|
+
purpose_source: pdlssPair.purposeSource,
|
|
1133
|
+
resolved_purpose: pdlssPair.purpose,
|
|
1134
|
+
action_source: pdlssPair.actionSource,
|
|
1135
|
+
resolved_action: pdlssPair.action
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1066
1138
|
const astraCreds = extractAstraSyncCredentials(req);
|
|
1067
1139
|
const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}`;
|
|
1068
1140
|
const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
|
|
@@ -1106,10 +1178,7 @@ function createMiddleware(options) {
|
|
|
1106
1178
|
const result = await verify(config, {
|
|
1107
1179
|
credentials,
|
|
1108
1180
|
purpose,
|
|
1109
|
-
|
|
1110
|
-
// Backend evaluator tolerates either case as defense-in-depth
|
|
1111
|
-
// (round-18.6 batch 2); SDK emits canonical form.
|
|
1112
|
-
action: req.method.toUpperCase(),
|
|
1181
|
+
action: pdlssPair.action,
|
|
1113
1182
|
resource: req.path,
|
|
1114
1183
|
createSession: shouldRecordDecisions,
|
|
1115
1184
|
counterpartyUrl,
|
|
@@ -1147,35 +1216,12 @@ function createMiddleware(options) {
|
|
|
1147
1216
|
}
|
|
1148
1217
|
return next();
|
|
1149
1218
|
}
|
|
1150
|
-
if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {
|
|
1151
|
-
const insufficientFailure = {
|
|
1152
|
-
dimension: "access_level.insufficient",
|
|
1153
|
-
message: `Endpoint requires accessLevel '${routeConfig.minAccessLevel}'; agent has '${result.accessLevel}'.`,
|
|
1154
|
-
guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
|
|
1155
|
-
};
|
|
1156
|
-
result.failures = [...result.failures ?? [], insufficientFailure];
|
|
1157
|
-
result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
|
|
1158
|
-
if (!result.guidance && wellKnownUrls) {
|
|
1159
|
-
result.guidance = {
|
|
1160
|
-
message: insufficientFailure.message,
|
|
1161
|
-
registrationUrl: wellKnownUrls.registrationUrl,
|
|
1162
|
-
documentationUrl: wellKnownUrls.documentationUrl
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
if (shouldRecordDecisions && sessionId) {
|
|
1166
|
-
recordDecision(config, sessionId, "denied", insufficientFailure.message).catch(() => {
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
dedupeFailures(result);
|
|
1170
|
-
onDenied(result, req, res);
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
1219
|
if (routeConfig.minTrustScore && result.agent) {
|
|
1174
1220
|
if (result.agent.trustScore < routeConfig.minTrustScore) {
|
|
1175
1221
|
const trustFailure = {
|
|
1176
|
-
dimension: "
|
|
1177
|
-
message:
|
|
1178
|
-
guidance: "
|
|
1222
|
+
dimension: "endpoint.trust",
|
|
1223
|
+
message: "Trust below the route requirement for this endpoint.",
|
|
1224
|
+
guidance: "Trust is below this route's floor. Trust is not overridable \u2014 the agent either meets the endpoint's trust policy or it doesn't. Raise the agent's trust via real signals (KYD, blockchain registration, agent-card), or have the operator lower the route's minTrustScore."
|
|
1179
1225
|
};
|
|
1180
1226
|
result.failures = [...result.failures ?? [], trustFailure];
|
|
1181
1227
|
result.denialReasons = [trustFailure.message];
|
|
@@ -1307,28 +1353,15 @@ function extractAstraSyncCredentialsFromNextRequest(request) {
|
|
|
1307
1353
|
});
|
|
1308
1354
|
return extractHttpCredentials(headers);
|
|
1309
1355
|
}
|
|
1310
|
-
function
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1319
|
-
switch (request.method.toUpperCase()) {
|
|
1320
|
-
case "GET":
|
|
1321
|
-
return "read_data";
|
|
1322
|
-
case "POST":
|
|
1323
|
-
return "write_data";
|
|
1324
|
-
case "PUT":
|
|
1325
|
-
case "PATCH":
|
|
1326
|
-
return "write_data";
|
|
1327
|
-
case "DELETE":
|
|
1328
|
-
return "delete_data";
|
|
1329
|
-
default:
|
|
1330
|
-
return "general";
|
|
1331
|
-
}
|
|
1356
|
+
function resolveNextPdlss(request, routeConfig) {
|
|
1357
|
+
return resolveHttpPdlss({
|
|
1358
|
+
method: request.method,
|
|
1359
|
+
astraPurpose: request.headers.get("x-astra-purpose") ?? void 0,
|
|
1360
|
+
astraAction: request.headers.get("x-astra-action") ?? void 0,
|
|
1361
|
+
legacyPurpose: request.headers.get("x-purpose") ?? void 0,
|
|
1362
|
+
routePurpose: routeConfig?.purpose,
|
|
1363
|
+
routeAction: routeConfig?.action
|
|
1364
|
+
});
|
|
1332
1365
|
}
|
|
1333
1366
|
function generateCommerceShieldHtml(result, options) {
|
|
1334
1367
|
const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
|
|
@@ -1541,7 +1574,16 @@ function createMiddleware2(options) {
|
|
|
1541
1574
|
}
|
|
1542
1575
|
const credentials = extractCredentialsFromNextRequest(request);
|
|
1543
1576
|
const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;
|
|
1544
|
-
const
|
|
1577
|
+
const pdlssPair = resolveNextPdlss(request, routeConfig);
|
|
1578
|
+
const purpose = pdlssPair.purpose;
|
|
1579
|
+
if (config.debug) {
|
|
1580
|
+
console.debug("[nextjs-middleware] pdlss resolved", {
|
|
1581
|
+
purpose_source: pdlssPair.purposeSource,
|
|
1582
|
+
resolved_purpose: pdlssPair.purpose,
|
|
1583
|
+
action_source: pdlssPair.actionSource,
|
|
1584
|
+
resolved_action: pdlssPair.action
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1545
1587
|
const astraCreds = extractAstraSyncCredentialsFromNextRequest(request);
|
|
1546
1588
|
const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
|
|
1547
1589
|
if (preCheckFailures.length > 0) {
|
|
@@ -1595,10 +1637,7 @@ function createMiddleware2(options) {
|
|
|
1595
1637
|
const result = await verify(config, {
|
|
1596
1638
|
credentials,
|
|
1597
1639
|
purpose,
|
|
1598
|
-
|
|
1599
|
-
// Backend evaluator tolerates either case as defense-in-depth
|
|
1600
|
-
// (round-18.6 batch 2); SDK emits canonical form.
|
|
1601
|
-
action: request.method.toUpperCase(),
|
|
1640
|
+
action: pdlssPair.action,
|
|
1602
1641
|
resource: pathname,
|
|
1603
1642
|
counterpartyUrl,
|
|
1604
1643
|
counterpartyType: config.counterpartyType || "website",
|
|
@@ -1613,7 +1652,7 @@ function createMiddleware2(options) {
|
|
|
1613
1652
|
agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0
|
|
1614
1653
|
}
|
|
1615
1654
|
});
|
|
1616
|
-
if (!result.identityVerified || !result.policyAllowed
|
|
1655
|
+
if (!result.identityVerified || !result.policyAllowed) {
|
|
1617
1656
|
if (pathname.startsWith("/api/")) {
|
|
1618
1657
|
return NextResponse.json(
|
|
1619
1658
|
{
|
|
@@ -1621,10 +1660,8 @@ function createMiddleware2(options) {
|
|
|
1621
1660
|
error: {
|
|
1622
1661
|
// Round-18 G4: 401 → identity missing (re-auth); 403 → identity
|
|
1623
1662
|
// OK, policy denied (update PDLSS / step up).
|
|
1624
|
-
code: !result.identityVerified ? "UNAUTHORIZED" : "
|
|
1663
|
+
code: !result.identityVerified ? "UNAUTHORIZED" : "POLICY_DENIED",
|
|
1625
1664
|
message: result.denialReasons?.[0] || "Access denied",
|
|
1626
|
-
accessLevel: result.accessLevel,
|
|
1627
|
-
required: routeConfig.minAccessLevel,
|
|
1628
1665
|
guidance: result.guidance
|
|
1629
1666
|
}
|
|
1630
1667
|
},
|
|
@@ -1652,7 +1689,6 @@ function createMiddleware2(options) {
|
|
|
1652
1689
|
response.headers.set("X-AstraSync-Access-Level", result.accessLevel);
|
|
1653
1690
|
if (result.agent) {
|
|
1654
1691
|
response.headers.set("X-AstraSync-Agent-Id", result.agent.astraId);
|
|
1655
|
-
response.headers.set("X-AstraSync-Trust-Score", result.agent.trustScore.toString());
|
|
1656
1692
|
}
|
|
1657
1693
|
return response;
|
|
1658
1694
|
};
|
|
@@ -1726,7 +1762,13 @@ var VerificationGatewayClient = class {
|
|
|
1726
1762
|
return this.executeWithRetry(() => quickVerify(this.config, credentials));
|
|
1727
1763
|
}
|
|
1728
1764
|
/**
|
|
1729
|
-
* Check if an agent has a specific access level
|
|
1765
|
+
* Check if an agent has a specific access level.
|
|
1766
|
+
*
|
|
1767
|
+
* @deprecated 3.2.0 — the access-level band is informational only; it no
|
|
1768
|
+
* longer gates in the middleware adapters (post-3.1.0 feedback #1). This
|
|
1769
|
+
* explicit opt-in query still works, but prefer gating on the server-side
|
|
1770
|
+
* policy decision (identity + policy + trust) or per-route `minTrustScore`.
|
|
1771
|
+
* Explicit per-route condition→constraint rules are the Phase-2 successor.
|
|
1730
1772
|
*/
|
|
1731
1773
|
async hasAccess(credentials, requiredLevel) {
|
|
1732
1774
|
const result = await this.quickVerify(credentials);
|
|
@@ -3353,9 +3395,9 @@ function toBuf(bytes) {
|
|
|
3353
3395
|
new Uint8Array(out).set(bytes);
|
|
3354
3396
|
return out;
|
|
3355
3397
|
}
|
|
3356
|
-
function checkTimestamp(
|
|
3357
|
-
if (!
|
|
3358
|
-
const ts = parseTimestamp(
|
|
3398
|
+
function checkTimestamp(headerValue2, toleranceSec, nowFn) {
|
|
3399
|
+
if (!headerValue2) return { ok: false, error: "missing Timestamp header" };
|
|
3400
|
+
const ts = parseTimestamp(headerValue2);
|
|
3359
3401
|
if (ts === null) return { ok: false, error: "unparseable Timestamp header" };
|
|
3360
3402
|
const now = nowFn ? nowFn() : Math.floor(Date.now() / 1e3);
|
|
3361
3403
|
if (Math.abs(now - ts) > toleranceSec) {
|
|
@@ -3587,14 +3629,14 @@ function verifyMPP(input) {
|
|
|
3587
3629
|
var import_schemas = require("@x402/core/schemas");
|
|
3588
3630
|
var import_utils = require("@x402/core/utils");
|
|
3589
3631
|
function extractX402FromRequest(request) {
|
|
3590
|
-
const
|
|
3632
|
+
const headerValue2 = readHeader4(request.headers, "x-payment");
|
|
3591
3633
|
if (request.body && typeof request.body === "object") {
|
|
3592
3634
|
const parsed = tryParsePayload(request.body);
|
|
3593
3635
|
if (parsed) return buildPayloadContext(parsed, "body");
|
|
3594
3636
|
}
|
|
3595
|
-
if (
|
|
3637
|
+
if (headerValue2) {
|
|
3596
3638
|
try {
|
|
3597
|
-
const decoded = (0, import_utils.safeBase64Decode)(
|
|
3639
|
+
const decoded = (0, import_utils.safeBase64Decode)(headerValue2);
|
|
3598
3640
|
if (decoded) {
|
|
3599
3641
|
const json = JSON.parse(decoded);
|
|
3600
3642
|
const parsed = tryParsePayload(json);
|
|
@@ -3617,10 +3659,10 @@ function extractX402FromResponse(response) {
|
|
|
3617
3659
|
const parsed = tryParseRequired(response.body);
|
|
3618
3660
|
if (parsed) return buildRequiredContext(parsed, "body");
|
|
3619
3661
|
}
|
|
3620
|
-
const
|
|
3621
|
-
if (
|
|
3662
|
+
const headerValue2 = readHeader4(response.headers, "x-payment-required");
|
|
3663
|
+
if (headerValue2) {
|
|
3622
3664
|
try {
|
|
3623
|
-
const decoded = (0, import_utils.safeBase64Decode)(
|
|
3665
|
+
const decoded = (0, import_utils.safeBase64Decode)(headerValue2);
|
|
3624
3666
|
if (decoded) {
|
|
3625
3667
|
const json = JSON.parse(decoded);
|
|
3626
3668
|
const parsed = tryParseRequired(json);
|
|
@@ -4456,7 +4498,10 @@ function mcpToPdlss(parsed, requestPath, headerPurpose, headerAction, toolGate)
|
|
|
4456
4498
|
}
|
|
4457
4499
|
let action;
|
|
4458
4500
|
let actionSource;
|
|
4459
|
-
if (
|
|
4501
|
+
if (toolGate?.action !== void 0) {
|
|
4502
|
+
action = toolGate.action;
|
|
4503
|
+
actionSource = "tool_gate";
|
|
4504
|
+
} else if (headerAction) {
|
|
4460
4505
|
action = headerAction;
|
|
4461
4506
|
actionSource = "header";
|
|
4462
4507
|
} else if (parsed.actionFromBody && parsed.actionSourceFromBody) {
|
|
@@ -4567,7 +4612,6 @@ function createMcpMiddleware(options) {
|
|
|
4567
4612
|
return next();
|
|
4568
4613
|
}
|
|
4569
4614
|
req.mcpRequest = parsed;
|
|
4570
|
-
const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
|
|
4571
4615
|
const headerRaw = req.headers["x-astra-id"] ?? req.headers["x-astra-agentid"];
|
|
4572
4616
|
const headerAstraId = typeof headerRaw === "string" ? headerRaw : Array.isArray(headerRaw) ? headerRaw[0] : void 0;
|
|
4573
4617
|
const bodyAstraId = parsed.agentIdFromBody;
|
|
@@ -4604,7 +4648,7 @@ function createMcpMiddleware(options) {
|
|
|
4604
4648
|
return next();
|
|
4605
4649
|
}
|
|
4606
4650
|
}
|
|
4607
|
-
const { level: minAccessLevel
|
|
4651
|
+
const { level: minAccessLevel } = resolveMinAccessLevel(parsed, {
|
|
4608
4652
|
toolGates,
|
|
4609
4653
|
methodGates
|
|
4610
4654
|
});
|
|
@@ -4630,7 +4674,7 @@ function createMcpMiddleware(options) {
|
|
|
4630
4674
|
req.path,
|
|
4631
4675
|
headerPurpose,
|
|
4632
4676
|
headerAction,
|
|
4633
|
-
gate ? { purpose: gate.purpose, resource: gate.resource } : void 0
|
|
4677
|
+
gate ? { purpose: gate.purpose, action: gate.action, resource: gate.resource } : void 0
|
|
4634
4678
|
);
|
|
4635
4679
|
if (config.debug) {
|
|
4636
4680
|
console.debug("[mcp-middleware] pdlss resolved", {
|
|
@@ -4640,6 +4684,23 @@ function createMcpMiddleware(options) {
|
|
|
4640
4684
|
resolved_action: pdlss.action
|
|
4641
4685
|
});
|
|
4642
4686
|
}
|
|
4687
|
+
if (!pdlss.purpose) {
|
|
4688
|
+
const id = req.body?.id ?? null;
|
|
4689
|
+
res.status(400).json({
|
|
4690
|
+
jsonrpc: "2.0",
|
|
4691
|
+
id,
|
|
4692
|
+
error: {
|
|
4693
|
+
code: -32602,
|
|
4694
|
+
message: "PDLSS_PURPOSE_REQUIRED",
|
|
4695
|
+
data: {
|
|
4696
|
+
dimension: "pdlss.purpose",
|
|
4697
|
+
detail: "This tool is access-gated but the call declared no PDLSS purpose. Supply a bare-category purpose via the X-Astra-Purpose header or params._meta.astrasync.purpose, or have the merchant set the tool\u2019s purpose in its toolGate config.",
|
|
4698
|
+
resolvedAction: pdlss.action
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
});
|
|
4702
|
+
return;
|
|
4703
|
+
}
|
|
4643
4704
|
const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
|
|
4644
4705
|
const shouldRecordDecisions = recordDecisions !== false;
|
|
4645
4706
|
const result = await verify(config, {
|
|
@@ -4663,7 +4724,6 @@ function createMcpMiddleware(options) {
|
|
|
4663
4724
|
});
|
|
4664
4725
|
req.agentVerification = result;
|
|
4665
4726
|
const sessionId = result.sessionId;
|
|
4666
|
-
const correlationId = result.correlationId;
|
|
4667
4727
|
if (!result.identityVerified || !result.policyAllowed) {
|
|
4668
4728
|
if (shouldRecordDecisions && sessionId) {
|
|
4669
4729
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
@@ -4684,48 +4744,6 @@ function createMcpMiddleware(options) {
|
|
|
4684
4744
|
}
|
|
4685
4745
|
return next();
|
|
4686
4746
|
}
|
|
4687
|
-
if (!hasMinimumAccess(result.accessLevel, minAccessLevel)) {
|
|
4688
|
-
const insufficientFailure = {
|
|
4689
|
-
dimension: "access_level.insufficient",
|
|
4690
|
-
message: `Tool requires accessLevel '${minAccessLevel}'; agent has '${result.accessLevel}'.`,
|
|
4691
|
-
guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
|
|
4692
|
-
};
|
|
4693
|
-
result.failures = [...result.failures ?? [], insufficientFailure];
|
|
4694
|
-
result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
|
|
4695
|
-
if (!result.guidance && wellKnownUrls) {
|
|
4696
|
-
result.guidance = {
|
|
4697
|
-
message: insufficientFailure.message,
|
|
4698
|
-
registrationUrl: wellKnownUrls.registrationUrl,
|
|
4699
|
-
documentationUrl: wellKnownUrls.documentationUrl
|
|
4700
|
-
};
|
|
4701
|
-
}
|
|
4702
|
-
if (shouldRecordDecisions) {
|
|
4703
|
-
const overrideKind = gateSource === "toolGate" ? "toolGate" : gateSource === "methodGate" ? "methodGate" : "other";
|
|
4704
|
-
const override = {
|
|
4705
|
-
overriddenBy: overrideKind,
|
|
4706
|
-
...parsed.toolName && { toolName: parsed.toolName },
|
|
4707
|
-
requestedLevel: minAccessLevel,
|
|
4708
|
-
grantedLevel: result.accessLevel
|
|
4709
|
-
};
|
|
4710
|
-
if (sessionId) {
|
|
4711
|
-
recordDecision(config, sessionId, "denied", result.denialReasons?.[0], override).catch(
|
|
4712
|
-
() => {
|
|
4713
|
-
}
|
|
4714
|
-
);
|
|
4715
|
-
} else if (correlationId) {
|
|
4716
|
-
recordAnonymousLocalOverride(
|
|
4717
|
-
config,
|
|
4718
|
-
correlationId,
|
|
4719
|
-
override,
|
|
4720
|
-
result.denialReasons?.[0]
|
|
4721
|
-
).catch(() => {
|
|
4722
|
-
});
|
|
4723
|
-
}
|
|
4724
|
-
}
|
|
4725
|
-
dedupeFailures2(result);
|
|
4726
|
-
onDenied(result, req, res);
|
|
4727
|
-
return;
|
|
4728
|
-
}
|
|
4729
4747
|
if (effectiveAstraId) {
|
|
4730
4748
|
res.setHeader(
|
|
4731
4749
|
MCP_VERIFIED_HOP_HEADER,
|