Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/browser';
import { httpClientIntegration } from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [httpClientIntegration()],
tracesSampleRate: 1,
dataCollection: {
cookies: true,
httpHeaders: { request: true, response: true },
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fetch('http://sentry-test.io/foo', {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';

sentryTest(
'should capture request and response headers when using dataCollection options',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const req = await Promise.all([waitForErrorRequest(page), page.goto(url)]).then(([r]) => r);
const eventData = envelopeRequestParser<Event>(req);

expect(eventData.exception?.values).toHaveLength(1);

expect(eventData).toMatchObject({
message: 'HTTP Client Error with status code: 500',
request: {
url: 'http://sentry-test.io/foo',
method: 'GET',
headers: {
accept: 'application/json',
cache: 'no-cache',
'content-type': 'application/json',
},
},
contexts: {
response: {
status_code: 500,
headers: {
'content-type': 'text/html',
},
},
},
});
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/browser';
import { httpClientIntegration } from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [httpClientIntegration()],
tracesSampleRate: 1,
dataCollection: {
cookies: false,
httpHeaders: { request: false, response: false },
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fetch('http://sentry-test.io/foo', {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';

sentryTest(
'should not capture headers or cookies when dataCollection disables them',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const req = await Promise.all([waitForErrorRequest(page), page.goto(url)]).then(([r]) => r);
const eventData = envelopeRequestParser<Event>(req);

expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.message).toBe('HTTP Client Error with status code: 500');

// Request URL and method are always present
expect(eventData.request?.url).toBe('http://sentry-test.io/foo');
expect(eventData.request?.method).toBe('GET');

// Request headers set in subject.js should not be captured
expect(eventData.request?.headers?.accept).toBeUndefined();
expect(eventData.request?.headers?.cache).toBeUndefined();
expect(eventData.request?.headers?.['content-type']).toBeUndefined();
expect(eventData.request?.cookies).toBeUndefined();

// Response headers should not be captured
expect(eventData.contexts?.response?.headers?.['content-type']).toBeUndefined();
expect(eventData.contexts?.response?.cookies).toBeUndefined();
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/browser';
import { httpClientIntegration } from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [httpClientIntegration()],
tracesSampleRate: 1,
dataCollection: {
cookies: false,
httpHeaders: { request: true, response: true },
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fetch('http://sentry-test.io/foo', {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';

sentryTest(
'should capture headers but not cookies when cookies are disabled in dataCollection',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const req = await Promise.all([waitForErrorRequest(page), page.goto(url)]).then(([r]) => r);
const eventData = envelopeRequestParser<Event>(req);

expect(eventData.exception?.values).toHaveLength(1);

// Headers should be present
expect(eventData.request?.headers).toMatchObject({
accept: 'application/json',
cache: 'no-cache',
'content-type': 'application/json',
});

expect(eventData.contexts?.response?.headers).toMatchObject({
'content-type': 'text/html',
});

// Cookies should not be present
expect(eventData.request?.cookies).toBeUndefined();
expect(eventData.contexts?.response?.cookies).toBeUndefined();
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fetch('http://sentry-test.io/foo', {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
Authorization: 'Bearer super-secret-token-123',
'Content-Type': 'application/json',
'X-API-Key': 'my-api-key-456',
'X-Custom-Header': 'safe-value',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';

sentryTest(
'should filter sensitive header and cookie values with sendDefaultPii',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
'X-Auth-Token': 'secret-response-token',
'X-Request-Id': 'abc-123',
},
});
});

const req = await Promise.all([waitForErrorRequest(page), page.goto(url)]).then(([r]) => r);
const eventData = envelopeRequestParser<Event>(req);

expect(eventData.exception?.values).toHaveLength(1);

const reqHeaders = eventData.request?.headers || {};
const resHeaders = (eventData.contexts?.response?.headers as Record<string, string>) || {};

// Non-sensitive request headers should be present with their values
expect(reqHeaders['accept']).toBe('application/json');
expect(reqHeaders['content-type']).toBe('application/json');
expect(reqHeaders['x-custom-header']).toBe('safe-value');

// Sensitive request headers should have their values filtered
// 'authorization' matches the 'auth' snippet
expect(reqHeaders['authorization']).toBe('[Filtered]');
// 'x-api-key' matches the 'key' snippet
expect(reqHeaders['x-api-key']).toBe('[Filtered]');

// Non-sensitive response headers should be present with their values
expect(resHeaders['x-request-id']).toBe('abc-123');

// Sensitive response headers should have their values filtered
// 'x-auth-token' matches 'auth' and 'token' snippets
expect(resHeaders['x-auth-token']).toBe('[Filtered]');
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as Sentry from '@sentry/browser';
import { httpClientIntegration } from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [httpClientIntegration()],
tracesSampleRate: 1,
// sendDefaultPii is not set (defaults to false)
// dataCollection is not set
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fetch('http://sentry-test.io/foo', {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';

sentryTest(
'should not capture request/response headers or cookies without sendDefaultPii',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const req = await Promise.all([waitForErrorRequest(page), page.goto(url)]).then(([r]) => r);
const eventData = envelopeRequestParser<Event>(req);

expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.message).toBe('HTTP Client Error with status code: 500');

// Request URL and method are always present
expect(eventData.request?.url).toBe('http://sentry-test.io/foo');
expect(eventData.request?.method).toBe('GET');

// Without sendDefaultPii, no request headers should be captured by the integration
expect(eventData.request?.headers?.accept).toBeUndefined();
expect(eventData.request?.headers?.cache).toBeUndefined();
expect(eventData.request?.headers?.['content-type']).toBeUndefined();
expect(eventData.request?.cookies).toBeUndefined();

// Response headers should not be captured either
expect(eventData.contexts?.response?.headers?.['content-type']).toBeUndefined();
expect(eventData.contexts?.response?.cookies).toBeUndefined();
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/browser';
import { httpClientIntegration } from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [httpClientIntegration()],
tracesSampleRate: 1,
dataCollection: {
cookies: true,
httpHeaders: { request: true, response: true },
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const xhr = new XMLHttpRequest();

xhr.open('GET', 'http://sentry-test.io/foo', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Cache', 'no-cache');
xhr.send();
Loading
Loading