@astrasyncai/verification-gateway 5.16.0 → 5.17.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.
- package/README.md +94 -18
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +33 -5
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +33 -5
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.d.ts +1 -1
- package/dist/adapters/mcp.js +24 -5
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +24 -5
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +24 -5
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +24 -5
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +1 -1
- package/dist/adapters/sdk.d.ts +1 -1
- package/dist/adapters/sdk.js +5 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +5 -1
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +5 -1
- package/dist/bin/astrasync-codex-hook.js +5 -1
- package/dist/bin/astrasync-guard.js +447 -25
- package/dist/bin/astrasync.js +470 -28
- package/dist/browser/background.js +5 -1
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +5 -1
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +5 -1
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +5 -1
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +5 -1
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +5 -1
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +5 -1
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +5 -1
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +5 -1
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +5 -1
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -5
- package/dist/index.mjs.map +1 -1
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.js +1 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-Bm1QvGqI.d.ts → types-DA8LzAOB.d.ts} +6 -0
- package/dist/{types-Blz520bR.d.mts → types-DRb_plOh.d.mts} +14 -0
- package/dist/{types-Blz520bR.d.ts → types-DRb_plOh.d.ts} +14 -0
- package/dist/{types-DBe4hgfT.d.mts → types-vNBvYD7Z.d.mts} +6 -0
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +5 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +5 -1
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin/astrasync.js
CHANGED
|
@@ -36,7 +36,7 @@ var SDK_VERSION;
|
|
|
36
36
|
var init_version = __esm({
|
|
37
37
|
"src/version.ts"() {
|
|
38
38
|
"use strict";
|
|
39
|
-
SDK_VERSION = "5.
|
|
39
|
+
SDK_VERSION = "5.17.1";
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -49,10 +49,10 @@ function withUserAgent(initHeaders) {
|
|
|
49
49
|
return copy;
|
|
50
50
|
}
|
|
51
51
|
if (Array.isArray(initHeaders)) {
|
|
52
|
-
const has2 = initHeaders.some(([
|
|
52
|
+
const has2 = initHeaders.some(([name2]) => name2.toLowerCase() === "user-agent");
|
|
53
53
|
return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
|
|
54
54
|
}
|
|
55
|
-
const has = Object.keys(initHeaders).some((
|
|
55
|
+
const has = Object.keys(initHeaders).some((name2) => name2.toLowerCase() === "user-agent");
|
|
56
56
|
return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
|
|
57
57
|
}
|
|
58
58
|
var SDK_USER_AGENT, sdkFetch;
|
|
@@ -611,9 +611,9 @@ function requireType() {
|
|
|
611
611
|
}
|
|
612
612
|
function Type2(tag, options) {
|
|
613
613
|
options = options || {};
|
|
614
|
-
Object.keys(options).forEach(function(
|
|
615
|
-
if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(
|
|
616
|
-
throw new YAMLException2('Unknown option "' +
|
|
614
|
+
Object.keys(options).forEach(function(name2) {
|
|
615
|
+
if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name2) === -1) {
|
|
616
|
+
throw new YAMLException2('Unknown option "' + name2 + '" is met in definition of "' + tag + '" YAML type.');
|
|
617
617
|
}
|
|
618
618
|
});
|
|
619
619
|
this.options = options;
|
|
@@ -644,9 +644,9 @@ function requireSchema() {
|
|
|
644
644
|
hasRequiredSchema = 1;
|
|
645
645
|
const YAMLException2 = requireException();
|
|
646
646
|
const Type2 = requireType();
|
|
647
|
-
function compileList(schema2,
|
|
647
|
+
function compileList(schema2, name2) {
|
|
648
648
|
const result = [];
|
|
649
|
-
schema2[
|
|
649
|
+
schema2[name2].forEach(function(currentType) {
|
|
650
650
|
let newIndex = result.length;
|
|
651
651
|
result.forEach(function(previousType, previousIndex) {
|
|
652
652
|
if (previousType.tag === currentType.tag && previousType.kind === currentType.kind && previousType.multi === currentType.multi) {
|
|
@@ -1520,18 +1520,18 @@ function requireLoader() {
|
|
|
1520
1520
|
state.onWarning.call(null, generateError(state, message));
|
|
1521
1521
|
}
|
|
1522
1522
|
}
|
|
1523
|
-
function storeAnchor(state,
|
|
1523
|
+
function storeAnchor(state, name2, value) {
|
|
1524
1524
|
const transactions = state.anchorMapTransactions;
|
|
1525
1525
|
if (transactions.length !== 0) {
|
|
1526
1526
|
const transaction = transactions[transactions.length - 1];
|
|
1527
|
-
if (!_hasOwnProperty.call(transaction,
|
|
1528
|
-
transaction[
|
|
1529
|
-
existed: _hasOwnProperty.call(state.anchorMap,
|
|
1530
|
-
value: state.anchorMap[
|
|
1527
|
+
if (!_hasOwnProperty.call(transaction, name2)) {
|
|
1528
|
+
transaction[name2] = {
|
|
1529
|
+
existed: _hasOwnProperty.call(state.anchorMap, name2),
|
|
1530
|
+
value: state.anchorMap[name2]
|
|
1531
1531
|
};
|
|
1532
1532
|
}
|
|
1533
1533
|
}
|
|
1534
|
-
state.anchorMap[
|
|
1534
|
+
state.anchorMap[name2] = value;
|
|
1535
1535
|
}
|
|
1536
1536
|
function beginAnchorTransaction(state) {
|
|
1537
1537
|
state.anchorMapTransactions.push(/* @__PURE__ */ Object.create(null));
|
|
@@ -1543,9 +1543,9 @@ function requireLoader() {
|
|
|
1543
1543
|
const parent = transactions[transactions.length - 1];
|
|
1544
1544
|
const names = Object.keys(transaction);
|
|
1545
1545
|
for (let index = 0, length = names.length; index < length; index += 1) {
|
|
1546
|
-
const
|
|
1547
|
-
if (!_hasOwnProperty.call(parent,
|
|
1548
|
-
parent[
|
|
1546
|
+
const name2 = names[index];
|
|
1547
|
+
if (!_hasOwnProperty.call(parent, name2)) {
|
|
1548
|
+
parent[name2] = transaction[name2];
|
|
1549
1549
|
}
|
|
1550
1550
|
}
|
|
1551
1551
|
}
|
|
@@ -1586,7 +1586,7 @@ function requireLoader() {
|
|
|
1586
1586
|
state.result = snapshot.result;
|
|
1587
1587
|
}
|
|
1588
1588
|
const directiveHandlers = {
|
|
1589
|
-
YAML: function handleYamlDirective(state,
|
|
1589
|
+
YAML: function handleYamlDirective(state, name2, args) {
|
|
1590
1590
|
if (state.version !== null) {
|
|
1591
1591
|
throwError(state, "duplication of %YAML directive");
|
|
1592
1592
|
}
|
|
@@ -1608,7 +1608,7 @@ function requireLoader() {
|
|
|
1608
1608
|
throwWarning(state, "unsupported YAML version of the document");
|
|
1609
1609
|
}
|
|
1610
1610
|
},
|
|
1611
|
-
TAG: function handleTagDirective(state,
|
|
1611
|
+
TAG: function handleTagDirective(state, name2, args) {
|
|
1612
1612
|
let prefix;
|
|
1613
1613
|
if (args.length !== 2) {
|
|
1614
1614
|
throwError(state, "TAG directive accepts exactly two arguments");
|
|
@@ -3938,10 +3938,10 @@ ${category.label}
|
|
|
3938
3938
|
scope.blockedResources = additionalBlocked;
|
|
3939
3939
|
}
|
|
3940
3940
|
this.print("");
|
|
3941
|
-
const
|
|
3941
|
+
const name2 = (await this.ask("Policy name [default: local-guard]: ")).trim() || "local-guard";
|
|
3942
3942
|
const policy = {
|
|
3943
3943
|
version: "1.0",
|
|
3944
|
-
name,
|
|
3944
|
+
name: name2,
|
|
3945
3945
|
purposes,
|
|
3946
3946
|
...scope && { scope },
|
|
3947
3947
|
...limits && { limits },
|
|
@@ -4662,8 +4662,8 @@ function extractBacktickSubstitutions(text, collect) {
|
|
|
4662
4662
|
}
|
|
4663
4663
|
function basename(token) {
|
|
4664
4664
|
const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
|
|
4665
|
-
const
|
|
4666
|
-
return
|
|
4665
|
+
const name2 = slash === -1 ? token : token.slice(slash + 1);
|
|
4666
|
+
return name2.replace(/\.(exe|cmd|bat|ps1)$/i, "");
|
|
4667
4667
|
}
|
|
4668
4668
|
function matchSegment(segment, list) {
|
|
4669
4669
|
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
@@ -5170,6 +5170,7 @@ function getCacheKey(request2, counterpartyId) {
|
|
|
5170
5170
|
request2.enableRuntimeChallenge ? "1" : "0",
|
|
5171
5171
|
// 5.16.0: route policy changes the verdict (owners / challenge).
|
|
5172
5172
|
request2.routePolicy ? JSON.stringify(request2.routePolicy) : "",
|
|
5173
|
+
request2.approvalHeldByRoute ? "1" : "0",
|
|
5173
5174
|
// callerMetadata fields contribute to risk model; include the ones
|
|
5174
5175
|
// backend reads. sourceIp/userAgent/forwardedFor change per-request
|
|
5175
5176
|
// so their inclusion effectively forces a re-check for any varying
|
|
@@ -5341,6 +5342,7 @@ async function callVerifyAccessAPI(config, request2) {
|
|
|
5341
5342
|
if (requestData.enableRuntimeChallenge)
|
|
5342
5343
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
5343
5344
|
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
5345
|
+
if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
|
|
5344
5346
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
5345
5347
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
5346
5348
|
if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
|
|
@@ -5672,6 +5674,8 @@ async function verify(config, request2, options) {
|
|
|
5672
5674
|
} else if (result.recommendation === "step_up_required" && result.identityVerified) {
|
|
5673
5675
|
result.requiresStepUp = true;
|
|
5674
5676
|
result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
|
|
5677
|
+
const stepUpFailures = apiResponse.access?.failures;
|
|
5678
|
+
if (stepUpFailures?.length) result.failures = stepUpFailures;
|
|
5675
5679
|
}
|
|
5676
5680
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
5677
5681
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
@@ -7617,7 +7621,7 @@ function ensureGuardSelfProtection(policyPath) {
|
|
|
7617
7621
|
(0, import_fs5.writeFileSync)(policyPath, toYaml(parsed.policy), "utf-8");
|
|
7618
7622
|
return true;
|
|
7619
7623
|
}
|
|
7620
|
-
function buildDefaultClaudeCodePolicy(
|
|
7624
|
+
function buildDefaultClaudeCodePolicy(name2 = "claude-code-local-guard") {
|
|
7621
7625
|
const recommendedCommands = DEFAULT_BLOCKED_COMMANDS.filter((c) => c.recommended).map(
|
|
7622
7626
|
(c) => c.pattern
|
|
7623
7627
|
);
|
|
@@ -7625,7 +7629,7 @@ function buildDefaultClaudeCodePolicy(name = "claude-code-local-guard") {
|
|
|
7625
7629
|
const permissive = RISK_PRESETS.find((p) => p.name === "permissive");
|
|
7626
7630
|
return {
|
|
7627
7631
|
version: "1.0",
|
|
7628
|
-
name,
|
|
7632
|
+
name: name2,
|
|
7629
7633
|
description: "AstraSync Local Guard for Claude Code \u2014 default coding-agent policy. Edit freely; the hook re-reads this file on every tool call.",
|
|
7630
7634
|
purposes: [
|
|
7631
7635
|
{
|
|
@@ -8344,6 +8348,411 @@ var init_git_route = __esm({
|
|
|
8344
8348
|
}
|
|
8345
8349
|
});
|
|
8346
8350
|
|
|
8351
|
+
// src/fleet/local-cert.ts
|
|
8352
|
+
function len(n) {
|
|
8353
|
+
if (n < 128) return Buffer.from([n]);
|
|
8354
|
+
const bytes = [];
|
|
8355
|
+
while (n > 0) {
|
|
8356
|
+
bytes.unshift(n & 255);
|
|
8357
|
+
n >>= 8;
|
|
8358
|
+
}
|
|
8359
|
+
return Buffer.from([128 | bytes.length, ...bytes]);
|
|
8360
|
+
}
|
|
8361
|
+
function oid(dotted) {
|
|
8362
|
+
const parts = dotted.split(".").map(Number);
|
|
8363
|
+
const out = [parts[0] * 40 + parts[1]];
|
|
8364
|
+
for (const p of parts.slice(2)) {
|
|
8365
|
+
const enc = [p & 127];
|
|
8366
|
+
let v = p >> 7;
|
|
8367
|
+
while (v > 0) {
|
|
8368
|
+
enc.unshift(v & 127 | 128);
|
|
8369
|
+
v >>= 7;
|
|
8370
|
+
}
|
|
8371
|
+
out.push(...enc);
|
|
8372
|
+
}
|
|
8373
|
+
return tlv(6, Buffer.from(out));
|
|
8374
|
+
}
|
|
8375
|
+
function integer(buf) {
|
|
8376
|
+
const b = buf[0] & 128 ? Buffer.concat([Buffer.from([0]), buf]) : buf;
|
|
8377
|
+
return tlv(2, b);
|
|
8378
|
+
}
|
|
8379
|
+
function utcTime(d) {
|
|
8380
|
+
const s = d.toISOString().replace(/[-:T]/g, "").slice(2, 14) + "Z";
|
|
8381
|
+
return tlv(23, Buffer.from(s, "ascii"));
|
|
8382
|
+
}
|
|
8383
|
+
function name(cn) {
|
|
8384
|
+
return seq2(set2(seq2(oid("2.5.4.3"), tlv(12, Buffer.from(cn, "utf8")))));
|
|
8385
|
+
}
|
|
8386
|
+
function createLocalCert(hosts, days = 30) {
|
|
8387
|
+
const { privateKey, publicKey } = import_node_crypto5.default.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
8388
|
+
const spki = publicKey.export({ type: "spki", format: "der" });
|
|
8389
|
+
const now = Date.now();
|
|
8390
|
+
const san = seq2(...hosts.map((h) => tlv(130, Buffer.from(h, "ascii"))));
|
|
8391
|
+
const extensions = ctx(
|
|
8392
|
+
3,
|
|
8393
|
+
seq2(
|
|
8394
|
+
seq2(oid("2.5.29.17"), tlv(4, san)),
|
|
8395
|
+
// subjectAltName
|
|
8396
|
+
seq2(oid("2.5.29.19"), tlv(4, seq2()))
|
|
8397
|
+
// basicConstraints: not a CA
|
|
8398
|
+
)
|
|
8399
|
+
);
|
|
8400
|
+
const subject = name("AstraSync Local Guard (browse)");
|
|
8401
|
+
const tbs = seq2(
|
|
8402
|
+
ctx(0, integer(Buffer.from([2]))),
|
|
8403
|
+
// v3
|
|
8404
|
+
integer(import_node_crypto5.default.randomBytes(16)),
|
|
8405
|
+
ECDSA_SHA256,
|
|
8406
|
+
subject,
|
|
8407
|
+
seq2(utcTime(new Date(now - 6e4)), utcTime(new Date(now + days * 864e5))),
|
|
8408
|
+
subject,
|
|
8409
|
+
spki,
|
|
8410
|
+
extensions
|
|
8411
|
+
);
|
|
8412
|
+
const signature = import_node_crypto5.default.sign("sha256", tbs, privateKey);
|
|
8413
|
+
const der = seq2(tbs, ECDSA_SHA256, tlv(3, Buffer.concat([Buffer.from([0]), signature])));
|
|
8414
|
+
const b64 = der.toString("base64").match(/.{1,64}/g).join("\n");
|
|
8415
|
+
return {
|
|
8416
|
+
cert: `-----BEGIN CERTIFICATE-----
|
|
8417
|
+
${b64}
|
|
8418
|
+
-----END CERTIFICATE-----
|
|
8419
|
+
`,
|
|
8420
|
+
key: privateKey.export({ type: "pkcs8", format: "pem" }).toString()
|
|
8421
|
+
};
|
|
8422
|
+
}
|
|
8423
|
+
var import_node_crypto5, tlv, seq2, set2, ctx, ECDSA_SHA256;
|
|
8424
|
+
var init_local_cert = __esm({
|
|
8425
|
+
"src/fleet/local-cert.ts"() {
|
|
8426
|
+
"use strict";
|
|
8427
|
+
import_node_crypto5 = __toESM(require("crypto"));
|
|
8428
|
+
tlv = (tag, body) => Buffer.concat([Buffer.from([tag]), len(body.length), body]);
|
|
8429
|
+
seq2 = (...parts) => tlv(48, Buffer.concat(parts));
|
|
8430
|
+
set2 = (...parts) => tlv(49, Buffer.concat(parts));
|
|
8431
|
+
ctx = (n, body) => tlv(160 + n, body);
|
|
8432
|
+
ECDSA_SHA256 = seq2(oid("1.2.840.10045.4.3.2"));
|
|
8433
|
+
}
|
|
8434
|
+
});
|
|
8435
|
+
|
|
8436
|
+
// src/fleet/browse-proxy.ts
|
|
8437
|
+
function cleanHeaders(h) {
|
|
8438
|
+
const out = {};
|
|
8439
|
+
for (const [k, v] of Object.entries(h)) if (!HOP_BY_HOP.has(k) && v !== void 0) out[k] = v;
|
|
8440
|
+
return out;
|
|
8441
|
+
}
|
|
8442
|
+
async function startBrowseProxy(opts) {
|
|
8443
|
+
const hosts = new Set(opts.hosts.map((h) => h.toLowerCase()));
|
|
8444
|
+
const deviceHeader = `Device ${opts.deviceCredential}`;
|
|
8445
|
+
const { cert, key } = createLocalCert([...hosts]);
|
|
8446
|
+
function forward(req, res, target) {
|
|
8447
|
+
const headers = cleanHeaders(req.headers);
|
|
8448
|
+
headers["x-astra-device"] = deviceHeader;
|
|
8449
|
+
const mod = target.protocol === "https:" ? import_node_https.default : import_node_http.default;
|
|
8450
|
+
const upstream = mod.request(
|
|
8451
|
+
{
|
|
8452
|
+
host: target.host,
|
|
8453
|
+
port: target.port,
|
|
8454
|
+
method: req.method,
|
|
8455
|
+
path: target.path,
|
|
8456
|
+
headers,
|
|
8457
|
+
servername: target.protocol === "https:" ? String(req.headers.host ?? target.host).split(":")[0] : void 0,
|
|
8458
|
+
rejectUnauthorized: target.rejectUnauthorized ?? true
|
|
8459
|
+
},
|
|
8460
|
+
(up) => {
|
|
8461
|
+
res.writeHead(up.statusCode ?? 502, cleanHeaders(up.headers));
|
|
8462
|
+
up.pipe(res);
|
|
8463
|
+
}
|
|
8464
|
+
);
|
|
8465
|
+
upstream.on("error", () => {
|
|
8466
|
+
if (!res.headersSent) res.writeHead(502, { "content-type": "text/plain" });
|
|
8467
|
+
res.end("AstraSync browse proxy: upstream unreachable");
|
|
8468
|
+
});
|
|
8469
|
+
req.pipe(upstream);
|
|
8470
|
+
}
|
|
8471
|
+
function forwardUpgrade(req, socket, head, target) {
|
|
8472
|
+
const headers = { ...req.headers };
|
|
8473
|
+
delete headers["proxy-connection"];
|
|
8474
|
+
delete headers["proxy-authorization"];
|
|
8475
|
+
delete headers["x-astra-device"];
|
|
8476
|
+
if (target.addDevice) headers["x-astra-device"] = deviceHeader;
|
|
8477
|
+
const lines = [`${req.method} ${target.path} HTTP/1.1`];
|
|
8478
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
8479
|
+
if (v === void 0) continue;
|
|
8480
|
+
for (const one of Array.isArray(v) ? v : [v]) lines.push(`${k}: ${one}`);
|
|
8481
|
+
}
|
|
8482
|
+
const onConnect = () => {
|
|
8483
|
+
upstream.write(lines.join("\r\n") + "\r\n\r\n");
|
|
8484
|
+
if (head.length) upstream.write(head);
|
|
8485
|
+
upstream.pipe(socket);
|
|
8486
|
+
socket.pipe(upstream);
|
|
8487
|
+
};
|
|
8488
|
+
const upstream = target.protocol === "https:" ? import_node_tls.default.connect(
|
|
8489
|
+
{
|
|
8490
|
+
host: target.host,
|
|
8491
|
+
port: target.port,
|
|
8492
|
+
servername: String(req.headers.host ?? target.host).split(":")[0],
|
|
8493
|
+
rejectUnauthorized: target.rejectUnauthorized ?? true
|
|
8494
|
+
},
|
|
8495
|
+
onConnect
|
|
8496
|
+
) : import_node_net.default.connect(target.port, target.host, onConnect);
|
|
8497
|
+
upstream.on("error", () => socket.destroy());
|
|
8498
|
+
socket.on("error", () => upstream.destroy());
|
|
8499
|
+
}
|
|
8500
|
+
const inner = import_node_http.default.createServer((req, res) => {
|
|
8501
|
+
const hostHeader = String(req.headers.host ?? "");
|
|
8502
|
+
const [h, p] = hostHeader.split(":");
|
|
8503
|
+
const port2 = Number(p) || 443;
|
|
8504
|
+
const up = opts.upstreamFor?.(h, port2) ?? { host: h, port: port2 };
|
|
8505
|
+
forward(req, res, {
|
|
8506
|
+
protocol: "https:",
|
|
8507
|
+
host: up.host,
|
|
8508
|
+
port: up.port,
|
|
8509
|
+
path: req.url ?? "/",
|
|
8510
|
+
rejectUnauthorized: up.rejectUnauthorized
|
|
8511
|
+
});
|
|
8512
|
+
});
|
|
8513
|
+
const plainInner = import_node_http.default.createServer((req, res) => {
|
|
8514
|
+
const [h, p] = String(req.headers.host ?? "").split(":");
|
|
8515
|
+
forward(req, res, { protocol: "http:", host: h, port: Number(p) || 80, path: req.url ?? "/" });
|
|
8516
|
+
});
|
|
8517
|
+
plainInner.on("upgrade", (req, socket, head) => {
|
|
8518
|
+
const [h, p] = String(req.headers.host ?? "").split(":");
|
|
8519
|
+
forwardUpgrade(req, socket, head, {
|
|
8520
|
+
protocol: "http:",
|
|
8521
|
+
host: h,
|
|
8522
|
+
port: Number(p) || 80,
|
|
8523
|
+
path: req.url ?? "/",
|
|
8524
|
+
addDevice: true
|
|
8525
|
+
});
|
|
8526
|
+
});
|
|
8527
|
+
const server = import_node_http.default.createServer((req, res) => {
|
|
8528
|
+
let url;
|
|
8529
|
+
try {
|
|
8530
|
+
url = new URL(req.url ?? "");
|
|
8531
|
+
} catch {
|
|
8532
|
+
res.writeHead(400).end("absolute URL required");
|
|
8533
|
+
return;
|
|
8534
|
+
}
|
|
8535
|
+
if (hosts.has(url.hostname.toLowerCase())) {
|
|
8536
|
+
forward(req, res, {
|
|
8537
|
+
protocol: "http:",
|
|
8538
|
+
host: url.hostname,
|
|
8539
|
+
port: Number(url.port) || 80,
|
|
8540
|
+
path: `${url.pathname}${url.search}`
|
|
8541
|
+
});
|
|
8542
|
+
return;
|
|
8543
|
+
}
|
|
8544
|
+
const upstream = import_node_http.default.request(
|
|
8545
|
+
{
|
|
8546
|
+
host: url.hostname,
|
|
8547
|
+
port: Number(url.port) || 80,
|
|
8548
|
+
method: req.method,
|
|
8549
|
+
path: `${url.pathname}${url.search}`,
|
|
8550
|
+
headers: cleanHeaders(req.headers)
|
|
8551
|
+
},
|
|
8552
|
+
(up) => {
|
|
8553
|
+
res.writeHead(up.statusCode ?? 502, cleanHeaders(up.headers));
|
|
8554
|
+
up.pipe(res);
|
|
8555
|
+
}
|
|
8556
|
+
);
|
|
8557
|
+
upstream.on("error", () => res.writeHead(502).end());
|
|
8558
|
+
req.pipe(upstream);
|
|
8559
|
+
});
|
|
8560
|
+
inner.on("upgrade", (req, socket, head) => {
|
|
8561
|
+
const [h, p] = String(req.headers.host ?? "").split(":");
|
|
8562
|
+
const port2 = Number(p) || 443;
|
|
8563
|
+
const up = opts.upstreamFor?.(h, port2) ?? { host: h, port: port2 };
|
|
8564
|
+
forwardUpgrade(req, socket, head, {
|
|
8565
|
+
protocol: "https:",
|
|
8566
|
+
host: up.host,
|
|
8567
|
+
port: up.port,
|
|
8568
|
+
path: req.url ?? "/",
|
|
8569
|
+
addDevice: true,
|
|
8570
|
+
rejectUnauthorized: up.rejectUnauthorized
|
|
8571
|
+
});
|
|
8572
|
+
});
|
|
8573
|
+
server.on("upgrade", (req, socket, head) => {
|
|
8574
|
+
let url;
|
|
8575
|
+
try {
|
|
8576
|
+
url = new URL(req.url ?? "");
|
|
8577
|
+
} catch {
|
|
8578
|
+
socket.destroy();
|
|
8579
|
+
return;
|
|
8580
|
+
}
|
|
8581
|
+
forwardUpgrade(req, socket, head, {
|
|
8582
|
+
protocol: "http:",
|
|
8583
|
+
host: url.hostname,
|
|
8584
|
+
port: Number(url.port) || 80,
|
|
8585
|
+
path: `${url.pathname}${url.search}`,
|
|
8586
|
+
addDevice: hosts.has(url.hostname.toLowerCase())
|
|
8587
|
+
});
|
|
8588
|
+
});
|
|
8589
|
+
server.on("connect", (req, socket, head) => {
|
|
8590
|
+
const [host, portStr] = String(req.url ?? "").split(":");
|
|
8591
|
+
const port2 = Number(portStr) || 443;
|
|
8592
|
+
if (hosts.has(host.toLowerCase())) {
|
|
8593
|
+
socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
8594
|
+
const start = (first) => {
|
|
8595
|
+
const s = socket;
|
|
8596
|
+
s.pause();
|
|
8597
|
+
s.unshift(first);
|
|
8598
|
+
if (first[0] === 22) {
|
|
8599
|
+
const tlsSocket = new import_node_tls.default.TLSSocket(s, {
|
|
8600
|
+
isServer: true,
|
|
8601
|
+
secureContext: import_node_tls.default.createSecureContext({ cert, key })
|
|
8602
|
+
});
|
|
8603
|
+
inner.emit("connection", tlsSocket);
|
|
8604
|
+
tlsSocket.on("error", () => socket.destroy());
|
|
8605
|
+
} else {
|
|
8606
|
+
plainInner.emit("connection", s);
|
|
8607
|
+
s.resume();
|
|
8608
|
+
}
|
|
8609
|
+
};
|
|
8610
|
+
if (head.length) start(head);
|
|
8611
|
+
else socket.once("data", (chunk) => start(chunk));
|
|
8612
|
+
return;
|
|
8613
|
+
}
|
|
8614
|
+
const upstream = import_node_net.default.connect(port2, host, () => {
|
|
8615
|
+
socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
8616
|
+
if (head.length) upstream.write(head);
|
|
8617
|
+
upstream.pipe(socket);
|
|
8618
|
+
socket.pipe(upstream);
|
|
8619
|
+
});
|
|
8620
|
+
upstream.on("error", () => socket.destroy());
|
|
8621
|
+
socket.on("error", () => upstream.destroy());
|
|
8622
|
+
});
|
|
8623
|
+
await new Promise((resolve) => server.listen(opts.port ?? 0, "127.0.0.1", resolve));
|
|
8624
|
+
const port = server.address().port;
|
|
8625
|
+
return {
|
|
8626
|
+
port,
|
|
8627
|
+
close: () => new Promise((resolve) => {
|
|
8628
|
+
inner.close();
|
|
8629
|
+
plainInner.close();
|
|
8630
|
+
server.close(() => resolve());
|
|
8631
|
+
server.closeAllConnections?.();
|
|
8632
|
+
})
|
|
8633
|
+
};
|
|
8634
|
+
}
|
|
8635
|
+
var import_node_http, import_node_https, import_node_net, import_node_tls, HOP_BY_HOP;
|
|
8636
|
+
var init_browse_proxy = __esm({
|
|
8637
|
+
"src/fleet/browse-proxy.ts"() {
|
|
8638
|
+
"use strict";
|
|
8639
|
+
import_node_http = __toESM(require("http"));
|
|
8640
|
+
import_node_https = __toESM(require("https"));
|
|
8641
|
+
import_node_net = __toESM(require("net"));
|
|
8642
|
+
import_node_tls = __toESM(require("tls"));
|
|
8643
|
+
init_local_cert();
|
|
8644
|
+
HOP_BY_HOP = /* @__PURE__ */ new Set([
|
|
8645
|
+
"connection",
|
|
8646
|
+
"proxy-connection",
|
|
8647
|
+
"keep-alive",
|
|
8648
|
+
"transfer-encoding",
|
|
8649
|
+
"upgrade",
|
|
8650
|
+
"te",
|
|
8651
|
+
"trailer",
|
|
8652
|
+
"proxy-authorization"
|
|
8653
|
+
]);
|
|
8654
|
+
}
|
|
8655
|
+
});
|
|
8656
|
+
|
|
8657
|
+
// src/fleet/browse.ts
|
|
8658
|
+
function refusal(body) {
|
|
8659
|
+
const e = body.error;
|
|
8660
|
+
if (e && typeof e === "object")
|
|
8661
|
+
return { code: e.code ?? body.code, message: e.message ?? body.message };
|
|
8662
|
+
return { code: body.code, message: body.message ?? (typeof e === "string" ? e : void 0) };
|
|
8663
|
+
}
|
|
8664
|
+
async function runBrowse(paths, opts) {
|
|
8665
|
+
const log = opts.log ?? ((l) => console.log(l));
|
|
8666
|
+
const f = opts.fetchImpl ?? fetch;
|
|
8667
|
+
const loaded = loadEnrolment(paths);
|
|
8668
|
+
if (!loaded || !("enrolment" in loaded)) {
|
|
8669
|
+
throw new Error("This machine is not enrolled. Run: astrasync guard enroll <token>");
|
|
8670
|
+
}
|
|
8671
|
+
const { credential, apiBaseUrl } = loaded.enrolment;
|
|
8672
|
+
if (!opts.checkpoint || !opts.slug) throw new Error("--checkpoint and --slug are required");
|
|
8673
|
+
const appOrigin = new URL(apiBaseUrl).origin;
|
|
8674
|
+
const hosts = [new URL(apiBaseUrl).hostname, ...opts.extraHosts ?? []];
|
|
8675
|
+
const device = `Device ${credential}`;
|
|
8676
|
+
const proxy = await startBrowseProxy({ hosts, deviceCredential: credential, port: opts.port });
|
|
8677
|
+
try {
|
|
8678
|
+
const base2 = opts.checkpoint.replace(/\/+$/, "");
|
|
8679
|
+
const signin = await f(
|
|
8680
|
+
`${base2}/a/${encodeURIComponent(opts.slug)}/h/${encodeURIComponent(opts.upstream ?? "astrasync-api")}/agent-sessions`,
|
|
8681
|
+
{
|
|
8682
|
+
method: "POST",
|
|
8683
|
+
headers: { authorization: device, "content-type": "application/json" },
|
|
8684
|
+
body: "{}"
|
|
8685
|
+
}
|
|
8686
|
+
);
|
|
8687
|
+
const body = await signin.json().catch(() => ({}));
|
|
8688
|
+
if (signin.status !== 201 && signin.status !== 202) {
|
|
8689
|
+
const r = refusal(body);
|
|
8690
|
+
throw new Error(
|
|
8691
|
+
`Sign-in refused (${signin.status}${r.code ? ` ${r.code}` : ""}): ${r.message ?? "no detail"}`
|
|
8692
|
+
);
|
|
8693
|
+
}
|
|
8694
|
+
const sessionId = String(body.data?.sessionId ?? "");
|
|
8695
|
+
let code = body.data?.code;
|
|
8696
|
+
if (!code) {
|
|
8697
|
+
log("Waiting for the owner to approve this sign-in with their passkey\u2026");
|
|
8698
|
+
const deadline = Date.now() + (opts.waitMs ?? 10 * 6e4);
|
|
8699
|
+
let failures = 0;
|
|
8700
|
+
while (!code) {
|
|
8701
|
+
if (Date.now() > deadline) throw new Error("The owner did not approve in time.");
|
|
8702
|
+
await new Promise((r) => setTimeout(r, opts.pollIntervalMs ?? 3e3));
|
|
8703
|
+
let poll;
|
|
8704
|
+
try {
|
|
8705
|
+
poll = await f(`${apiBaseUrl.replace(/\/+$/, "")}/agent-sessions/${sessionId}/status`, {
|
|
8706
|
+
headers: { "x-astra-device": device }
|
|
8707
|
+
});
|
|
8708
|
+
} catch (err) {
|
|
8709
|
+
if (++failures >= 3)
|
|
8710
|
+
throw new Error(`Couldn't check the sign-in status: ${err.message}`);
|
|
8711
|
+
continue;
|
|
8712
|
+
}
|
|
8713
|
+
const p = await poll.json().catch(() => ({}));
|
|
8714
|
+
if (!poll.ok) {
|
|
8715
|
+
const r = refusal(p);
|
|
8716
|
+
const why = `(${poll.status}${r.code ? ` ${r.code}` : ""}): ${r.message ?? "no detail"}`;
|
|
8717
|
+
if (poll.status === 401 || poll.status === 403 || poll.status === 404)
|
|
8718
|
+
throw new Error(`Sign-in status check refused ${why}`);
|
|
8719
|
+
if (++failures >= 3) throw new Error(`Couldn't check the sign-in status ${why}`);
|
|
8720
|
+
continue;
|
|
8721
|
+
}
|
|
8722
|
+
failures = 0;
|
|
8723
|
+
const status = p.data?.status;
|
|
8724
|
+
if (status === "approved") code = p.data?.code;
|
|
8725
|
+
else if (status === "denied" || status === "ended")
|
|
8726
|
+
throw new Error(`Sign-in ${status === "denied" ? "was not approved" : "ended"}.`);
|
|
8727
|
+
}
|
|
8728
|
+
}
|
|
8729
|
+
const openUrl = `${appOrigin}/agent-session#code=${code}`;
|
|
8730
|
+
log("");
|
|
8731
|
+
log(`Signed in. Open this once (within 2 minutes), through the proxy:`);
|
|
8732
|
+
log(` ${openUrl}`);
|
|
8733
|
+
log("");
|
|
8734
|
+
log(
|
|
8735
|
+
`Device proxy: http://127.0.0.1:${proxy.port} (adds this machine's credential for ${hosts.join(", ")})`
|
|
8736
|
+
);
|
|
8737
|
+
log("Playwright:");
|
|
8738
|
+
log(
|
|
8739
|
+
` chromium.launch({ proxy: { server: 'http://127.0.0.1:${proxy.port}' } }) + newContext({ ignoreHTTPSErrors: true })`
|
|
8740
|
+
);
|
|
8741
|
+
log("Requests that skip the proxy end the session. Ctrl-C stops the proxy.");
|
|
8742
|
+
return { proxy, openUrl, sessionId };
|
|
8743
|
+
} catch (err) {
|
|
8744
|
+
await proxy.close();
|
|
8745
|
+
throw err;
|
|
8746
|
+
}
|
|
8747
|
+
}
|
|
8748
|
+
var init_browse = __esm({
|
|
8749
|
+
"src/fleet/browse.ts"() {
|
|
8750
|
+
"use strict";
|
|
8751
|
+
init_enrolment_store();
|
|
8752
|
+
init_browse_proxy();
|
|
8753
|
+
}
|
|
8754
|
+
});
|
|
8755
|
+
|
|
8347
8756
|
// src/cli/guard.ts
|
|
8348
8757
|
var guard_exports = {};
|
|
8349
8758
|
function createPrompt() {
|
|
@@ -8878,6 +9287,28 @@ async function cmdFleetStatus() {
|
|
|
8878
9287
|
const status = await getFleetStatus(fleetPaths());
|
|
8879
9288
|
for (const line of formatFleetStatus(status)) console.log(line);
|
|
8880
9289
|
}
|
|
9290
|
+
async function cmdBrowse() {
|
|
9291
|
+
try {
|
|
9292
|
+
const port = flagValue("--port");
|
|
9293
|
+
const { proxy } = await runBrowse(fleetPaths(), {
|
|
9294
|
+
checkpoint: flagValue("--checkpoint") ?? "",
|
|
9295
|
+
slug: flagValue("--slug") ?? "",
|
|
9296
|
+
upstream: flagValue("--upstream"),
|
|
9297
|
+
port: port ? Number(port) : void 0,
|
|
9298
|
+
extraHosts: flagValue("--host") ? [flagValue("--host")] : []
|
|
9299
|
+
});
|
|
9300
|
+
const stop = async () => {
|
|
9301
|
+
await proxy.close();
|
|
9302
|
+
process.exit(0);
|
|
9303
|
+
};
|
|
9304
|
+
process.on("SIGINT", stop);
|
|
9305
|
+
process.on("SIGTERM", stop);
|
|
9306
|
+
await new Promise(() => void 0);
|
|
9307
|
+
} catch (err) {
|
|
9308
|
+
console.error(`browse failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
9309
|
+
process.exit(1);
|
|
9310
|
+
}
|
|
9311
|
+
}
|
|
8881
9312
|
async function cmdGitRoute() {
|
|
8882
9313
|
try {
|
|
8883
9314
|
if (process.argv.includes("--undo")) {
|
|
@@ -8975,6 +9406,9 @@ async function main() {
|
|
|
8975
9406
|
case "git-route":
|
|
8976
9407
|
await cmdGitRoute();
|
|
8977
9408
|
break;
|
|
9409
|
+
case "browse":
|
|
9410
|
+
await cmdBrowse();
|
|
9411
|
+
break;
|
|
8978
9412
|
case "git-credential":
|
|
8979
9413
|
await cmdGitCredential();
|
|
8980
9414
|
break;
|
|
@@ -8998,6 +9432,12 @@ async function main() {
|
|
|
8998
9432
|
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
8999
9433
|
console.log(" (same flags; policy shared with Claude Code)");
|
|
9000
9434
|
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
9435
|
+
console.log(
|
|
9436
|
+
" browse Sign an agent in to your dashboard from this machine and"
|
|
9437
|
+
);
|
|
9438
|
+
console.log(
|
|
9439
|
+
" run its device proxy (--checkpoint <url> --slug <agent>)"
|
|
9440
|
+
);
|
|
9001
9441
|
console.log(" enroll <token> Enrol this machine in your organisation fleet");
|
|
9002
9442
|
console.log(" (--api-base-url <url>, --system for machine scope)");
|
|
9003
9443
|
console.log(" fleet-status Show fleet enrolment, signed policy and heartbeat");
|
|
@@ -9030,6 +9470,7 @@ var init_guard = __esm({
|
|
|
9030
9470
|
init_cli();
|
|
9031
9471
|
init_enrolment_store();
|
|
9032
9472
|
init_git_route();
|
|
9473
|
+
init_browse();
|
|
9033
9474
|
DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
9034
9475
|
DEFAULT_CONFIG_DIR = ".astrasync";
|
|
9035
9476
|
main().catch((err) => {
|
|
@@ -9065,6 +9506,7 @@ COMMANDS:
|
|
|
9065
9506
|
guard enroll <token> Enrol this machine in your organisation's fleet
|
|
9066
9507
|
(--api-base-url <url>, --system for machine scope)
|
|
9067
9508
|
guard fleet-status Fleet enrolment, signed policy and heartbeat state
|
|
9509
|
+
guard browse Sign an agent in to your dashboard via this machine's device proxy
|
|
9068
9510
|
guard managed-settings MDM templates for Claude Code / Codex (--claude, --codex, --out <dir>)
|
|
9069
9511
|
|
|
9070
9512
|
REGISTER OPTIONS:
|
|
@@ -9148,13 +9590,13 @@ async function main2() {
|
|
|
9148
9590
|
baseUrl: config.baseUrl
|
|
9149
9591
|
});
|
|
9150
9592
|
if (command === "register") {
|
|
9151
|
-
const
|
|
9152
|
-
if (!
|
|
9593
|
+
const name2 = commandArgs["name"];
|
|
9594
|
+
if (!name2) {
|
|
9153
9595
|
console.error("Error: --name is required for register command");
|
|
9154
9596
|
process.exit(1);
|
|
9155
9597
|
}
|
|
9156
9598
|
const options = {
|
|
9157
|
-
name,
|
|
9599
|
+
name: name2,
|
|
9158
9600
|
...commandArgs["description"] && { description: commandArgs["description"] },
|
|
9159
9601
|
...commandArgs["agent-type"] && { agentType: commandArgs["agent-type"] },
|
|
9160
9602
|
...commandArgs["api-endpoint"] && { apiEndpoint: commandArgs["api-endpoint"] }
|
|
@@ -3988,7 +3988,7 @@ function getTrustLevel(score) {
|
|
|
3988
3988
|
}
|
|
3989
3989
|
|
|
3990
3990
|
// src/version.ts
|
|
3991
|
-
var SDK_VERSION = "5.
|
|
3991
|
+
var SDK_VERSION = "5.17.1";
|
|
3992
3992
|
|
|
3993
3993
|
// src/http.ts
|
|
3994
3994
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -4127,6 +4127,7 @@ function getCacheKey(request, counterpartyId) {
|
|
|
4127
4127
|
request.enableRuntimeChallenge ? "1" : "0",
|
|
4128
4128
|
// 5.16.0: route policy changes the verdict (owners / challenge).
|
|
4129
4129
|
request.routePolicy ? JSON.stringify(request.routePolicy) : "",
|
|
4130
|
+
request.approvalHeldByRoute ? "1" : "0",
|
|
4130
4131
|
// callerMetadata fields contribute to risk model; include the ones
|
|
4131
4132
|
// backend reads. sourceIp/userAgent/forwardedFor change per-request
|
|
4132
4133
|
// so their inclusion effectively forces a re-check for any varying
|
|
@@ -4300,6 +4301,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
4300
4301
|
if (requestData.enableRuntimeChallenge)
|
|
4301
4302
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
4302
4303
|
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
4304
|
+
if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
|
|
4303
4305
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
4304
4306
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
4305
4307
|
if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
|
|
@@ -4631,6 +4633,8 @@ async function verify(config, request, options) {
|
|
|
4631
4633
|
} else if (result.recommendation === "step_up_required" && result.identityVerified) {
|
|
4632
4634
|
result.requiresStepUp = true;
|
|
4633
4635
|
result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
|
|
4636
|
+
const stepUpFailures = apiResponse.access?.failures;
|
|
4637
|
+
if (stepUpFailures?.length) result.failures = stepUpFailures;
|
|
4634
4638
|
}
|
|
4635
4639
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
4636
4640
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|