Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/core/src/__tests__/internal/fetchSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ describe('internal #getSettings', () => {
...clientArgs.config,
useSegmentEndpoints: true,
cdnProxy: cdnProxy,
allowInsecureProxy: true,
};
const anotherClient = new SegmentClient({
...clientArgs,
Expand All @@ -319,6 +320,7 @@ describe('internal #getSettings', () => {
...clientArgs.config,
useSegmentEndpoints: true,
cdnProxy: cdnProxy,
allowInsecureProxy: true,
};
const anotherClient = new SegmentClient({
...clientArgs,
Expand Down Expand Up @@ -346,6 +348,7 @@ describe('internal #getSettings', () => {
...clientArgs.config,
useSegmentEndpoints: true,
cdnProxy: cdnProxy,
allowInsecureProxy: true,
};
const anotherClient = new SegmentClient({
...clientArgs,
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/__tests__/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,16 @@ describe('getURL function', () => {
'Invalid URL has been passed'
);
});

it('should throw when an explicit http:// host is passed without opt-in', () => {
expect(() => getURL('http://proxy.example.com', '/path')).toThrow(
'Insecure HTTP proxy URL rejected'
);
});

it('should allow http:// host when allowInsecure is true', () => {
expect(getURL('http://proxy.example.com', '/path', true)).toBe(
'http://proxy.example.com/path'
);
});
});
6 changes: 5 additions & 1 deletion packages/core/src/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,11 @@ export class SegmentClient {
settingsEndpoint = `/${this.config.writeKey}/settings`;
}
try {
return getURL(settingsPrefix, settingsEndpoint);
return getURL(
settingsPrefix,
settingsEndpoint,
this.config.allowInsecureProxy
);
} catch (error) {
console.error(
'Error in getEndpointForSettings:',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/plugins/SegmentDestination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ export class SegmentDestination extends DestinationPlugin {
baseURL = this.apiHost ?? defaultApiHost;
}
try {
return getURL(baseURL, endpoint);
return getURL(baseURL, endpoint, config?.allowInsecureProxy);
} catch (error) {
console.error('Error in getEndpoint:', `fallback to ${defaultApiHost}`);
return defaultApiHost;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ describe('SegmentDestination', () => {
...clientArgs.config,
useSegmentEndpoints: true,
proxy: proxy,
allowInsecureProxy: true,
};
plugin.analytics = new SegmentClient({
...clientArgs,
Expand All @@ -578,6 +579,7 @@ describe('SegmentDestination', () => {
...clientArgs.config,
useSegmentEndpoints: true,
proxy: proxy,
allowInsecureProxy: true,
};
plugin.analytics = new SegmentClient({
...clientArgs,
Expand All @@ -601,6 +603,7 @@ describe('SegmentDestination', () => {
...clientArgs.config,
useSegmentEndpoints: true,
proxy: proxy,
allowInsecureProxy: true,
};
plugin.analytics = new SegmentClient({
...clientArgs,
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ export type Config = {
storePersistorSaveDelay?: number;
proxy?: string;
cdnProxy?: string;
/**
* Allow http:// proxy/cdnProxy URLs. By default the SDK rejects cleartext
* HTTP because the write key and all event PII travel in the request body.
* Only set this if your proxy terminates TLS internally and you understand
* the risk of on-path exposure.
*/
allowInsecureProxy?: boolean;
useSegmentEndpoints?: boolean; // Use if you want to use Segment endpoints
errorHandler?: (error: SegmentError) => void;
/** Client-side httpConfig overrides (highest precedence over defaults and CDN). */
Expand Down
12 changes: 11 additions & 1 deletion packages/core/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,20 @@ export const createPromise = <T>(
};
};

export function getURL(host: string, path: string) {
export function getURL(host: string, path: string, allowInsecure = false) {
if (!host.startsWith('https://') && !host.startsWith('http://')) {
host = 'https://' + host;
}
if (host.startsWith('http://')) {
if (!allowInsecure) {
throw new Error(
'Insecure HTTP proxy URL rejected. The write key and event PII travel in the request body. Use an https:// URL, or set allowInsecureProxy: true to suppress this error.'
);
}
console.warn(
'[Segment] Warning: proxy/cdnProxy is using http:// — the write key and all event PII will be sent in cleartext.'
);
}
const s = `${host}${path}`;
if (!validateURL(s)) {
console.error('Invalid URL has been passed');
Expand Down
Loading