@sentry/junior 0.81.0 → 0.82.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/{agent-hooks-ZGTDOXQY.js → agent-hooks-5ZRILRC3.js} +1 -1
- package/dist/app.js +7 -905
- package/dist/chat/agent-dispatch/runner.d.ts +1 -1
- package/dist/chat/app/services.d.ts +1 -1
- package/dist/chat/egress/credentialed.d.ts +57 -0
- package/dist/chat/egress/plugin.d.ts +24 -0
- package/dist/chat/plugins/credential-hooks.d.ts +2 -0
- package/dist/chat/respond.d.ts +1 -1
- package/dist/chat/sandbox/{egress-credentials.d.ts → egress/credentials.d.ts} +28 -5
- package/dist/chat/sandbox/egress/policy.d.ts +32 -0
- package/dist/chat/sandbox/egress/proxy.d.ts +35 -0
- package/dist/chat/sandbox/{egress-session.d.ts → egress/session.d.ts} +41 -11
- package/dist/chat/sandbox/sandbox.d.ts +2 -2
- package/dist/chat/tools/types.d.ts +2 -1
- package/dist/{chunk-BOMLWLTF.js → chunk-NC6LR6U4.js} +1048 -13
- package/dist/{chunk-Y3YUOEAZ.js → chunk-TR2G37II.js} +14 -5
- package/dist/{chunk-RV5RYIJW.js → chunk-UXPG6ZIN.js} +28 -0
- package/dist/cli/chat.js +3 -3
- package/dist/cli/env.js +7 -1
- package/dist/cli/init.js +1 -0
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +1 -1
- package/dist/handlers/agent-dispatch.d.ts +1 -1
- package/dist/handlers/sandbox-egress-proxy.d.ts +2 -2
- package/dist/reporting.js +1 -1
- package/dist/{runner-LNBN7BIN.js → runner-VRFJLG2M.js} +2 -2
- package/package.json +6 -5
- package/dist/chat/sandbox/egress-policy.d.ts +0 -15
- package/dist/chat/sandbox/egress-proxy.d.ts +0 -19
- /package/dist/chat/sandbox/{egress-oidc.d.ts → egress/oidc.d.ts} +0 -0
- /package/dist/chat/sandbox/{egress-schemas.d.ts → egress/schemas.d.ts} +0 -0
- /package/dist/chat/sandbox/{egress-tracing.d.ts → egress/tracing.d.ts} +0 -0
package/dist/app.js
CHANGED
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
buildDeliveredTurnStatePatch,
|
|
11
11
|
buildDeterministicTurnId,
|
|
12
12
|
clearPendingAuth,
|
|
13
|
-
clearSandboxEgressCredentialLease,
|
|
14
13
|
coerceThreadArtifactsState,
|
|
15
14
|
createConversationMemoryService,
|
|
16
15
|
createUserTokenStore,
|
|
17
16
|
deleteMcpAuthSession,
|
|
18
17
|
deleteSlackMessage,
|
|
19
18
|
estimateTextTokens,
|
|
19
|
+
executeCredentialedEgressRequest,
|
|
20
20
|
finalizeMcpAuthorization,
|
|
21
21
|
formatProviderLabel,
|
|
22
22
|
generateAssistantReply,
|
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
getMcpStoredOAuthCredentials,
|
|
31
31
|
getPersistedSandboxState,
|
|
32
32
|
getPersistedThreadState,
|
|
33
|
-
getSandboxEgressCredentialLease,
|
|
34
33
|
getThreadTitleSourceMessage,
|
|
35
34
|
isAuthResumeRetryableTurnError,
|
|
36
35
|
isCooperativeTurnYieldError,
|
|
@@ -39,12 +38,10 @@ import {
|
|
|
39
38
|
isRedundantReactionAckText,
|
|
40
39
|
isRetryableTurnError,
|
|
41
40
|
isTurnInputCommitLostError,
|
|
42
|
-
issueProviderCredentialLease,
|
|
43
41
|
listThreadReplies,
|
|
44
42
|
markConversationMessage,
|
|
45
43
|
markTurnClosed,
|
|
46
44
|
markTurnFailed,
|
|
47
|
-
matchesSandboxEgressDomain,
|
|
48
45
|
mergeArtifactsState,
|
|
49
46
|
normalizeConversationText,
|
|
50
47
|
normalizeSandboxEgressTracePropagationDomains,
|
|
@@ -57,12 +54,10 @@ import {
|
|
|
57
54
|
removeReactionFromMessage,
|
|
58
55
|
renderSlackLegacyAttachmentText,
|
|
59
56
|
resolveBaseUrl,
|
|
57
|
+
resolvePluginOAuthAccount,
|
|
60
58
|
resolveSandboxEgressProviderForHost,
|
|
61
59
|
scheduleSessionCompletedPluginTasks,
|
|
62
60
|
setConfigDefaults,
|
|
63
|
-
setSandboxEgressAuthRequiredSignal,
|
|
64
|
-
setSandboxEgressCredentialLease,
|
|
65
|
-
setSandboxEgressPermissionDeniedSignal,
|
|
66
61
|
shouldEmitDevAgentTrace,
|
|
67
62
|
shouldPropagateSandboxEgressTrace,
|
|
68
63
|
startActiveTurn,
|
|
@@ -71,7 +66,7 @@ import {
|
|
|
71
66
|
updateConversationStats,
|
|
72
67
|
uploadFilesToThread,
|
|
73
68
|
upsertConversationMessage
|
|
74
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-NC6LR6U4.js";
|
|
75
70
|
import {
|
|
76
71
|
CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
|
|
77
72
|
CONVERSATION_WORK_STALE_ENQUEUE_MS,
|
|
@@ -149,7 +144,7 @@ import {
|
|
|
149
144
|
setPlugins,
|
|
150
145
|
validatePlugins,
|
|
151
146
|
verifySlackDirectCredentialSubject
|
|
152
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-TR2G37II.js";
|
|
153
148
|
import {
|
|
154
149
|
createPluginLogger,
|
|
155
150
|
createPluginState
|
|
@@ -203,7 +198,6 @@ import {
|
|
|
203
198
|
discoverSkills
|
|
204
199
|
} from "./chunk-WCXVEQPI.js";
|
|
205
200
|
import {
|
|
206
|
-
CredentialUnavailableError,
|
|
207
201
|
buildOAuthTokenRequest,
|
|
208
202
|
hasRequiredOAuthScope,
|
|
209
203
|
parseOAuthTokenResponse,
|
|
@@ -3548,194 +3542,6 @@ async function GET3(request, provider, waitUntil, options = {}) {
|
|
|
3548
3542
|
// src/handlers/oauth-callback.ts
|
|
3549
3543
|
import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS2 } from "chat";
|
|
3550
3544
|
|
|
3551
|
-
// src/chat/plugins/credential-hooks.ts
|
|
3552
|
-
import {
|
|
3553
|
-
pluginAuthorizationSchema,
|
|
3554
|
-
pluginCredentialResultSchema,
|
|
3555
|
-
pluginGrantSchema,
|
|
3556
|
-
pluginProviderAccountSchema
|
|
3557
|
-
} from "@sentry/junior-plugin-api";
|
|
3558
|
-
function parseSchema(schema, value, message) {
|
|
3559
|
-
const result = schema.safeParse(value);
|
|
3560
|
-
if (!result.success) {
|
|
3561
|
-
throw new Error(message);
|
|
3562
|
-
}
|
|
3563
|
-
return result.data;
|
|
3564
|
-
}
|
|
3565
|
-
function parseAuthorization(value, pluginName) {
|
|
3566
|
-
if (value === void 0) {
|
|
3567
|
-
return void 0;
|
|
3568
|
-
}
|
|
3569
|
-
const authorization = parseSchema(
|
|
3570
|
-
pluginAuthorizationSchema,
|
|
3571
|
-
value,
|
|
3572
|
-
`Plugin "${pluginName}" grant authorization is invalid`
|
|
3573
|
-
);
|
|
3574
|
-
if (authorization.provider !== pluginName) {
|
|
3575
|
-
throw new Error(
|
|
3576
|
-
`Plugin "${pluginName}" grant authorization provider must match the issuing plugin`
|
|
3577
|
-
);
|
|
3578
|
-
}
|
|
3579
|
-
return authorization;
|
|
3580
|
-
}
|
|
3581
|
-
function parseGrant(value, pluginName) {
|
|
3582
|
-
return parseSchema(
|
|
3583
|
-
pluginGrantSchema,
|
|
3584
|
-
value,
|
|
3585
|
-
`Plugin "${pluginName}" grantForEgress returned an invalid grant`
|
|
3586
|
-
);
|
|
3587
|
-
}
|
|
3588
|
-
function pluginFor(provider) {
|
|
3589
|
-
return getPlugins().find((candidate) => candidate.manifest.name === provider);
|
|
3590
|
-
}
|
|
3591
|
-
function basePluginContext(plugin) {
|
|
3592
|
-
const pluginName = plugin.manifest.name;
|
|
3593
|
-
return {
|
|
3594
|
-
plugin: { name: pluginName },
|
|
3595
|
-
log: createPluginLogger(pluginName),
|
|
3596
|
-
db: getDb()
|
|
3597
|
-
};
|
|
3598
|
-
}
|
|
3599
|
-
function parseCredentialResult(value, pluginName) {
|
|
3600
|
-
const result = parseSchema(
|
|
3601
|
-
pluginCredentialResultSchema,
|
|
3602
|
-
value,
|
|
3603
|
-
`Plugin "${pluginName}" issueCredential result is invalid`
|
|
3604
|
-
);
|
|
3605
|
-
if (result.type === "lease") {
|
|
3606
|
-
parseAuthorization(result.lease.authorization, pluginName);
|
|
3607
|
-
return result;
|
|
3608
|
-
}
|
|
3609
|
-
if (result.type === "unavailable") {
|
|
3610
|
-
return result;
|
|
3611
|
-
}
|
|
3612
|
-
parseAuthorization(result.authorization, pluginName);
|
|
3613
|
-
return result;
|
|
3614
|
-
}
|
|
3615
|
-
async function selectPluginGrant(input) {
|
|
3616
|
-
const plugin = pluginFor(input.provider);
|
|
3617
|
-
const hook = plugin?.hooks?.grantForEgress;
|
|
3618
|
-
if (!plugin || !hook) {
|
|
3619
|
-
return void 0;
|
|
3620
|
-
}
|
|
3621
|
-
const result = await hook({
|
|
3622
|
-
...basePluginContext(plugin),
|
|
3623
|
-
request: {
|
|
3624
|
-
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
|
|
3625
|
-
method: input.method,
|
|
3626
|
-
url: input.upstreamUrl.toString()
|
|
3627
|
-
}
|
|
3628
|
-
});
|
|
3629
|
-
return result === void 0 ? void 0 : parseGrant(result, plugin.manifest.name);
|
|
3630
|
-
}
|
|
3631
|
-
async function onPluginEgressResponse(input) {
|
|
3632
|
-
const plugin = pluginFor(input.provider);
|
|
3633
|
-
const hook = plugin?.hooks?.onEgressResponse;
|
|
3634
|
-
if (!plugin || !hook) {
|
|
3635
|
-
return {};
|
|
3636
|
-
}
|
|
3637
|
-
let permissionDenied;
|
|
3638
|
-
await hook({
|
|
3639
|
-
...basePluginContext(plugin),
|
|
3640
|
-
grant: input.grant,
|
|
3641
|
-
permissionDenied(message) {
|
|
3642
|
-
const trimmed = message.trim();
|
|
3643
|
-
if (!trimmed) {
|
|
3644
|
-
throw new Error(
|
|
3645
|
-
`Plugin "${plugin.manifest.name}" onEgressResponse permissionDenied message is empty`
|
|
3646
|
-
);
|
|
3647
|
-
}
|
|
3648
|
-
permissionDenied = { message: trimmed };
|
|
3649
|
-
},
|
|
3650
|
-
request: {
|
|
3651
|
-
method: input.method,
|
|
3652
|
-
url: input.upstreamUrl.toString()
|
|
3653
|
-
},
|
|
3654
|
-
response: input.response
|
|
3655
|
-
});
|
|
3656
|
-
return permissionDenied ? { permissionDenied } : {};
|
|
3657
|
-
}
|
|
3658
|
-
function hasEgressCredentialHooks(provider) {
|
|
3659
|
-
const hooks = pluginFor(provider)?.hooks;
|
|
3660
|
-
return Boolean(hooks?.grantForEgress || hooks?.issueCredential);
|
|
3661
|
-
}
|
|
3662
|
-
async function resolvePluginOAuthAccount(input) {
|
|
3663
|
-
const plugin = pluginFor(input.provider);
|
|
3664
|
-
const hook = plugin?.hooks?.resolveOAuthAccount;
|
|
3665
|
-
if (!plugin || !hook) {
|
|
3666
|
-
return void 0;
|
|
3667
|
-
}
|
|
3668
|
-
const account = await hook({
|
|
3669
|
-
...basePluginContext(plugin),
|
|
3670
|
-
tokens: input.tokens
|
|
3671
|
-
});
|
|
3672
|
-
return account === void 0 ? void 0 : parseSchema(
|
|
3673
|
-
pluginProviderAccountSchema,
|
|
3674
|
-
account,
|
|
3675
|
-
`Plugin "${plugin.manifest.name}" resolveOAuthAccount returned an invalid account`
|
|
3676
|
-
);
|
|
3677
|
-
}
|
|
3678
|
-
async function issuePluginCredential(input) {
|
|
3679
|
-
const plugin = pluginFor(input.provider);
|
|
3680
|
-
const hook = plugin?.hooks?.issueCredential;
|
|
3681
|
-
if (!plugin || !hook) {
|
|
3682
|
-
throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
|
|
3683
|
-
}
|
|
3684
|
-
const currentUserId = input.actor.type === "user" ? input.actor.userId : void 0;
|
|
3685
|
-
const credentialSubjectUserId = input.credentialSubject?.userId;
|
|
3686
|
-
const result = await hook({
|
|
3687
|
-
...basePluginContext(plugin),
|
|
3688
|
-
actor: input.actor,
|
|
3689
|
-
grant: input.grant,
|
|
3690
|
-
...input.credentialSubject ? { credentialSubject: input.credentialSubject } : {},
|
|
3691
|
-
tokens: {
|
|
3692
|
-
...currentUserId ? {
|
|
3693
|
-
currentUser: {
|
|
3694
|
-
userId: currentUserId,
|
|
3695
|
-
get: async () => await input.userTokenStore.get(
|
|
3696
|
-
currentUserId,
|
|
3697
|
-
plugin.manifest.name
|
|
3698
|
-
),
|
|
3699
|
-
set: async (tokens) => {
|
|
3700
|
-
await input.userTokenStore.set(
|
|
3701
|
-
currentUserId,
|
|
3702
|
-
plugin.manifest.name,
|
|
3703
|
-
tokens
|
|
3704
|
-
);
|
|
3705
|
-
},
|
|
3706
|
-
withRefresh: async (callback) => await input.userTokenStore.withRefresh(
|
|
3707
|
-
currentUserId,
|
|
3708
|
-
plugin.manifest.name,
|
|
3709
|
-
callback
|
|
3710
|
-
)
|
|
3711
|
-
}
|
|
3712
|
-
} : {},
|
|
3713
|
-
...credentialSubjectUserId ? {
|
|
3714
|
-
credentialSubject: {
|
|
3715
|
-
userId: credentialSubjectUserId,
|
|
3716
|
-
get: async () => await input.userTokenStore.get(
|
|
3717
|
-
credentialSubjectUserId,
|
|
3718
|
-
plugin.manifest.name
|
|
3719
|
-
),
|
|
3720
|
-
set: async (tokens) => {
|
|
3721
|
-
await input.userTokenStore.set(
|
|
3722
|
-
credentialSubjectUserId,
|
|
3723
|
-
plugin.manifest.name,
|
|
3724
|
-
tokens
|
|
3725
|
-
);
|
|
3726
|
-
},
|
|
3727
|
-
withRefresh: async (callback) => await input.userTokenStore.withRefresh(
|
|
3728
|
-
credentialSubjectUserId,
|
|
3729
|
-
plugin.manifest.name,
|
|
3730
|
-
callback
|
|
3731
|
-
)
|
|
3732
|
-
}
|
|
3733
|
-
} : {}
|
|
3734
|
-
}
|
|
3735
|
-
});
|
|
3736
|
-
return parseCredentialResult(result, plugin.manifest.name);
|
|
3737
|
-
}
|
|
3738
|
-
|
|
3739
3545
|
// src/chat/slack/app-home.ts
|
|
3740
3546
|
import fs from "fs";
|
|
3741
3547
|
import path from "path";
|
|
@@ -4554,174 +4360,7 @@ async function GET4(request, provider, waitUntil, options = {}) {
|
|
|
4554
4360
|
});
|
|
4555
4361
|
}
|
|
4556
4362
|
|
|
4557
|
-
// src/chat/sandbox/egress
|
|
4558
|
-
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
4559
|
-
var SandboxEgressCredentialError = class extends Error {
|
|
4560
|
-
authorization;
|
|
4561
|
-
grant;
|
|
4562
|
-
kind;
|
|
4563
|
-
provider;
|
|
4564
|
-
constructor(input) {
|
|
4565
|
-
super(input.message);
|
|
4566
|
-
this.name = "SandboxEgressCredentialError";
|
|
4567
|
-
this.authorization = input.authorization;
|
|
4568
|
-
this.grant = input.grant;
|
|
4569
|
-
this.kind = input.kind;
|
|
4570
|
-
this.provider = input.provider;
|
|
4571
|
-
}
|
|
4572
|
-
};
|
|
4573
|
-
function defaultGrantForProvider(input) {
|
|
4574
|
-
const access = HTTP_READ_METHODS.has(
|
|
4575
|
-
input.method.toUpperCase()
|
|
4576
|
-
) ? "read" : "write";
|
|
4577
|
-
return {
|
|
4578
|
-
source: "broker",
|
|
4579
|
-
grant: {
|
|
4580
|
-
name: "default",
|
|
4581
|
-
access,
|
|
4582
|
-
reason: `sandbox-egress:${input.provider}:${access}`
|
|
4583
|
-
}
|
|
4584
|
-
};
|
|
4585
|
-
}
|
|
4586
|
-
function oauthAuthorizationForProvider(provider) {
|
|
4587
|
-
const oauth = pluginCatalogRuntime.getOAuthConfig(provider);
|
|
4588
|
-
return oauth ? {
|
|
4589
|
-
type: "oauth",
|
|
4590
|
-
provider,
|
|
4591
|
-
...oauth.scope ? { scope: oauth.scope } : {}
|
|
4592
|
-
} : void 0;
|
|
4593
|
-
}
|
|
4594
|
-
function credentialSubjectFromContext(context) {
|
|
4595
|
-
return "subject" in context.credentials && context.credentials.subject ? { type: "user", userId: context.credentials.subject.userId } : void 0;
|
|
4596
|
-
}
|
|
4597
|
-
function assertLeaseTransformsOwnedByProvider(provider, lease) {
|
|
4598
|
-
for (const transform of lease.headerTransforms) {
|
|
4599
|
-
if (resolveSandboxEgressProviderForHost(transform.domain) !== provider) {
|
|
4600
|
-
throw new Error(
|
|
4601
|
-
`Credential lease for ${provider} included header transform for unowned domain ${transform.domain}`
|
|
4602
|
-
);
|
|
4603
|
-
}
|
|
4604
|
-
}
|
|
4605
|
-
}
|
|
4606
|
-
async function selectSandboxEgressGrant(input) {
|
|
4607
|
-
if (!hasEgressCredentialHooks(input.provider)) {
|
|
4608
|
-
return defaultGrantForProvider(input);
|
|
4609
|
-
}
|
|
4610
|
-
const pluginGrant = await selectPluginGrant({
|
|
4611
|
-
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
|
|
4612
|
-
provider: input.provider,
|
|
4613
|
-
method: input.method,
|
|
4614
|
-
upstreamUrl: input.upstreamUrl
|
|
4615
|
-
});
|
|
4616
|
-
if (!pluginGrant) {
|
|
4617
|
-
throw new Error(
|
|
4618
|
-
`Plugin "${input.provider}" grantForEgress must return a grant for sandbox egress`
|
|
4619
|
-
);
|
|
4620
|
-
}
|
|
4621
|
-
return { source: "plugin", grant: pluginGrant };
|
|
4622
|
-
}
|
|
4623
|
-
function authorizationForSandboxEgressGrant(provider, selection) {
|
|
4624
|
-
return selection.source === "broker" ? oauthAuthorizationForProvider(provider) : void 0;
|
|
4625
|
-
}
|
|
4626
|
-
async function sandboxEgressCredentialLease(provider, selection, context) {
|
|
4627
|
-
const { grant } = selection;
|
|
4628
|
-
const cached = await getSandboxEgressCredentialLease(
|
|
4629
|
-
provider,
|
|
4630
|
-
grant.name,
|
|
4631
|
-
context
|
|
4632
|
-
);
|
|
4633
|
-
if (cached) {
|
|
4634
|
-
if (selection.source === "plugin" && cached.grant.access !== grant.access) {
|
|
4635
|
-
throw new Error(
|
|
4636
|
-
`Cached credential lease for ${provider}/${grant.name} has ${cached.grant.access} access, but ${grant.access} was selected`
|
|
4637
|
-
);
|
|
4638
|
-
}
|
|
4639
|
-
return {
|
|
4640
|
-
...cached,
|
|
4641
|
-
grant
|
|
4642
|
-
};
|
|
4643
|
-
}
|
|
4644
|
-
let lease;
|
|
4645
|
-
if (selection.source === "plugin") {
|
|
4646
|
-
const credentialSubject = credentialSubjectFromContext(context);
|
|
4647
|
-
const pluginResult = await issuePluginCredential({
|
|
4648
|
-
provider,
|
|
4649
|
-
grant,
|
|
4650
|
-
actor: context.credentials.actor,
|
|
4651
|
-
...credentialSubject ? { credentialSubject } : {},
|
|
4652
|
-
userTokenStore: createUserTokenStore()
|
|
4653
|
-
});
|
|
4654
|
-
if (pluginResult.type === "needed") {
|
|
4655
|
-
throw new SandboxEgressCredentialError({
|
|
4656
|
-
provider,
|
|
4657
|
-
grant,
|
|
4658
|
-
kind: "auth_required",
|
|
4659
|
-
authorization: pluginResult.authorization,
|
|
4660
|
-
message: pluginResult.message
|
|
4661
|
-
});
|
|
4662
|
-
}
|
|
4663
|
-
if (pluginResult.type === "unavailable") {
|
|
4664
|
-
throw new SandboxEgressCredentialError({
|
|
4665
|
-
provider,
|
|
4666
|
-
grant,
|
|
4667
|
-
kind: "unavailable",
|
|
4668
|
-
message: pluginResult.message
|
|
4669
|
-
});
|
|
4670
|
-
}
|
|
4671
|
-
lease = pluginResult.lease;
|
|
4672
|
-
} else {
|
|
4673
|
-
try {
|
|
4674
|
-
lease = await issueProviderCredentialLease({
|
|
4675
|
-
context: context.credentials,
|
|
4676
|
-
provider,
|
|
4677
|
-
reason: grant.reason ?? `sandbox-egress:${provider}:default`
|
|
4678
|
-
});
|
|
4679
|
-
} catch (error) {
|
|
4680
|
-
if (error instanceof CredentialUnavailableError) {
|
|
4681
|
-
throw new SandboxEgressCredentialError({
|
|
4682
|
-
provider,
|
|
4683
|
-
grant,
|
|
4684
|
-
kind: "auth_required",
|
|
4685
|
-
authorization: authorizationForSandboxEgressGrant(
|
|
4686
|
-
provider,
|
|
4687
|
-
selection
|
|
4688
|
-
),
|
|
4689
|
-
message: error.message
|
|
4690
|
-
});
|
|
4691
|
-
}
|
|
4692
|
-
throw error;
|
|
4693
|
-
}
|
|
4694
|
-
}
|
|
4695
|
-
const headerTransforms = lease.headerTransforms ?? [];
|
|
4696
|
-
if (headerTransforms.length === 0) {
|
|
4697
|
-
throw new Error(
|
|
4698
|
-
`Credential lease for ${provider} did not include header transforms`
|
|
4699
|
-
);
|
|
4700
|
-
}
|
|
4701
|
-
const leaseExpiresAtMs = Date.parse(lease.expiresAt);
|
|
4702
|
-
if (!Number.isFinite(leaseExpiresAtMs) || leaseExpiresAtMs <= Date.now()) {
|
|
4703
|
-
throw new Error(`Credential lease for ${provider} is expired`);
|
|
4704
|
-
}
|
|
4705
|
-
const authorization = selection.source === "broker" ? oauthAuthorizationForProvider(provider) : lease.authorization;
|
|
4706
|
-
const cachedLease = {
|
|
4707
|
-
provider,
|
|
4708
|
-
grant,
|
|
4709
|
-
...lease.account ? { account: lease.account } : {},
|
|
4710
|
-
...authorization ? { authorization } : {},
|
|
4711
|
-
expiresAt: lease.expiresAt,
|
|
4712
|
-
headerTransforms
|
|
4713
|
-
};
|
|
4714
|
-
assertLeaseTransformsOwnedByProvider(provider, cachedLease);
|
|
4715
|
-
await setSandboxEgressCredentialLease(context, cachedLease);
|
|
4716
|
-
return cachedLease;
|
|
4717
|
-
}
|
|
4718
|
-
function hasSandboxEgressLeaseTransformForHost(lease, host) {
|
|
4719
|
-
return lease.headerTransforms.some(
|
|
4720
|
-
(transform) => matchesSandboxEgressDomain(host, transform.domain)
|
|
4721
|
-
);
|
|
4722
|
-
}
|
|
4723
|
-
|
|
4724
|
-
// src/chat/sandbox/egress-oidc.ts
|
|
4363
|
+
// src/chat/sandbox/egress/oidc.ts
|
|
4725
4364
|
import {
|
|
4726
4365
|
createRemoteJWKSet,
|
|
4727
4366
|
decodeJwt,
|
|
@@ -4791,71 +4430,19 @@ async function verifyVercelSandboxOidcToken(token) {
|
|
|
4791
4430
|
return verified.payload;
|
|
4792
4431
|
}
|
|
4793
4432
|
|
|
4794
|
-
// src/chat/sandbox/egress
|
|
4795
|
-
import { EgressAuthRequired } from "@sentry/junior-plugin-api";
|
|
4433
|
+
// src/chat/sandbox/egress/proxy.ts
|
|
4796
4434
|
var OIDC_TOKEN_HEADER = "vercel-sandbox-oidc-token";
|
|
4797
4435
|
var FORWARDED_HOST_HEADER = "vercel-forwarded-host";
|
|
4798
4436
|
var FORWARDED_SCHEME_HEADER = "vercel-forwarded-scheme";
|
|
4799
4437
|
var FORWARDED_PORT_HEADER = "vercel-forwarded-port";
|
|
4800
4438
|
var FORWARDED_PATH_HEADER = "vercel-forwarded-path";
|
|
4801
|
-
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
4802
|
-
"connection",
|
|
4803
|
-
"host",
|
|
4804
|
-
"keep-alive",
|
|
4805
|
-
"proxy-authenticate",
|
|
4806
|
-
"proxy-authorization",
|
|
4807
|
-
"te",
|
|
4808
|
-
"trailer",
|
|
4809
|
-
"transfer-encoding",
|
|
4810
|
-
"upgrade"
|
|
4811
|
-
]);
|
|
4812
|
-
var PROXY_ONLY_HEADERS = /* @__PURE__ */ new Set([
|
|
4813
|
-
OIDC_TOKEN_HEADER,
|
|
4814
|
-
FORWARDED_HOST_HEADER,
|
|
4815
|
-
FORWARDED_SCHEME_HEADER,
|
|
4816
|
-
FORWARDED_PORT_HEADER,
|
|
4817
|
-
FORWARDED_PATH_HEADER
|
|
4818
|
-
]);
|
|
4819
|
-
var TRACE_PROPAGATION_HEADERS = /* @__PURE__ */ new Set([
|
|
4820
|
-
"baggage",
|
|
4821
|
-
"sentry-trace",
|
|
4822
|
-
"traceparent"
|
|
4823
|
-
]);
|
|
4824
|
-
var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
|
|
4825
|
-
"content-encoding",
|
|
4826
|
-
"content-length"
|
|
4827
|
-
]);
|
|
4828
|
-
var UPSTREAM_TOKEN_REJECTION_STATUS = 401;
|
|
4829
|
-
var UPSTREAM_PERMISSION_REJECTION_STATUS = 403;
|
|
4830
|
-
var GRANT_SELECTION_BODY_TEXT_LIMIT_BYTES = 64 * 1024;
|
|
4831
|
-
var RESPONSE_BODY_TEXT_LIMIT_BYTES = 64 * 1024;
|
|
4832
4439
|
function jsonError(message, status) {
|
|
4833
4440
|
return Response.json({ error: message }, { status });
|
|
4834
4441
|
}
|
|
4835
|
-
function authRequiredResponse(input) {
|
|
4836
|
-
return new Response(
|
|
4837
|
-
`junior-auth-required provider=${input.provider} grant=${input.grant.name} access=${input.grant.access} 401 unauthorized
|
|
4838
|
-
${input.message}`,
|
|
4839
|
-
{
|
|
4840
|
-
status: 401,
|
|
4841
|
-
headers: {
|
|
4842
|
-
"content-type": "text/plain; charset=utf-8",
|
|
4843
|
-
"cache-control": "no-store"
|
|
4844
|
-
}
|
|
4845
|
-
}
|
|
4846
|
-
);
|
|
4847
|
-
}
|
|
4848
|
-
function shouldLogSandboxEgressInfo() {
|
|
4849
|
-
const environment = (process.env.SENTRY_ENVIRONMENT ?? process.env.VERCEL_ENV ?? process.env.NODE_ENV ?? "").trim().toLowerCase();
|
|
4850
|
-
return environment !== "production";
|
|
4851
|
-
}
|
|
4852
4442
|
function egressAttributes(input) {
|
|
4853
4443
|
return {
|
|
4854
4444
|
...input.egressId ? { "app.sandbox.egress_id": input.egressId } : {},
|
|
4855
4445
|
...input.provider ? { "app.provider.name": input.provider } : {},
|
|
4856
|
-
...input.grantName ? { "app.grant.name": input.grantName } : {},
|
|
4857
|
-
...input.grantAccess ? { "app.grant.access": input.grantAccess } : {},
|
|
4858
|
-
...input.grantReason ? { "app.grant.reason": input.grantReason } : {},
|
|
4859
4446
|
...input.host ? { "server.address": input.host } : {},
|
|
4860
4447
|
...input.method ? { "http.request.method": input.method } : {},
|
|
4861
4448
|
...input.path ? { "url.path": input.path } : {},
|
|
@@ -4898,63 +4485,6 @@ function routingAttributes(request, upstreamUrl) {
|
|
|
4898
4485
|
}
|
|
4899
4486
|
return attributes;
|
|
4900
4487
|
}
|
|
4901
|
-
function displayedUpstreamPath(upstreamUrl) {
|
|
4902
|
-
const gitService = upstreamUrl.searchParams.get("service");
|
|
4903
|
-
if (upstreamUrl.hostname.toLowerCase() === "github.com" && (gitService === "git-upload-pack" || gitService === "git-receive-pack")) {
|
|
4904
|
-
return `${upstreamUrl.pathname}?service=${gitService}`;
|
|
4905
|
-
}
|
|
4906
|
-
return upstreamUrl.pathname;
|
|
4907
|
-
}
|
|
4908
|
-
function upstreamPermissionAttributes(provider, upstream) {
|
|
4909
|
-
if (provider !== "github") {
|
|
4910
|
-
return {};
|
|
4911
|
-
}
|
|
4912
|
-
return {
|
|
4913
|
-
"app.github.accepted_permissions": upstream.headers.get("x-accepted-github-permissions") ?? void 0,
|
|
4914
|
-
"app.github.sso": upstream.headers.get("x-github-sso") ?? void 0
|
|
4915
|
-
};
|
|
4916
|
-
}
|
|
4917
|
-
function githubPermissionHeaders(upstream) {
|
|
4918
|
-
const acceptedPermissions = upstream.headers.get(
|
|
4919
|
-
"x-accepted-github-permissions"
|
|
4920
|
-
);
|
|
4921
|
-
const sso = upstream.headers.get("x-github-sso");
|
|
4922
|
-
return {
|
|
4923
|
-
...acceptedPermissions ? { acceptedPermissions } : {},
|
|
4924
|
-
...sso ? { sso } : {}
|
|
4925
|
-
};
|
|
4926
|
-
}
|
|
4927
|
-
function permissionDeniedMessage(provider, grant) {
|
|
4928
|
-
return `${provider} returned HTTP 403 after Junior injected the ${grant.name} grant. Junior forwarded the request; this is not a local runtime block.`;
|
|
4929
|
-
}
|
|
4930
|
-
function isEgressAuthRequired(error) {
|
|
4931
|
-
return error instanceof EgressAuthRequired || error instanceof Error && error.name === "EgressAuthRequired";
|
|
4932
|
-
}
|
|
4933
|
-
function logSandboxEgressUpstreamRequest(input) {
|
|
4934
|
-
if (!shouldLogSandboxEgressInfo()) {
|
|
4935
|
-
return;
|
|
4936
|
-
}
|
|
4937
|
-
logInfo(
|
|
4938
|
-
"sandbox_egress_upstream_request",
|
|
4939
|
-
{},
|
|
4940
|
-
{
|
|
4941
|
-
...egressAttributes({
|
|
4942
|
-
egressId: input.egressId,
|
|
4943
|
-
grantAccess: input.grantAccess,
|
|
4944
|
-
grantName: input.grantName,
|
|
4945
|
-
grantReason: input.grantReason,
|
|
4946
|
-
host: input.upstreamUrl.hostname,
|
|
4947
|
-
method: input.request.method,
|
|
4948
|
-
path: input.upstreamUrl.pathname,
|
|
4949
|
-
provider: input.provider,
|
|
4950
|
-
status: input.upstream.status
|
|
4951
|
-
}),
|
|
4952
|
-
...routingAttributes(input.request, input.upstreamUrl),
|
|
4953
|
-
"app.sandbox.egress.upstream_ok": input.upstream.ok
|
|
4954
|
-
},
|
|
4955
|
-
`Sandbox egress ${input.request.method} ${input.upstreamUrl.hostname}${displayedUpstreamPath(input.upstreamUrl)} -> ${input.upstream.status}`
|
|
4956
|
-
);
|
|
4957
|
-
}
|
|
4958
4488
|
function normalizeHost(value) {
|
|
4959
4489
|
const trimmed = value.trim().toLowerCase();
|
|
4960
4490
|
if (!trimmed || trimmed.includes("/") || trimmed.includes("\\") || trimmed.includes(":")) {
|
|
@@ -5032,123 +4562,6 @@ function buildUpstreamUrl(request) {
|
|
|
5032
4562
|
return { ok: false, error: "Invalid forwarded URL" };
|
|
5033
4563
|
}
|
|
5034
4564
|
}
|
|
5035
|
-
async function requestBodyBytes(request) {
|
|
5036
|
-
if (request.method === "GET" || request.method === "HEAD" || request.body === null) {
|
|
5037
|
-
return void 0;
|
|
5038
|
-
}
|
|
5039
|
-
return await request.arrayBuffer();
|
|
5040
|
-
}
|
|
5041
|
-
function isGrantSelectionBodyVisible(input) {
|
|
5042
|
-
return input.provider === "github" && input.upstreamUrl.hostname.toLowerCase() === "api.github.com" && input.upstreamUrl.pathname.toLowerCase().endsWith("/graphql");
|
|
5043
|
-
}
|
|
5044
|
-
function requestBodyText(body) {
|
|
5045
|
-
if (body === void 0 || body.byteLength > GRANT_SELECTION_BODY_TEXT_LIMIT_BYTES) {
|
|
5046
|
-
return void 0;
|
|
5047
|
-
}
|
|
5048
|
-
return new TextDecoder().decode(body);
|
|
5049
|
-
}
|
|
5050
|
-
function responseContentLength(upstream) {
|
|
5051
|
-
const raw = upstream.headers.get("content-length");
|
|
5052
|
-
if (!raw) {
|
|
5053
|
-
return void 0;
|
|
5054
|
-
}
|
|
5055
|
-
const parsed = Number(raw);
|
|
5056
|
-
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
5057
|
-
}
|
|
5058
|
-
async function responseTextWithinLimit(upstream, maxBytes) {
|
|
5059
|
-
const limit = Math.min(
|
|
5060
|
-
Math.max(0, Math.floor(maxBytes)),
|
|
5061
|
-
RESPONSE_BODY_TEXT_LIMIT_BYTES
|
|
5062
|
-
);
|
|
5063
|
-
if (limit <= 0) {
|
|
5064
|
-
return void 0;
|
|
5065
|
-
}
|
|
5066
|
-
const contentLength = responseContentLength(upstream);
|
|
5067
|
-
if (contentLength !== void 0 && contentLength > limit) {
|
|
5068
|
-
return void 0;
|
|
5069
|
-
}
|
|
5070
|
-
let clone;
|
|
5071
|
-
try {
|
|
5072
|
-
clone = upstream.clone();
|
|
5073
|
-
} catch {
|
|
5074
|
-
return void 0;
|
|
5075
|
-
}
|
|
5076
|
-
const body = clone.body;
|
|
5077
|
-
if (!body) {
|
|
5078
|
-
return "";
|
|
5079
|
-
}
|
|
5080
|
-
const reader = body.getReader();
|
|
5081
|
-
const chunks = [];
|
|
5082
|
-
let bytes = 0;
|
|
5083
|
-
try {
|
|
5084
|
-
while (true) {
|
|
5085
|
-
const { done, value } = await reader.read();
|
|
5086
|
-
if (done) {
|
|
5087
|
-
break;
|
|
5088
|
-
}
|
|
5089
|
-
if (!value) {
|
|
5090
|
-
continue;
|
|
5091
|
-
}
|
|
5092
|
-
bytes += value.byteLength;
|
|
5093
|
-
if (bytes > limit) {
|
|
5094
|
-
await reader.cancel().catch(() => void 0);
|
|
5095
|
-
return void 0;
|
|
5096
|
-
}
|
|
5097
|
-
chunks.push(value);
|
|
5098
|
-
}
|
|
5099
|
-
} catch {
|
|
5100
|
-
await reader.cancel().catch(() => void 0);
|
|
5101
|
-
return void 0;
|
|
5102
|
-
} finally {
|
|
5103
|
-
reader.releaseLock();
|
|
5104
|
-
}
|
|
5105
|
-
const combined = new Uint8Array(bytes);
|
|
5106
|
-
let offset = 0;
|
|
5107
|
-
for (const chunk of chunks) {
|
|
5108
|
-
combined.set(chunk, offset);
|
|
5109
|
-
offset += chunk.byteLength;
|
|
5110
|
-
}
|
|
5111
|
-
return new TextDecoder().decode(combined);
|
|
5112
|
-
}
|
|
5113
|
-
function requestHeaders(request, lease, upstreamHost, tracePropagation) {
|
|
5114
|
-
const headers = new Headers();
|
|
5115
|
-
const mayPropagateTrace = shouldPropagateSandboxEgressTrace(
|
|
5116
|
-
upstreamHost,
|
|
5117
|
-
tracePropagation
|
|
5118
|
-
);
|
|
5119
|
-
request.headers.forEach((value, key) => {
|
|
5120
|
-
const normalized = key.toLowerCase();
|
|
5121
|
-
if (HOP_BY_HOP_HEADERS.has(normalized) || PROXY_ONLY_HEADERS.has(normalized)) {
|
|
5122
|
-
return;
|
|
5123
|
-
}
|
|
5124
|
-
if (TRACE_PROPAGATION_HEADERS.has(normalized) && !mayPropagateTrace) {
|
|
5125
|
-
return;
|
|
5126
|
-
}
|
|
5127
|
-
headers.append(key, value);
|
|
5128
|
-
});
|
|
5129
|
-
for (const transform of lease.headerTransforms) {
|
|
5130
|
-
if (!matchesSandboxEgressDomain(upstreamHost, transform.domain)) {
|
|
5131
|
-
continue;
|
|
5132
|
-
}
|
|
5133
|
-
for (const [key, value] of Object.entries(transform.headers)) {
|
|
5134
|
-
if (TRACE_PROPAGATION_HEADERS.has(key.toLowerCase()) && !mayPropagateTrace) {
|
|
5135
|
-
continue;
|
|
5136
|
-
}
|
|
5137
|
-
headers.set(key, value);
|
|
5138
|
-
}
|
|
5139
|
-
}
|
|
5140
|
-
return headers;
|
|
5141
|
-
}
|
|
5142
|
-
function responseHeaders(upstream) {
|
|
5143
|
-
const headers = new Headers();
|
|
5144
|
-
upstream.headers.forEach((value, key) => {
|
|
5145
|
-
const normalized = key.toLowerCase();
|
|
5146
|
-
if (!HOP_BY_HOP_HEADERS.has(normalized) && !DECODED_RESPONSE_HEADERS.has(normalized)) {
|
|
5147
|
-
headers.append(key, value);
|
|
5148
|
-
}
|
|
5149
|
-
});
|
|
5150
|
-
return headers;
|
|
5151
|
-
}
|
|
5152
4565
|
function continueSandboxEgressTrace(request, upstreamHost, tracePropagation, callback) {
|
|
5153
4566
|
const sentryTrace = request.headers.get("sentry-trace") ?? void 0;
|
|
5154
4567
|
const baggage = request.headers.get("baggage") ?? void 0;
|
|
@@ -5167,9 +4580,6 @@ function isSandboxEgressForwardedRequest(request) {
|
|
|
5167
4580
|
);
|
|
5168
4581
|
}
|
|
5169
4582
|
async function proxySandboxEgressRequest(request, deps = {}) {
|
|
5170
|
-
return await proxySandboxEgressRequestImpl(request, deps);
|
|
5171
|
-
}
|
|
5172
|
-
async function proxySandboxEgressRequestImpl(request, deps) {
|
|
5173
4583
|
const oidcToken = request.headers.get(OIDC_TOKEN_HEADER)?.trim();
|
|
5174
4584
|
if (!oidcToken) {
|
|
5175
4585
|
return jsonError("Missing Vercel Sandbox OIDC token", 401);
|
|
@@ -5273,7 +4683,7 @@ async function proxySandboxEgressRequestImpl(request, deps) {
|
|
|
5273
4683
|
request,
|
|
5274
4684
|
upstreamUrl.hostname,
|
|
5275
4685
|
deps.tracePropagation ?? {},
|
|
5276
|
-
async () => await
|
|
4686
|
+
async () => await executeCredentialedEgressRequest({
|
|
5277
4687
|
activeEgressId,
|
|
5278
4688
|
credentialContext,
|
|
5279
4689
|
deps,
|
|
@@ -5283,314 +4693,6 @@ async function proxySandboxEgressRequestImpl(request, deps) {
|
|
|
5283
4693
|
})
|
|
5284
4694
|
);
|
|
5285
4695
|
}
|
|
5286
|
-
async function proxySandboxEgressVerifiedRequest(input) {
|
|
5287
|
-
const {
|
|
5288
|
-
activeEgressId,
|
|
5289
|
-
credentialContext,
|
|
5290
|
-
deps,
|
|
5291
|
-
provider,
|
|
5292
|
-
request,
|
|
5293
|
-
upstreamUrl
|
|
5294
|
-
} = input;
|
|
5295
|
-
let body;
|
|
5296
|
-
let bodyRead = false;
|
|
5297
|
-
if (isGrantSelectionBodyVisible({ provider, upstreamUrl })) {
|
|
5298
|
-
body = await requestBodyBytes(request);
|
|
5299
|
-
bodyRead = true;
|
|
5300
|
-
}
|
|
5301
|
-
const grantSelection = await selectSandboxEgressGrant({
|
|
5302
|
-
bodyText: requestBodyText(body),
|
|
5303
|
-
provider,
|
|
5304
|
-
method: request.method,
|
|
5305
|
-
upstreamUrl
|
|
5306
|
-
});
|
|
5307
|
-
let lease;
|
|
5308
|
-
try {
|
|
5309
|
-
lease = await sandboxEgressCredentialLease(
|
|
5310
|
-
provider,
|
|
5311
|
-
grantSelection,
|
|
5312
|
-
credentialContext
|
|
5313
|
-
);
|
|
5314
|
-
} catch (error) {
|
|
5315
|
-
if (error instanceof SandboxEgressCredentialError) {
|
|
5316
|
-
await setSandboxEgressAuthRequiredSignal(credentialContext, {
|
|
5317
|
-
provider: error.provider,
|
|
5318
|
-
grant: error.grant,
|
|
5319
|
-
kind: error.kind,
|
|
5320
|
-
...error.authorization ? { authorization: error.authorization } : {},
|
|
5321
|
-
message: error.message
|
|
5322
|
-
});
|
|
5323
|
-
const isAuthRequired = error.kind === "auth_required";
|
|
5324
|
-
logWarn(
|
|
5325
|
-
isAuthRequired ? "sandbox_egress_credential_needed" : "sandbox_egress_credential_unavailable",
|
|
5326
|
-
{},
|
|
5327
|
-
{
|
|
5328
|
-
...egressAttributes({
|
|
5329
|
-
egressId: activeEgressId,
|
|
5330
|
-
grantAccess: error.grant.access,
|
|
5331
|
-
grantName: error.grant.name,
|
|
5332
|
-
grantReason: error.grant.reason,
|
|
5333
|
-
host: upstreamUrl.hostname,
|
|
5334
|
-
method: request.method,
|
|
5335
|
-
path: upstreamUrl.pathname,
|
|
5336
|
-
provider: error.provider,
|
|
5337
|
-
status: 401
|
|
5338
|
-
}),
|
|
5339
|
-
...routingAttributes(request, upstreamUrl)
|
|
5340
|
-
},
|
|
5341
|
-
isAuthRequired ? "Sandbox egress grant needs user authorization before issuing a credential lease" : "Sandbox egress credential lease is unavailable for selected grant"
|
|
5342
|
-
);
|
|
5343
|
-
return authRequiredResponse({
|
|
5344
|
-
provider: error.provider,
|
|
5345
|
-
grant: error.grant,
|
|
5346
|
-
message: error.message
|
|
5347
|
-
});
|
|
5348
|
-
}
|
|
5349
|
-
throw error;
|
|
5350
|
-
}
|
|
5351
|
-
if (!hasSandboxEgressLeaseTransformForHost(lease, upstreamUrl.hostname)) {
|
|
5352
|
-
logWarn(
|
|
5353
|
-
"sandbox_egress_transform_missing",
|
|
5354
|
-
{},
|
|
5355
|
-
{
|
|
5356
|
-
...egressAttributes({
|
|
5357
|
-
egressId: activeEgressId,
|
|
5358
|
-
grantAccess: lease.grant.access,
|
|
5359
|
-
grantName: lease.grant.name,
|
|
5360
|
-
grantReason: lease.grant.reason,
|
|
5361
|
-
host: upstreamUrl.hostname,
|
|
5362
|
-
method: request.method,
|
|
5363
|
-
path: upstreamUrl.pathname,
|
|
5364
|
-
provider,
|
|
5365
|
-
status: 403
|
|
5366
|
-
}),
|
|
5367
|
-
"app.sandbox.egress.transform_domains": lease.headerTransforms.map(
|
|
5368
|
-
(transform) => transform.domain
|
|
5369
|
-
),
|
|
5370
|
-
...routingAttributes(request, upstreamUrl)
|
|
5371
|
-
},
|
|
5372
|
-
"Sandbox egress credential lease does not cover forwarded host"
|
|
5373
|
-
);
|
|
5374
|
-
return jsonError("Credential lease does not cover forwarded host", 403);
|
|
5375
|
-
}
|
|
5376
|
-
const fetchImpl = deps.fetch ?? fetch;
|
|
5377
|
-
const headers = requestHeaders(
|
|
5378
|
-
request,
|
|
5379
|
-
lease,
|
|
5380
|
-
upstreamUrl.hostname,
|
|
5381
|
-
deps.tracePropagation ?? {}
|
|
5382
|
-
);
|
|
5383
|
-
if (!bodyRead) {
|
|
5384
|
-
body = await requestBodyBytes(request);
|
|
5385
|
-
}
|
|
5386
|
-
const intercepted = await deps.interceptHttp?.({
|
|
5387
|
-
provider,
|
|
5388
|
-
request: new Request(upstreamUrl, {
|
|
5389
|
-
method: request.method,
|
|
5390
|
-
headers,
|
|
5391
|
-
...body !== void 0 ? { body } : {}
|
|
5392
|
-
}),
|
|
5393
|
-
upstreamUrl
|
|
5394
|
-
});
|
|
5395
|
-
if (intercepted) {
|
|
5396
|
-
return intercepted;
|
|
5397
|
-
}
|
|
5398
|
-
const upstream = await fetchImpl(upstreamUrl, {
|
|
5399
|
-
method: request.method,
|
|
5400
|
-
headers,
|
|
5401
|
-
...body !== void 0 ? { body } : {},
|
|
5402
|
-
redirect: "manual"
|
|
5403
|
-
});
|
|
5404
|
-
try {
|
|
5405
|
-
const effects = await onPluginEgressResponse({
|
|
5406
|
-
provider,
|
|
5407
|
-
grant: lease.grant,
|
|
5408
|
-
method: request.method,
|
|
5409
|
-
upstreamUrl,
|
|
5410
|
-
response: {
|
|
5411
|
-
headers: new Headers(upstream.headers),
|
|
5412
|
-
readText: async (maxBytes) => await responseTextWithinLimit(upstream, maxBytes),
|
|
5413
|
-
status: upstream.status
|
|
5414
|
-
}
|
|
5415
|
-
});
|
|
5416
|
-
if (effects.permissionDenied) {
|
|
5417
|
-
await setSandboxEgressPermissionDeniedSignal(credentialContext, {
|
|
5418
|
-
provider,
|
|
5419
|
-
grant: lease.grant,
|
|
5420
|
-
...lease.account ? { account: lease.account } : {},
|
|
5421
|
-
message: effects.permissionDenied.message,
|
|
5422
|
-
source: "upstream",
|
|
5423
|
-
status: upstream.status,
|
|
5424
|
-
upstreamHost: upstreamUrl.hostname,
|
|
5425
|
-
upstreamPath: displayedUpstreamPath(upstreamUrl),
|
|
5426
|
-
...provider === "github" ? githubPermissionHeaders(upstream) : {}
|
|
5427
|
-
});
|
|
5428
|
-
logWarn(
|
|
5429
|
-
"sandbox_egress_upstream_permission_classified",
|
|
5430
|
-
{},
|
|
5431
|
-
{
|
|
5432
|
-
...egressAttributes({
|
|
5433
|
-
egressId: activeEgressId,
|
|
5434
|
-
grantAccess: lease.grant.access,
|
|
5435
|
-
grantName: lease.grant.name,
|
|
5436
|
-
grantReason: lease.grant.reason,
|
|
5437
|
-
host: upstreamUrl.hostname,
|
|
5438
|
-
method: request.method,
|
|
5439
|
-
path: upstreamUrl.pathname,
|
|
5440
|
-
provider,
|
|
5441
|
-
status: upstream.status
|
|
5442
|
-
}),
|
|
5443
|
-
...routingAttributes(request, upstreamUrl),
|
|
5444
|
-
...upstreamPermissionAttributes(provider, upstream)
|
|
5445
|
-
},
|
|
5446
|
-
"Sandbox egress plugin classified upstream response as permission denied"
|
|
5447
|
-
);
|
|
5448
|
-
}
|
|
5449
|
-
} catch (error) {
|
|
5450
|
-
if (!isEgressAuthRequired(error)) {
|
|
5451
|
-
throw error;
|
|
5452
|
-
}
|
|
5453
|
-
await clearSandboxEgressCredentialLease(
|
|
5454
|
-
provider,
|
|
5455
|
-
lease.grant.name,
|
|
5456
|
-
credentialContext
|
|
5457
|
-
);
|
|
5458
|
-
await setSandboxEgressAuthRequiredSignal(credentialContext, {
|
|
5459
|
-
provider,
|
|
5460
|
-
grant: lease.grant,
|
|
5461
|
-
kind: "auth_required",
|
|
5462
|
-
...error.authorization ?? lease.authorization ? { authorization: error.authorization ?? lease.authorization } : {},
|
|
5463
|
-
message: error.message
|
|
5464
|
-
});
|
|
5465
|
-
logWarn(
|
|
5466
|
-
"sandbox_egress_upstream_auth_required_classified",
|
|
5467
|
-
{},
|
|
5468
|
-
{
|
|
5469
|
-
...egressAttributes({
|
|
5470
|
-
egressId: activeEgressId,
|
|
5471
|
-
grantAccess: lease.grant.access,
|
|
5472
|
-
grantName: lease.grant.name,
|
|
5473
|
-
grantReason: lease.grant.reason,
|
|
5474
|
-
host: upstreamUrl.hostname,
|
|
5475
|
-
method: request.method,
|
|
5476
|
-
path: upstreamUrl.pathname,
|
|
5477
|
-
provider,
|
|
5478
|
-
status: upstream.status
|
|
5479
|
-
}),
|
|
5480
|
-
...routingAttributes(request, upstreamUrl),
|
|
5481
|
-
...upstreamPermissionAttributes(provider, upstream)
|
|
5482
|
-
},
|
|
5483
|
-
"Sandbox egress plugin classified upstream response as auth required"
|
|
5484
|
-
);
|
|
5485
|
-
await upstream.body?.cancel().catch(() => void 0);
|
|
5486
|
-
return authRequiredResponse({
|
|
5487
|
-
provider,
|
|
5488
|
-
grant: lease.grant,
|
|
5489
|
-
message: error.message
|
|
5490
|
-
});
|
|
5491
|
-
}
|
|
5492
|
-
logSandboxEgressUpstreamRequest({
|
|
5493
|
-
egressId: activeEgressId,
|
|
5494
|
-
grantAccess: lease.grant.access,
|
|
5495
|
-
grantName: lease.grant.name,
|
|
5496
|
-
grantReason: lease.grant.reason,
|
|
5497
|
-
provider,
|
|
5498
|
-
request,
|
|
5499
|
-
upstream,
|
|
5500
|
-
upstreamUrl
|
|
5501
|
-
});
|
|
5502
|
-
if (upstream.status >= 400) {
|
|
5503
|
-
logWarn(
|
|
5504
|
-
"sandbox_egress_upstream_error_response",
|
|
5505
|
-
{},
|
|
5506
|
-
{
|
|
5507
|
-
...egressAttributes({
|
|
5508
|
-
egressId: activeEgressId,
|
|
5509
|
-
grantAccess: lease.grant.access,
|
|
5510
|
-
grantName: lease.grant.name,
|
|
5511
|
-
grantReason: lease.grant.reason,
|
|
5512
|
-
host: upstreamUrl.hostname,
|
|
5513
|
-
method: request.method,
|
|
5514
|
-
path: upstreamUrl.pathname,
|
|
5515
|
-
provider,
|
|
5516
|
-
status: upstream.status
|
|
5517
|
-
}),
|
|
5518
|
-
...routingAttributes(request, upstreamUrl),
|
|
5519
|
-
...upstreamPermissionAttributes(provider, upstream),
|
|
5520
|
-
"error.type": `http_${upstream.status}`
|
|
5521
|
-
},
|
|
5522
|
-
`Sandbox egress upstream returned HTTP ${upstream.status}`
|
|
5523
|
-
);
|
|
5524
|
-
}
|
|
5525
|
-
if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS || upstream.status === UPSTREAM_PERMISSION_REJECTION_STATUS) {
|
|
5526
|
-
logWarn(
|
|
5527
|
-
"sandbox_egress_upstream_auth_rejected",
|
|
5528
|
-
{},
|
|
5529
|
-
{
|
|
5530
|
-
...egressAttributes({
|
|
5531
|
-
egressId: activeEgressId,
|
|
5532
|
-
grantAccess: lease.grant.access,
|
|
5533
|
-
grantName: lease.grant.name,
|
|
5534
|
-
grantReason: lease.grant.reason,
|
|
5535
|
-
host: upstreamUrl.hostname,
|
|
5536
|
-
method: request.method,
|
|
5537
|
-
path: upstreamUrl.pathname,
|
|
5538
|
-
provider,
|
|
5539
|
-
status: upstream.status
|
|
5540
|
-
}),
|
|
5541
|
-
...routingAttributes(request, upstreamUrl),
|
|
5542
|
-
...upstreamPermissionAttributes(provider, upstream),
|
|
5543
|
-
...upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? {
|
|
5544
|
-
"app.sandbox.egress.www_authenticate": upstream.headers.get("www-authenticate") ?? void 0
|
|
5545
|
-
} : {}
|
|
5546
|
-
},
|
|
5547
|
-
upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? "Sandbox egress upstream auth rejected injected credential" : "Sandbox egress upstream permission denied"
|
|
5548
|
-
);
|
|
5549
|
-
if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS) {
|
|
5550
|
-
await clearSandboxEgressCredentialLease(
|
|
5551
|
-
provider,
|
|
5552
|
-
lease.grant.name,
|
|
5553
|
-
credentialContext
|
|
5554
|
-
);
|
|
5555
|
-
await setSandboxEgressAuthRequiredSignal(credentialContext, {
|
|
5556
|
-
provider,
|
|
5557
|
-
grant: lease.grant,
|
|
5558
|
-
kind: "auth_required",
|
|
5559
|
-
...lease.authorization ? { authorization: lease.authorization } : {},
|
|
5560
|
-
message: `Provider rejected the injected ${provider} credential.`
|
|
5561
|
-
});
|
|
5562
|
-
await upstream.body?.cancel().catch(() => void 0);
|
|
5563
|
-
return authRequiredResponse({
|
|
5564
|
-
provider,
|
|
5565
|
-
grant: lease.grant,
|
|
5566
|
-
message: `Provider rejected the injected ${provider} credential.
|
|
5567
|
-
`
|
|
5568
|
-
});
|
|
5569
|
-
} else {
|
|
5570
|
-
await clearSandboxEgressCredentialLease(
|
|
5571
|
-
provider,
|
|
5572
|
-
lease.grant.name,
|
|
5573
|
-
credentialContext
|
|
5574
|
-
);
|
|
5575
|
-
await setSandboxEgressPermissionDeniedSignal(credentialContext, {
|
|
5576
|
-
provider,
|
|
5577
|
-
grant: lease.grant,
|
|
5578
|
-
...lease.account ? { account: lease.account } : {},
|
|
5579
|
-
message: permissionDeniedMessage(provider, lease.grant),
|
|
5580
|
-
source: "upstream",
|
|
5581
|
-
status: UPSTREAM_PERMISSION_REJECTION_STATUS,
|
|
5582
|
-
upstreamHost: upstreamUrl.hostname,
|
|
5583
|
-
upstreamPath: displayedUpstreamPath(upstreamUrl),
|
|
5584
|
-
...provider === "github" ? githubPermissionHeaders(upstream) : {}
|
|
5585
|
-
});
|
|
5586
|
-
}
|
|
5587
|
-
}
|
|
5588
|
-
return new Response(upstream.body, {
|
|
5589
|
-
status: upstream.status,
|
|
5590
|
-
statusText: upstream.statusText,
|
|
5591
|
-
headers: responseHeaders(upstream)
|
|
5592
|
-
});
|
|
5593
|
-
}
|
|
5594
4696
|
|
|
5595
4697
|
// src/handlers/sandbox-egress-proxy.ts
|
|
5596
4698
|
async function ALL(request, options = {}) {
|