Skip to content

Commit 11ea830

Browse files
authored
fix(aws-serverless): Take http_proxy into account when choosing useLayerExtension default (#17817)
The default setting for `useLayerExtension` now considers the `http_proxy` environment variable. When `http_proxy` is set, `useLayerExtension` will be off by default. If you use a `http_proxy` but would still like to make use of the Sentry Lambda extension, exempt `localhost` in a `no_proxy` environment variable. Fixes: #17804 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Disable `useLayerExtension` by default when `http_proxy` is set (unless `no_proxy` exempts localhost), add debug warnings, tests, and changelog entry. > > - **aws-serverless**: > - Consider proxy env vars when defaulting `useLayerExtension` in `packages/aws-serverless/src/init.ts`. > - New `shouldDisableLayerExtensionForProxy()` checks `http_proxy` and `no_proxy` (localhost exemptions). > - Update default: enable only if using Lambda layer, no custom tunnel, and no proxy interference. > - Add debug warnings when disabling due to proxy and when tunneling via extension. > - **Tests**: > - Expand `packages/aws-serverless/test/init.test.ts` to cover proxy/no_proxy scenarios, explicit overrides, and env cleanup. > - **Docs/Changelog**: > - Add Important Changes note explaining new default behavior and how to re-enable with `no_proxy` exemptions. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 53e333f. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent e966cdc commit 11ea830

File tree

3 files changed

+312
-2
lines changed

3 files changed

+312
-2
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
### Important Changes
8+
9+
- **fix(aws-serverless): Take `http_proxy` into account when choosing
10+
`useLayerExtension` default ([#17817](https://github.com/getsentry/sentry-javascript/pull/17817))**
11+
12+
The default setting for `useLayerExtension` now considers the `http_proxy` environment variable. When `http_proxy` is set, `useLayerExtension` will be off by default. If you use a `http_proxy` but would still like to make use of the Sentry Lambda extension, exempt `localhost` in a `no_proxy` environment variable.
13+
714
## 10.16.0
815

916
- feat(logs): Add internal `replay_is_buffering` flag ([#17752](https://github.com/getsentry/sentry-javascript/pull/17752))

packages/aws-serverless/src/init.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,44 @@ import { getDefaultIntegrationsWithoutPerformance, initWithoutDefaultIntegration
55
import { DEBUG_BUILD } from './debug-build';
66
import { awsIntegration } from './integration/aws';
77
import { awsLambdaIntegration } from './integration/awslambda';
8+
9+
/**
10+
* Checks if proxy environment variables would interfere with the layer extension.
11+
* The layer extension uses localhost:9000, so we need to check if proxy settings would prevent this.
12+
*/
13+
function shouldDisableLayerExtensionForProxy(): boolean {
14+
const { http_proxy, no_proxy } = process.env;
15+
16+
// If no http proxy is configured, no interference (https_proxy doesn't affect HTTP requests)
17+
if (!http_proxy) {
18+
return false;
19+
}
20+
21+
// Check if localhost is exempted by no_proxy
22+
if (no_proxy) {
23+
const exemptions = no_proxy.split(',').map(exemption => exemption.trim().toLowerCase());
24+
25+
// Handle common localhost exemption patterns explicitly
26+
// If localhost is exempted, requests to the layer extension will not be proxied
27+
const localhostExemptions = ['*', 'localhost', '127.0.0.1', '::1'];
28+
if (exemptions.some(exemption => localhostExemptions.includes(exemption))) {
29+
return false;
30+
}
31+
}
32+
33+
// If http_proxy is set and no localhost exemption, it would interfere
34+
// The layer extension uses HTTP to localhost:9000, so only http_proxy matters
35+
if (http_proxy) {
36+
DEBUG_BUILD &&
37+
debug.log(
38+
'Disabling useLayerExtension due to http_proxy environment variable. Consider adding localhost to no_proxy to re-enable.',
39+
);
40+
return true;
41+
}
42+
43+
return false;
44+
}
45+
846
/**
947
* Get the default integrations for the AWSLambda SDK.
1048
*/
@@ -28,9 +66,11 @@ export interface AwsServerlessOptions extends NodeOptions {
2866
*/
2967
export function init(options: AwsServerlessOptions = {}): NodeClient | undefined {
3068
const sdkSource = getSDKSource();
69+
const proxyWouldInterfere = shouldDisableLayerExtensionForProxy();
70+
3171
const opts = {
3272
defaultIntegrations: getDefaultIntegrations(options),
33-
useLayerExtension: sdkSource === 'aws-lambda-layer' && !options.tunnel,
73+
useLayerExtension: sdkSource === 'aws-lambda-layer' && !options.tunnel && !proxyWouldInterfere,
3474
...options,
3575
};
3676

@@ -48,6 +88,11 @@ export function init(options: AwsServerlessOptions = {}): NodeClient | undefined
4888
} else {
4989
DEBUG_BUILD && debug.warn('The Sentry Lambda extension is only supported when using the AWS Lambda layer.');
5090
}
91+
} else if (sdkSource === 'aws-lambda-layer' && proxyWouldInterfere) {
92+
DEBUG_BUILD &&
93+
debug.warn(
94+
'Sentry Lambda extension disabled due to proxy environment variables (http_proxy/https_proxy). Consider adding localhost to no_proxy to re-enable.',
95+
);
5196
}
5297

5398
applySdkMetadata(opts, 'aws-serverless', ['aws-serverless'], sdkSource);

packages/aws-serverless/test/init.test.ts

Lines changed: 259 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getSDKSource } from '@sentry/core';
22
import { initWithoutDefaultIntegrations } from '@sentry/node';
3-
import { describe, expect, test, vi } from 'vitest';
3+
import { beforeEach, describe, expect, test, vi } from 'vitest';
44
import type { AwsServerlessOptions } from '../src/init';
55
import { init } from '../src/init';
66

@@ -18,6 +18,12 @@ const mockGetSDKSource = vi.mocked(getSDKSource);
1818
const mockInitWithoutDefaultIntegrations = vi.mocked(initWithoutDefaultIntegrations);
1919

2020
describe('init', () => {
21+
beforeEach(() => {
22+
// Clean up environment variables between tests
23+
delete process.env.http_proxy;
24+
delete process.env.no_proxy;
25+
});
26+
2127
describe('Lambda extension setup', () => {
2228
test('should preserve user-provided tunnel option when Lambda extension is enabled', () => {
2329
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
@@ -128,4 +134,256 @@ describe('init', () => {
128134
);
129135
});
130136
});
137+
138+
describe('proxy environment variables and layer extension', () => {
139+
test('should enable useLayerExtension when no proxy env vars are set', () => {
140+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
141+
const options: AwsServerlessOptions = {};
142+
143+
init(options);
144+
145+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
146+
expect.objectContaining({
147+
useLayerExtension: true,
148+
tunnel: 'http://localhost:9000/envelope',
149+
}),
150+
);
151+
});
152+
153+
test('should disable useLayerExtension when http_proxy is set', () => {
154+
process.env.http_proxy = 'http://proxy.example.com:8080';
155+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
156+
const options: AwsServerlessOptions = {};
157+
158+
init(options);
159+
160+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
161+
expect.objectContaining({
162+
useLayerExtension: false,
163+
}),
164+
);
165+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
166+
expect.not.objectContaining({
167+
tunnel: expect.any(String),
168+
}),
169+
);
170+
});
171+
172+
describe('no_proxy patterns', () => {
173+
test('should enable useLayerExtension when no_proxy=* (wildcard)', () => {
174+
process.env.http_proxy = 'http://proxy.example.com:8080';
175+
process.env.no_proxy = '*';
176+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
177+
const options: AwsServerlessOptions = {};
178+
179+
init(options);
180+
181+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
182+
expect.objectContaining({
183+
useLayerExtension: true,
184+
tunnel: 'http://localhost:9000/envelope',
185+
}),
186+
);
187+
});
188+
189+
test('should enable useLayerExtension when no_proxy contains localhost', () => {
190+
process.env.http_proxy = 'http://proxy.example.com:8080';
191+
process.env.no_proxy = 'localhost';
192+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
193+
const options: AwsServerlessOptions = {};
194+
195+
init(options);
196+
197+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
198+
expect.objectContaining({
199+
useLayerExtension: true,
200+
tunnel: 'http://localhost:9000/envelope',
201+
}),
202+
);
203+
});
204+
205+
test('should enable useLayerExtension when no_proxy contains 127.0.0.1', () => {
206+
process.env.http_proxy = 'http://proxy.example.com:8080';
207+
process.env.no_proxy = '127.0.0.1';
208+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
209+
const options: AwsServerlessOptions = {};
210+
211+
init(options);
212+
213+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
214+
expect.objectContaining({
215+
useLayerExtension: true,
216+
tunnel: 'http://localhost:9000/envelope',
217+
}),
218+
);
219+
});
220+
221+
test('should enable useLayerExtension when no_proxy contains ::1', () => {
222+
process.env.http_proxy = 'http://proxy.example.com:8080';
223+
process.env.no_proxy = '::1';
224+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
225+
const options: AwsServerlessOptions = {};
226+
227+
init(options);
228+
229+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
230+
expect.objectContaining({
231+
useLayerExtension: true,
232+
tunnel: 'http://localhost:9000/envelope',
233+
}),
234+
);
235+
});
236+
237+
test('should enable useLayerExtension when no_proxy contains localhost in a comma-separated list', () => {
238+
process.env.http_proxy = 'http://proxy.example.com:8080';
239+
process.env.no_proxy = 'example.com,localhost,other.com';
240+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
241+
const options: AwsServerlessOptions = {};
242+
243+
init(options);
244+
245+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
246+
expect.objectContaining({
247+
useLayerExtension: true,
248+
tunnel: 'http://localhost:9000/envelope',
249+
}),
250+
);
251+
});
252+
253+
test('should disable useLayerExtension when no_proxy does not contain localhost patterns', () => {
254+
process.env.http_proxy = 'http://proxy.example.com:8080';
255+
process.env.no_proxy = 'example.com,other.com';
256+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
257+
const options: AwsServerlessOptions = {};
258+
259+
init(options);
260+
261+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
262+
expect.objectContaining({
263+
useLayerExtension: false,
264+
}),
265+
);
266+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
267+
expect.not.objectContaining({
268+
tunnel: expect.any(String),
269+
}),
270+
);
271+
});
272+
273+
test('should disable useLayerExtension when no_proxy contains host (no longer supported)', () => {
274+
process.env.http_proxy = 'http://proxy.example.com:8080';
275+
process.env.no_proxy = 'host';
276+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
277+
const options: AwsServerlessOptions = {};
278+
279+
init(options);
280+
281+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
282+
expect.objectContaining({
283+
useLayerExtension: false,
284+
}),
285+
);
286+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
287+
expect.not.objectContaining({
288+
tunnel: expect.any(String),
289+
}),
290+
);
291+
});
292+
293+
test('should handle case-insensitive no_proxy values', () => {
294+
process.env.http_proxy = 'http://proxy.example.com:8080';
295+
process.env.no_proxy = 'LOCALHOST';
296+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
297+
const options: AwsServerlessOptions = {};
298+
299+
init(options);
300+
301+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
302+
expect.objectContaining({
303+
useLayerExtension: true,
304+
tunnel: 'http://localhost:9000/envelope',
305+
}),
306+
);
307+
});
308+
309+
test('should handle whitespace in no_proxy values', () => {
310+
process.env.http_proxy = 'http://proxy.example.com:8080';
311+
process.env.no_proxy = ' localhost , example.com ';
312+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
313+
const options: AwsServerlessOptions = {};
314+
315+
init(options);
316+
317+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
318+
expect.objectContaining({
319+
useLayerExtension: true,
320+
tunnel: 'http://localhost:9000/envelope',
321+
}),
322+
);
323+
});
324+
});
325+
326+
test('should respect explicit useLayerExtension=false even with no proxy interference', () => {
327+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
328+
const options: AwsServerlessOptions = {
329+
useLayerExtension: false,
330+
};
331+
332+
init(options);
333+
334+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
335+
expect.objectContaining({
336+
useLayerExtension: false,
337+
}),
338+
);
339+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
340+
expect.not.objectContaining({
341+
tunnel: expect.any(String),
342+
}),
343+
);
344+
});
345+
346+
test('should respect explicit useLayerExtension=false even with proxy that would interfere', () => {
347+
process.env.http_proxy = 'http://proxy.example.com:8080';
348+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
349+
const options: AwsServerlessOptions = {
350+
useLayerExtension: false,
351+
};
352+
353+
init(options);
354+
355+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
356+
expect.objectContaining({
357+
useLayerExtension: false,
358+
}),
359+
);
360+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
361+
expect.not.objectContaining({
362+
tunnel: expect.any(String),
363+
}),
364+
);
365+
});
366+
367+
test('should respect explicit useLayerExtension=false even when no_proxy would enable it', () => {
368+
process.env.http_proxy = 'http://proxy.example.com:8080';
369+
process.env.no_proxy = 'localhost';
370+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
371+
const options: AwsServerlessOptions = {
372+
useLayerExtension: false,
373+
};
374+
375+
init(options);
376+
377+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
378+
expect.objectContaining({
379+
useLayerExtension: false,
380+
}),
381+
);
382+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
383+
expect.not.objectContaining({
384+
tunnel: expect.any(String),
385+
}),
386+
);
387+
});
388+
});
131389
});

0 commit comments

Comments
 (0)