@sentry/junior 0.51.0 → 0.52.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.
Files changed (2) hide show
  1. package/dist/app.js +218 -166
  2. package/package.json +1 -1
package/dist/app.js CHANGED
@@ -9138,84 +9138,51 @@ function createTracedStreamFn(base = streamSimple) {
9138
9138
  // src/chat/sandbox/sandbox.ts
9139
9139
  import fs4 from "fs/promises";
9140
9140
 
9141
- // src/chat/sandbox/egress-policy.ts
9142
- function matchesSandboxEgressDomain(host, domain) {
9143
- return host.toLowerCase() === domain.toLowerCase();
9144
- }
9145
- function manifestDomains(manifest) {
9146
- const domains = /* @__PURE__ */ new Set([
9147
- ...manifest.credentials?.domains ?? [],
9148
- ...manifest.domains ?? []
9149
- ]);
9150
- return [...domains].sort((left, right) => left.localeCompare(right));
9151
- }
9152
- function providerEntries() {
9153
- return getPluginProviders().map((plugin) => ({
9154
- provider: plugin.manifest.name,
9155
- domains: manifestDomains(plugin.manifest)
9156
- })).filter((entry) => entry.domains.length > 0).sort((left, right) => left.provider.localeCompare(right.provider));
9157
- }
9158
- function resolveSandboxEgressProviderForHost(host) {
9159
- return providerEntries().find(
9160
- (entry) => entry.domains.some((domain) => matchesSandboxEgressDomain(host, domain))
9161
- )?.provider;
9162
- }
9163
- function sandboxProxyUrl() {
9164
- const baseUrl = resolveBaseUrl();
9165
- if (!baseUrl) {
9166
- throw new Error(
9167
- "Cannot determine base URL for sandbox credential egress (set JUNIOR_BASE_URL or deploy to Vercel)"
9168
- );
9169
- }
9170
- return new URL("/", baseUrl).toString();
9141
+ // src/chat/sandbox/egress-session.ts
9142
+ import { createHmac, randomUUID as randomUUID3, timingSafeEqual } from "crypto";
9143
+ var SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress";
9144
+ var SANDBOX_EGRESS_TOKEN_VERSION = "v1";
9145
+ var SANDBOX_EGRESS_LEASE_PREFIX = "sandbox-egress-lease";
9146
+ var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
9147
+ function leaseKey(provider, context) {
9148
+ return `${SANDBOX_EGRESS_LEASE_PREFIX}:${provider}:${context.requesterId}:${context.egressId}:${context.contextId}`;
9171
9149
  }
9172
- function buildSandboxEgressNetworkPolicy() {
9173
- const allow = {
9174
- "*": []
9175
- };
9176
- const entries = providerEntries();
9177
- if (entries.length === 0) {
9178
- return { allow };
9150
+ function getSandboxEgressSecret() {
9151
+ const explicit = process.env.JUNIOR_SANDBOX_EGRESS_SECRET?.trim();
9152
+ if (explicit) {
9153
+ return explicit;
9179
9154
  }
9180
- const forwardURL = sandboxProxyUrl();
9181
- for (const entry of entries) {
9182
- for (const domain of entry.domains) {
9183
- allow[domain] = [{ forwardURL }];
9184
- }
9155
+ const sharedInternal = process.env.JUNIOR_INTERNAL_RESUME_SECRET?.trim();
9156
+ if (sharedInternal) {
9157
+ return sharedInternal;
9185
9158
  }
9186
- return { allow };
9159
+ throw new Error(
9160
+ "Cannot determine sandbox egress secret (set JUNIOR_SANDBOX_EGRESS_SECRET or JUNIOR_INTERNAL_RESUME_SECRET)"
9161
+ );
9187
9162
  }
9188
- async function resolveSandboxCommandEnvironment() {
9189
- const env = {};
9190
- for (const plugin of getPluginProviders().sort(
9191
- (left, right) => left.manifest.name.localeCompare(right.manifest.name)
9192
- )) {
9193
- Object.assign(env, resolvePluginCommandEnv(plugin.manifest));
9194
- const credentials = plugin.manifest.credentials;
9195
- if (credentials) {
9196
- env[credentials.authTokenEnv] = resolveAuthTokenPlaceholder(credentials);
9197
- }
9198
- }
9199
- return env;
9163
+ function base64Url(input) {
9164
+ return Buffer.from(input, "utf8").toString("base64url");
9200
9165
  }
9201
-
9202
- // src/chat/sandbox/egress-session.ts
9203
- import { randomUUID as randomUUID3 } from "crypto";
9204
- var SANDBOX_EGRESS_SESSION_PREFIX = "sandbox-egress-session";
9205
- var SANDBOX_EGRESS_LEASE_PREFIX = "sandbox-egress-lease";
9206
- var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
9207
- function sessionKey2(egressId) {
9208
- return `${SANDBOX_EGRESS_SESSION_PREFIX}:${egressId}`;
9166
+ function fromBase64Url(input) {
9167
+ return Buffer.from(input, "base64url").toString("utf8");
9209
9168
  }
9210
- function leaseKey(egressId, provider, session) {
9211
- return `${SANDBOX_EGRESS_LEASE_PREFIX}:${egressId}:${provider}:${session.requesterId}:${session.activationId}`;
9169
+ function signPayload(payload) {
9170
+ return createHmac("sha256", getSandboxEgressSecret()).update(payload).digest("base64url");
9212
9171
  }
9213
- function parseSession(value) {
9172
+ function timingSafeMatch(expected, actual) {
9173
+ const expectedBuffer = Buffer.from(expected);
9174
+ const actualBuffer = Buffer.from(actual);
9175
+ if (expectedBuffer.length !== actualBuffer.length) {
9176
+ return false;
9177
+ }
9178
+ return timingSafeEqual(expectedBuffer, actualBuffer);
9179
+ }
9180
+ function parseRequesterContext(value) {
9214
9181
  if (!value || typeof value !== "object") {
9215
9182
  return void 0;
9216
9183
  }
9217
9184
  const record = value;
9218
- if (typeof record.requesterId !== "string" || typeof record.expiresAtMs !== "number" || !Number.isFinite(record.expiresAtMs) || typeof record.activationId !== "string" || !record.activationId) {
9185
+ if (typeof record.requesterId !== "string" || !record.requesterId || typeof record.egressId !== "string" || !record.egressId || typeof record.expiresAtMs !== "number" || !Number.isFinite(record.expiresAtMs) || typeof record.contextId !== "string" || !record.contextId) {
9219
9186
  return void 0;
9220
9187
  }
9221
9188
  if (record.expiresAtMs <= Date.now()) {
@@ -9223,8 +9190,9 @@ function parseSession(value) {
9223
9190
  }
9224
9191
  return {
9225
9192
  requesterId: record.requesterId,
9193
+ egressId: record.egressId,
9226
9194
  expiresAtMs: record.expiresAtMs,
9227
- activationId: record.activationId
9195
+ contextId: record.contextId
9228
9196
  };
9229
9197
  }
9230
9198
  function parseLease(value) {
@@ -9253,50 +9221,127 @@ function parseLease(value) {
9253
9221
  headerTransforms
9254
9222
  };
9255
9223
  }
9256
- async function upsertSandboxEgressSession(input) {
9257
- const state = getStateAdapter();
9258
- await state.connect();
9224
+ function createSandboxEgressRequesterToken(input) {
9259
9225
  const ttlMs = Math.max(1, input.ttlMs ?? DEFAULT_SESSION_TTL_MS);
9260
9226
  const now = Date.now();
9261
- const session = {
9227
+ const context = {
9262
9228
  requesterId: input.requesterId,
9229
+ egressId: input.egressId,
9263
9230
  expiresAtMs: now + ttlMs,
9264
- activationId: randomUUID3()
9231
+ contextId: randomUUID3()
9265
9232
  };
9266
- await state.set(sessionKey2(input.egressId), session, ttlMs);
9233
+ const payload = `${SANDBOX_EGRESS_TOKEN_VERSION}.${base64Url(
9234
+ JSON.stringify(context)
9235
+ )}`;
9236
+ return `${payload}.${signPayload(payload)}`;
9267
9237
  }
9268
- async function clearSandboxEgressSession(egressId) {
9269
- const state = getStateAdapter();
9270
- await state.connect();
9271
- await state.delete(sessionKey2(egressId));
9272
- }
9273
- async function getSandboxEgressSession(egressId) {
9274
- const state = getStateAdapter();
9275
- await state.connect();
9276
- return parseSession(await state.get(sessionKey2(egressId)));
9238
+ function parseSandboxEgressRequesterToken(token) {
9239
+ if (!token) {
9240
+ return void 0;
9241
+ }
9242
+ const parts = token.split(".");
9243
+ if (parts.length !== 3 || parts[0] !== SANDBOX_EGRESS_TOKEN_VERSION) {
9244
+ return void 0;
9245
+ }
9246
+ const encodedSession = parts[1];
9247
+ const signature = parts[2];
9248
+ if (!encodedSession || !signature) {
9249
+ return void 0;
9250
+ }
9251
+ const payload = `${parts[0]}.${encodedSession}`;
9252
+ if (!timingSafeMatch(signPayload(payload), signature)) {
9253
+ return void 0;
9254
+ }
9255
+ try {
9256
+ return parseRequesterContext(JSON.parse(fromBase64Url(encodedSession)));
9257
+ } catch {
9258
+ return void 0;
9259
+ }
9277
9260
  }
9278
- async function setSandboxEgressCredentialLease(egressId, session, lease) {
9261
+ async function setSandboxEgressCredentialLease(context, lease) {
9279
9262
  const leaseExpiresAtMs = Date.parse(lease.expiresAt);
9280
9263
  if (!Number.isFinite(leaseExpiresAtMs) || leaseExpiresAtMs <= Date.now()) {
9281
9264
  return;
9282
9265
  }
9283
9266
  const ttlMs = Math.max(
9284
9267
  1,
9285
- Math.min(leaseExpiresAtMs, session.expiresAtMs) - Date.now()
9268
+ Math.min(leaseExpiresAtMs, context.expiresAtMs) - Date.now()
9286
9269
  );
9287
9270
  const state = getStateAdapter();
9288
9271
  await state.connect();
9289
- await state.set(leaseKey(egressId, lease.provider, session), lease, ttlMs);
9272
+ await state.set(leaseKey(lease.provider, context), lease, ttlMs);
9290
9273
  }
9291
- async function getSandboxEgressCredentialLease(egressId, provider, session) {
9274
+ async function getSandboxEgressCredentialLease(provider, context) {
9292
9275
  const state = getStateAdapter();
9293
9276
  await state.connect();
9294
- return parseLease(await state.get(leaseKey(egressId, provider, session)));
9277
+ return parseLease(await state.get(leaseKey(provider, context)));
9295
9278
  }
9296
- async function clearSandboxEgressCredentialLease(egressId, provider, session) {
9279
+ async function clearSandboxEgressCredentialLease(provider, context) {
9297
9280
  const state = getStateAdapter();
9298
9281
  await state.connect();
9299
- await state.delete(leaseKey(egressId, provider, session));
9282
+ await state.delete(leaseKey(provider, context));
9283
+ }
9284
+
9285
+ // src/chat/sandbox/egress-policy.ts
9286
+ function matchesSandboxEgressDomain(host, domain) {
9287
+ return host.toLowerCase() === domain.toLowerCase();
9288
+ }
9289
+ function manifestDomains(manifest) {
9290
+ const domains = /* @__PURE__ */ new Set([
9291
+ ...manifest.credentials?.domains ?? [],
9292
+ ...manifest.domains ?? []
9293
+ ]);
9294
+ return [...domains].sort((left, right) => left.localeCompare(right));
9295
+ }
9296
+ function providerEntries() {
9297
+ return getPluginProviders().map((plugin) => ({
9298
+ provider: plugin.manifest.name,
9299
+ domains: manifestDomains(plugin.manifest)
9300
+ })).filter((entry) => entry.domains.length > 0).sort((left, right) => left.provider.localeCompare(right.provider));
9301
+ }
9302
+ function resolveSandboxEgressProviderForHost(host) {
9303
+ return providerEntries().find(
9304
+ (entry) => entry.domains.some((domain) => matchesSandboxEgressDomain(host, domain))
9305
+ )?.provider;
9306
+ }
9307
+ function sandboxProxyUrl(requesterToken) {
9308
+ const baseUrl = resolveBaseUrl();
9309
+ if (!baseUrl) {
9310
+ throw new Error(
9311
+ "Cannot determine base URL for sandbox credential egress (set JUNIOR_BASE_URL or deploy to Vercel)"
9312
+ );
9313
+ }
9314
+ const path11 = requesterToken ? `${SANDBOX_EGRESS_PROXY_PATH}/${requesterToken}` : SANDBOX_EGRESS_PROXY_PATH;
9315
+ return new URL(path11, baseUrl).toString();
9316
+ }
9317
+ function buildSandboxEgressNetworkPolicy(input) {
9318
+ const allow = {
9319
+ "*": []
9320
+ };
9321
+ const entries = providerEntries();
9322
+ if (entries.length === 0) {
9323
+ return { allow };
9324
+ }
9325
+ const forwardURL = sandboxProxyUrl(input?.requesterToken);
9326
+ for (const entry of entries) {
9327
+ for (const domain of entry.domains) {
9328
+ allow[domain] = [{ forwardURL }];
9329
+ }
9330
+ }
9331
+ return { allow };
9332
+ }
9333
+ async function resolveSandboxCommandEnvironment() {
9334
+ const env = {};
9335
+ for (const plugin of getPluginProviders().sort(
9336
+ (left, right) => left.manifest.name.localeCompare(right.manifest.name)
9337
+ )) {
9338
+ Object.assign(env, resolvePluginCommandEnv(plugin.manifest));
9339
+ const credentials = plugin.manifest.credentials;
9340
+ if (credentials) {
9341
+ env[credentials.authTokenEnv] = resolveAuthTokenPlaceholder(credentials);
9342
+ }
9343
+ }
9344
+ return env;
9300
9345
  }
9301
9346
 
9302
9347
  // src/chat/sandbox/http-error-details.ts
@@ -10601,7 +10646,6 @@ function createSandboxSessionManager(options) {
10601
10646
  }
10602
10647
  };
10603
10648
  const buildToolExecutors = async (sandboxInstance) => {
10604
- const activeSandboxId = sandboxInstance.sandboxId;
10605
10649
  const toolkit = await withSandboxSpan(
10606
10650
  "sandbox.bash_tool.init",
10607
10651
  "sandbox.tool.init",
@@ -10621,35 +10665,9 @@ function createSandboxSessionManager(options) {
10621
10665
  }
10622
10666
  return {
10623
10667
  bash: async (input) => {
10624
- const commandEgressId = sandboxInstance.sandboxEgressId;
10625
10668
  let timedOut = false;
10626
10669
  let timeoutId;
10627
- let commandFinished = false;
10628
- const finishCommand = async () => {
10629
- if (commandFinished) {
10630
- return;
10631
- }
10632
- commandFinished = true;
10633
- await options?.afterCommand?.(commandEgressId);
10634
- await refreshNetworkPolicy(sandboxInstance);
10635
- };
10636
- const finishCommandBestEffort = async () => {
10637
- try {
10638
- await finishCommand();
10639
- } catch (error) {
10640
- logWarn(
10641
- "sandbox_command_cleanup_failed",
10642
- traceContext,
10643
- {
10644
- "app.sandbox.id": activeSandboxId,
10645
- "error.type": error instanceof Error ? error.name : "sandbox_command_cleanup_error"
10646
- },
10647
- "Sandbox command cleanup failed"
10648
- );
10649
- }
10650
- };
10651
10670
  try {
10652
- await options?.beforeCommand?.(commandEgressId);
10653
10671
  await refreshNetworkPolicy(sandboxInstance);
10654
10672
  const sandboxCommandEnv = await resolveCommandEnv();
10655
10673
  const script = buildNonInteractiveShellScript(input.command, {
@@ -10671,7 +10689,6 @@ function createSandboxSessionManager(options) {
10671
10689
  cwd: SANDBOX_WORKSPACE_ROOT,
10672
10690
  ...controller ? { signal: controller.signal } : {}
10673
10691
  });
10674
- await finishCommandBestEffort();
10675
10692
  return await readCommandOutput(commandResult2);
10676
10693
  } catch (error) {
10677
10694
  if (timedOut) {
@@ -10692,7 +10709,6 @@ function createSandboxSessionManager(options) {
10692
10709
  if (timeoutId) {
10693
10710
  clearTimeout(timeoutId);
10694
10711
  }
10695
- await finishCommandBestEffort();
10696
10712
  }
10697
10713
  },
10698
10714
  readFile: async (input) => await executeReadFile(input, {
@@ -10781,25 +10797,40 @@ function createSandboxExecutor(options) {
10781
10797
  let referenceFiles = [];
10782
10798
  const traceContext = options?.traceContext ?? {};
10783
10799
  const credentialEgress = options?.credentialEgress;
10784
- const authorizeSandboxEgressForCommand = credentialEgress ? async (egressId) => {
10785
- await upsertSandboxEgressSession({
10786
- egressId,
10800
+ const sandboxEgressTokenTtlMs = Math.max(
10801
+ 1,
10802
+ options?.timeoutMs ?? 1e3 * 60 * 30
10803
+ );
10804
+ const sandboxEgressRequesterTokens = /* @__PURE__ */ new Map();
10805
+ const sandboxEgressRequesterTokenFor = (egressId) => {
10806
+ const cached = sandboxEgressRequesterTokens.get(egressId);
10807
+ if (cached && cached.expiresAtMs > Date.now()) {
10808
+ return cached.token;
10809
+ }
10810
+ if (!credentialEgress) {
10811
+ throw new Error("Sandbox credential egress is not configured");
10812
+ }
10813
+ const now = Date.now();
10814
+ const token = createSandboxEgressRequesterToken({
10787
10815
  requesterId: credentialEgress.requesterId,
10788
- ttlMs: options?.timeoutMs
10816
+ egressId,
10817
+ ttlMs: sandboxEgressTokenTtlMs
10789
10818
  });
10790
- } : void 0;
10791
- const clearSandboxEgressForCommand = credentialEgress ? async (egressId) => {
10792
- await clearSandboxEgressSession(egressId);
10793
- } : void 0;
10819
+ sandboxEgressRequesterTokens.set(egressId, {
10820
+ expiresAtMs: now + sandboxEgressTokenTtlMs,
10821
+ token
10822
+ });
10823
+ return token;
10824
+ };
10794
10825
  const sessionManager = createSandboxSessionManager({
10795
10826
  sandboxId: options?.sandboxId,
10796
10827
  sandboxDependencyProfileHash: options?.sandboxDependencyProfileHash,
10797
10828
  timeoutMs: options?.timeoutMs,
10798
10829
  traceContext,
10799
10830
  commandEnv: credentialEgress ? async () => await resolveSandboxCommandEnvironment() : void 0,
10800
- createNetworkPolicy: credentialEgress ? buildSandboxEgressNetworkPolicy : void 0,
10801
- beforeCommand: authorizeSandboxEgressForCommand,
10802
- afterCommand: clearSandboxEgressForCommand,
10831
+ createNetworkPolicy: credentialEgress ? (egressId) => buildSandboxEgressNetworkPolicy({
10832
+ requesterToken: sandboxEgressRequesterTokenFor(egressId)
10833
+ }) : void 0,
10803
10834
  onSandboxAcquired: async (sandbox) => {
10804
10835
  await options?.onSandboxAcquired?.(sandbox);
10805
10836
  }
@@ -14927,7 +14958,7 @@ async function persistAuthPauseTurnState(args) {
14927
14958
  }
14928
14959
 
14929
14960
  // src/chat/services/timeout-resume.ts
14930
- import { createHmac, timingSafeEqual } from "crypto";
14961
+ import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
14931
14962
  var TURN_TIMEOUT_RESUME_PATH = "/api/internal/turn-resume";
14932
14963
  var TURN_TIMEOUT_RESUME_SIGNATURE_VERSION = "v1";
14933
14964
  var TURN_TIMEOUT_RESUME_MAX_SKEW_MS = 5 * 60 * 1e3;
@@ -14962,16 +14993,16 @@ function buildSignedPayload(timestamp, body) {
14962
14993
  return `${timestamp}:${body}`;
14963
14994
  }
14964
14995
  function signTurnTimeoutResumeBody(secret, timestamp, body) {
14965
- const digest = createHmac("sha256", secret).update(buildSignedPayload(timestamp, body)).digest("hex");
14996
+ const digest = createHmac2("sha256", secret).update(buildSignedPayload(timestamp, body)).digest("hex");
14966
14997
  return `${TURN_TIMEOUT_RESUME_SIGNATURE_VERSION}=${digest}`;
14967
14998
  }
14968
- function timingSafeMatch(expected, actual) {
14999
+ function timingSafeMatch2(expected, actual) {
14969
15000
  const expectedBuffer = Buffer.from(expected);
14970
15001
  const actualBuffer = Buffer.from(actual);
14971
15002
  if (expectedBuffer.length !== actualBuffer.length) {
14972
15003
  return false;
14973
15004
  }
14974
- return timingSafeEqual(expectedBuffer, actualBuffer);
15005
+ return timingSafeEqual2(expectedBuffer, actualBuffer);
14975
15006
  }
14976
15007
  function parseTurnTimeoutResumeRequest(value) {
14977
15008
  if (!value || typeof value !== "object") {
@@ -15034,7 +15065,7 @@ async function verifyTurnTimeoutResumeRequest(request) {
15034
15065
  }
15035
15066
  const body = await request.text();
15036
15067
  const expectedSignature = signTurnTimeoutResumeBody(secret, timestamp, body);
15037
- if (!timingSafeMatch(expectedSignature, signature)) {
15068
+ if (!timingSafeMatch2(expectedSignature, signature)) {
15038
15069
  return void 0;
15039
15070
  }
15040
15071
  try {
@@ -16069,10 +16100,32 @@ function egressAttributes(input) {
16069
16100
  ...input.status ? { "http.response.status_code": input.status } : {}
16070
16101
  };
16071
16102
  }
16103
+ function requesterTokenFromRequest(request) {
16104
+ const pathname = new URL(request.url).pathname;
16105
+ const prefix = `${SANDBOX_EGRESS_PROXY_PATH}/`;
16106
+ if (!pathname.startsWith(prefix)) {
16107
+ return void 0;
16108
+ }
16109
+ const token = pathname.slice(prefix.length).split("/")[0];
16110
+ if (!token) {
16111
+ return void 0;
16112
+ }
16113
+ try {
16114
+ return decodeURIComponent(token);
16115
+ } catch {
16116
+ return void 0;
16117
+ }
16118
+ }
16119
+ function redactedProxyPath(pathname) {
16120
+ if (pathname.startsWith(`${SANDBOX_EGRESS_PROXY_PATH}/`)) {
16121
+ return `${SANDBOX_EGRESS_PROXY_PATH}/<token>`;
16122
+ }
16123
+ return pathname;
16124
+ }
16072
16125
  function routingAttributes(request, upstreamUrl) {
16073
16126
  const proxyUrl = new URL(request.url);
16074
16127
  const attributes = {
16075
- "app.sandbox.egress.proxy_path": proxyUrl.pathname
16128
+ "app.sandbox.egress.proxy_path": redactedProxyPath(proxyUrl.pathname)
16076
16129
  };
16077
16130
  if (upstreamUrl) {
16078
16131
  attributes["app.sandbox.egress.upstream_path"] = upstreamUrl.pathname;
@@ -16125,22 +16178,23 @@ function normalizePort(value) {
16125
16178
  function sandboxIdFromPayload(payload) {
16126
16179
  return typeof payload.sandbox_id === "string" ? payload.sandbox_id : void 0;
16127
16180
  }
16181
+ function normalizedForwardedPath(path11) {
16182
+ if (!path11.startsWith("/") || path11.startsWith("//") || path11.includes("#") || /[\r\n]/.test(path11)) {
16183
+ return { ok: false, error: "Invalid forwarded path" };
16184
+ }
16185
+ try {
16186
+ const url = new URL(path11, "https://sandbox-forwarded.local");
16187
+ return { ok: true, path: `${url.pathname}${url.search}` };
16188
+ } catch {
16189
+ return { ok: false, error: "Invalid forwarded path" };
16190
+ }
16191
+ }
16128
16192
  function upstreamPath(request) {
16129
16193
  const forwardedPath = request.headers.get(FORWARDED_PATH_HEADER);
16130
- if (forwardedPath?.trim()) {
16131
- const path11 = forwardedPath.trim();
16132
- if (!path11.startsWith("/") || path11.startsWith("//") || path11.includes("#") || /[\r\n]/.test(path11)) {
16133
- return { ok: false, error: "Invalid forwarded path" };
16134
- }
16135
- try {
16136
- const url2 = new URL(path11, "https://sandbox-forwarded.local");
16137
- return { ok: true, path: `${url2.pathname}${url2.search}` };
16138
- } catch {
16139
- return { ok: false, error: "Invalid forwarded path" };
16140
- }
16194
+ if (!forwardedPath?.trim()) {
16195
+ return { ok: false, error: "Missing forwarded path" };
16141
16196
  }
16142
- const url = new URL(request.url);
16143
- return { ok: true, path: `${url.pathname}${url.search}` };
16197
+ return normalizedForwardedPath(forwardedPath.trim());
16144
16198
  }
16145
16199
  function buildUpstreamUrl(request) {
16146
16200
  const forwardedHost = request.headers.get(FORWARDED_HOST_HEADER);
@@ -16212,18 +16266,14 @@ function responseHeaders(upstream) {
16212
16266
  });
16213
16267
  return headers;
16214
16268
  }
16215
- async function credentialLease(egressId, provider, session) {
16216
- const cached = await getSandboxEgressCredentialLease(
16217
- egressId,
16218
- provider,
16219
- session
16220
- );
16269
+ async function credentialLease(provider, context) {
16270
+ const cached = await getSandboxEgressCredentialLease(provider, context);
16221
16271
  if (cached) {
16222
16272
  return cached;
16223
16273
  }
16224
16274
  const lease = await issueProviderCredentialLease({
16225
16275
  provider,
16226
- requesterId: session.requesterId,
16276
+ requesterId: context.requesterId,
16227
16277
  reason: `sandbox-egress:${provider}`
16228
16278
  });
16229
16279
  const headerTransforms = lease.headerTransforms ?? [];
@@ -16237,7 +16287,7 @@ async function credentialLease(egressId, provider, session) {
16237
16287
  expiresAt: lease.expiresAt,
16238
16288
  headerTransforms
16239
16289
  };
16240
- await setSandboxEgressCredentialLease(egressId, session, cachedLease);
16290
+ await setSandboxEgressCredentialLease(context, cachedLease);
16241
16291
  return cachedLease;
16242
16292
  }
16243
16293
  function hasTransformForHost(lease, host) {
@@ -16278,7 +16328,7 @@ async function proxySandboxEgressRequest(request, deps = {}) {
16278
16328
  {},
16279
16329
  {
16280
16330
  "http.request.method": request.method,
16281
- "url.path": new URL(request.url).pathname
16331
+ "url.path": redactedProxyPath(new URL(request.url).pathname)
16282
16332
  },
16283
16333
  "Sandbox egress OIDC payload did not include a VM session id"
16284
16334
  );
@@ -16296,7 +16346,7 @@ async function proxySandboxEgressRequest(request, deps = {}) {
16296
16346
  ...egressAttributes({
16297
16347
  egressId: activeEgressId,
16298
16348
  method: request.method,
16299
- path: new URL(request.url).pathname,
16349
+ path: redactedProxyPath(new URL(request.url).pathname),
16300
16350
  status: 400
16301
16351
  }),
16302
16352
  ...routingAttributes(request)
@@ -16325,10 +16375,12 @@ async function proxySandboxEgressRequest(request, deps = {}) {
16325
16375
  );
16326
16376
  return jsonError("No provider owns forwarded host", 403);
16327
16377
  }
16328
- const session = await getSandboxEgressSession(activeEgressId);
16329
- if (!session) {
16378
+ const requesterContext = parseSandboxEgressRequesterToken(
16379
+ requesterTokenFromRequest(request)
16380
+ );
16381
+ if (!requesterContext || requesterContext.egressId !== activeEgressId) {
16330
16382
  logWarn(
16331
- "sandbox_egress_session_unauthorized",
16383
+ "sandbox_egress_requester_context_unauthorized",
16332
16384
  {},
16333
16385
  {
16334
16386
  ...egressAttributes({
@@ -16341,13 +16393,13 @@ async function proxySandboxEgressRequest(request, deps = {}) {
16341
16393
  }),
16342
16394
  ...routingAttributes(request, upstreamUrl)
16343
16395
  },
16344
- "Sandbox egress VM session is not authorized for requester credentials"
16396
+ "Sandbox egress request did not include a valid requester context for the VM session"
16345
16397
  );
16346
- return jsonError("Sandbox egress session is not authorized", 403);
16398
+ return jsonError("Sandbox egress requester context is not authorized", 403);
16347
16399
  }
16348
16400
  let lease;
16349
16401
  try {
16350
- lease = await credentialLease(activeEgressId, provider, session);
16402
+ lease = await credentialLease(provider, requesterContext);
16351
16403
  } catch (error) {
16352
16404
  if (error instanceof CredentialUnavailableError) {
16353
16405
  logWarn(
@@ -16451,7 +16503,7 @@ ${error.message}`,
16451
16503
  },
16452
16504
  "Sandbox egress upstream auth rejected"
16453
16505
  );
16454
- await clearSandboxEgressCredentialLease(activeEgressId, provider, session);
16506
+ await clearSandboxEgressCredentialLease(provider, requesterContext);
16455
16507
  }
16456
16508
  return new Response(upstream.body, {
16457
16509
  status: upstream.status,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"