Skip to content
Merged
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ module.exports = [
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
gzip: false,
brotli: false,
limit: '490 KiB',
limit: '492 KiB',
disablePlugins: ['@size-limit/webpack'],
webpack: false,
modifyEsbuildConfig: function (config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,21 @@ describe('express tracing with error', () => {
runner.makeRequest('get', '/test/123/abc?q=1');
await runner.completed();
});

test('preserves encoded query parameters while filtering sensitive values on events', async () => {
const runner = createRunner()
.ignore('transaction')
.expect({
event: {
request: {
query_string: 'q=hello%20world&token=[Filtered]',
},
},
})
.start();

await runner.makeRequest('get', '/test/123/abc?q=hello%20world&token=secret');
await runner.completed();
});
});
});
10 changes: 8 additions & 2 deletions packages/angular/src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ import {
getAbsoluteUrl,
} from '@sentry/browser';
import type { Integration, Span } from '@sentry/core';
import { debug, parseStringToURLObject, stripUrlQueryAndFragment, timestampInSeconds } from '@sentry/core';
import {
debug,
parseStringToURLObject,
stripUrlQueryAndFragment,
timestampInSeconds,
filterCollectedUrl,
} from '@sentry/core';
import type { Observable } from 'rxjs';
import { Subscription } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
Expand Down Expand Up @@ -71,7 +77,7 @@ export function _updateSpanAttributesForParametrizedUrl(route: string, url: stri
span.setAttributes({
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.${op}.angular`,
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
[URL_FULL]: absoluteUrl,
[URL_FULL]: filterCollectedUrl(absoluteUrl),
[URL_PATH]: parseStringToURLObject(absoluteUrl)?.pathname,
[URL_TEMPLATE]: route,
});
Expand Down
6 changes: 4 additions & 2 deletions packages/astro/src/server/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
spanToJSON,
stripUrlQueryAndFragment,
winterCGRequestToRequestData,
filterCollectedUrl,
filterCollectedUrlQuery,
} from '@sentry/core';
import {
captureException,
Expand Down Expand Up @@ -219,7 +221,7 @@ async function instrumentRequestStartHttpServerSpan(
[SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD]: method,
// This is here for backwards compatibility, we used to set this here before
method,
[URL_FULL]: ctx.url.href,
[URL_FULL]: filterCollectedUrl(ctx.url.href),
[URL_PATH]: ctx.url.pathname,
url: stripUrlQueryAndFragment(ctx.url.href),
...httpHeadersToSpanAttributes(
Expand All @@ -233,7 +235,7 @@ async function instrumentRequestStartHttpServerSpan(
}

if (ctx.url.search) {
attributes['http.query'] = ctx.url.search;
attributes['http.query'] = filterCollectedUrlQuery(ctx.url.search);
}

if (ctx.url.hash) {
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-serverless/src/requestSpanOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
import { CLOUD_ACCOUNT_ID, FAAS_COLDSTART, URL_FULL } from '@sentry/conventions/attributes';
import type { SpanAttributes, StartSpanOptions } from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_KIND } from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_KIND, filterCollectedUrl } from '@sentry/core';
import type { Context } from 'aws-lambda';
import { ATTR_FAAS_EXECUTION, ATTR_FAAS_ID } from './semconv';

Expand Down Expand Up @@ -62,7 +62,7 @@ function extractOtherEventFields(event: unknown): SpanAttributes {
const answer: SpanAttributes = {};
const fullUrl = extractFullUrl(event as ApiGatewayLikeEvent);
if (fullUrl) {
answer[URL_FULL] = fullUrl;
answer[URL_FULL] = filterCollectedUrl(fullUrl);
}
return answer;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/browser-utils/src/metrics/browserMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
setMeasurement,
spanToJSON,
stringMatchesSomePattern,
filterCollectedUrl,
} from '@sentry/core';
import { htmlTreeAsString } from '../htmlTreeAsString';
import { WINDOW } from '../types';
Expand Down Expand Up @@ -775,7 +776,7 @@ export function _addResourceSpans(

attributes['url.same_origin'] = resourceUrl.includes(WINDOW.location.origin);

attributes[URL_FULL] = resourceUrl;
attributes[URL_FULL] = filterCollectedUrl(resourceUrl);

_setResourceRequestAttributes(entry, attributes, [
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
startInactiveSpan,
stripDataUrlContent,
filterCollectedUrl,
} from '@sentry/core';

const responseToStreamSpan = new WeakMap<object, Span>();
Expand Down Expand Up @@ -80,7 +81,7 @@ export const fetchStreamPerformanceIntegration = defineIntegration(() => {
name: `${method} ${sanitizedUrl}`,
startTime: handlerData.endTimestamp,
attributes: {
url: stripDataUrlContent(url),
url: filterCollectedUrl(stripDataUrlContent(url)),
'http.method': method,
type: 'fetch',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client.stream',
Expand Down
4 changes: 2 additions & 2 deletions packages/browser/src/integrations/httpcontext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core/browser';
import { defineIntegration, filterCollectedUrl, safeSetSpanJSONAttributes } from '@sentry/core/browser';
import { getHttpRequestData, WINDOW } from '../helpers';
import { HTTP_REQUEST_HEADER_KEY_BASE, SENTRY_OP, URL_FULL, USER_AGENT_ORIGINAL } from '@sentry/conventions/attributes';

Expand Down Expand Up @@ -45,7 +45,7 @@ export const httpContextIntegration = defineIntegration(() => {
...(span.is_segment && {
// Coerce empty string to undefined so the helper's nullish check drops it,
// rather than writing an empty `url.full` attribute onto the span.
[URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? reqData.url : undefined,
[URL_FULL]: span.attributes?.[SENTRY_OP] !== 'http.client' ? filterCollectedUrl(reqData.url) : undefined,
[`${HTTP_REQUEST_HEADER_KEY_BASE}.referer`]: reqData.headers['Referer'],
}),
});
Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/tracing/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
startTrackingLongTasks,
} from '@sentry/browser-utils';
import { DEBUG_BUILD } from '../debug-build';
import { filterCollectedUrl } from '@sentry/core';
import { getHttpRequestData, WINDOW } from '../helpers';
import { fetchStreamPerformanceIntegration } from '../integrations/fetchStreamPerformance';
import { WEB_VITALS_INTEGRATION_NAME, webVitalsIntegration } from '../integrations/webVitals';
Expand Down Expand Up @@ -431,7 +432,7 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption

const attributes = {
...(urlObject?.pathname && { [URL_PATH]: urlObject.pathname }),
...(urlObject && !isURLObjectRelative(urlObject) && { [URL_FULL]: urlObject.href }),
...(urlObject && !isURLObjectRelative(urlObject) && { [URL_FULL]: filterCollectedUrl(urlObject.href) }),
...finalStartSpanOptions.attributes,
};

Expand Down
14 changes: 8 additions & 6 deletions packages/browser/src/tracing/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {
} from '@sentry/core/browser';
import {
addFetchInstrumentationHandler,
filterCollectedUrl,
filterCollectedUrlQuery,
getActiveSpan,
getClient,
getLocationHref,
Expand Down Expand Up @@ -174,10 +176,10 @@ export function instrumentOutgoingRequests(client: Client, _options?: Partial<Re
const sanitizedFullUrl = fullUrl ? stripDataUrlContent(fullUrl) : undefined;
createdSpan.setAttributes({
// oxlint-disable-next-line typescript/no-deprecated
[HTTP_URL]: sanitizedFullUrl,
[HTTP_URL]: filterCollectedUrl(sanitizedFullUrl),
// `url.full` must match `http.url`. Setting it here ensures parentless `http.client`
// segment spans don't get `url.full` backfilled with the host page URL (see httpContextIntegration).
[URL_FULL]: sanitizedFullUrl,
[URL_FULL]: filterCollectedUrl(sanitizedFullUrl),
'server.address': host,
});

Expand Down Expand Up @@ -389,17 +391,17 @@ function xhrCallback(
? startInactiveSpan({
name: `${method} ${urlForSpanName}`,
attributes: {
url: stripDataUrlContent(url),
url: filterCollectedUrl(stripDataUrlContent(url)),
type: 'xhr',
'http.method': method,
'http.url': sanitizedFullUrl,
'http.url': filterCollectedUrl(sanitizedFullUrl),
// `url.full` must match `http.url`. Setting it here ensures parentless `http.client`
// segment spans don't get `url.full` backfilled with the host page URL (see httpContextIntegration).
[URL_FULL]: sanitizedFullUrl,
[URL_FULL]: filterCollectedUrl(sanitizedFullUrl),
'server.address': parsedUrl?.host,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.browser',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client',
...(parsedUrl?.search && { 'http.query': parsedUrl?.search }),
...(parsedUrl?.search && { 'http.query': filterCollectedUrlQuery(parsedUrl?.search) }),
...(parsedUrl?.hash && { 'http.fragment': parsedUrl?.hash }),
},
})
Expand Down
6 changes: 4 additions & 2 deletions packages/bun/src/integrations/bunserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
setHttpStatus,
startSpan,
withIsolationScope,
filterCollectedUrl,
filterCollectedUrlQuery,
} from '@sentry/core';
import type { ServeOptions } from 'bun';
import { URL_FULL } from '@sentry/conventions/attributes';
Expand Down Expand Up @@ -274,7 +276,7 @@ function getSpanAttributesFromParsedUrl(

if (parsedUrl) {
if (parsedUrl.search) {
attributes['url.query'] = parsedUrl.search;
attributes['url.query'] = filterCollectedUrlQuery(parsedUrl.search);
}
if (parsedUrl.hash) {
attributes['url.fragment'] = parsedUrl.hash;
Expand All @@ -283,7 +285,7 @@ function getSpanAttributesFromParsedUrl(
attributes['url.path'] = parsedUrl.pathname;
}
if (!isURLObjectRelative(parsedUrl)) {
attributes[URL_FULL] = parsedUrl.href;
attributes[URL_FULL] = filterCollectedUrl(parsedUrl.href);
if (parsedUrl.port) {
attributes['url.port'] = parsedUrl.port;
}
Expand Down
9 changes: 8 additions & 1 deletion packages/cloudflare/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,14 @@ export function wrapRequestHandler(
isolationScope.setClient(client);

const urlObject = parseStringToURLObject(request.url);
const [name, attributes] = getHttpSpanDetailsFromUrlObject(urlObject, 'server', 'auto.http.cloudflare', request);
const [name, attributes] = getHttpSpanDetailsFromUrlObject(
urlObject,
'server',
'auto.http.cloudflare',
request,
undefined,
client,
);

const contentLength = request.headers.get('content-length');
if (contentLength) {
Expand Down
18 changes: 11 additions & 7 deletions packages/core/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HTTP_URL, URL_FULL } from '@sentry/conventions/attributes';
import type { Client } from './client';
import { getClient } from './currentScopes';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes';
import { setHttpStatus, SPAN_STATUS_ERROR, spanIsIgnored, startInactiveSpan } from './tracing';
Expand All @@ -9,6 +10,7 @@ import type { HandlerDataFetch } from './types/instrument';
import type { ResponseHookInfo } from './types/request';
import type { Span, SpanAttributes, SpanOrigin } from './types/span';
import { SENTRY_BAGGAGE_KEY_PREFIX } from './utils/baggage';
import { filterCollectedUrl, filterCollectedUrlQuery } from './utils/data-collection/filterCollectedUrl';
import { hasSpansEnabled } from './utils/hasSpansEnabled';
import { isInstanceOf, isRequest } from './utils/is';
import { getActiveSpan } from './utils/spanUtils';
Expand Down Expand Up @@ -117,7 +119,7 @@ export function instrumentFetchRequest(

const span =
shouldCreateSpanResult && shouldEmitSpan
? startInactiveSpan(getSpanStartOptions(url, method, spanOrigin))
? startInactiveSpan(getSpanStartOptions(url, method, spanOrigin, client))
: new SentryNonRecordingSpan();
const spanForTraceHeaders = spanIsIgnored(span) && hasParent ? undefined : span;

Expand Down Expand Up @@ -357,6 +359,7 @@ function getSpanStartOptions(
url: string,
method: string,
spanOrigin: SpanOrigin,
client: Client | undefined,
): Parameters<typeof startInactiveSpan>[0] {
// Data URLs need special handling because parseStringToURLObject treats them as "relative"
// (no "://"), causing getSanitizedUrlStringFromUrlObject to return just the pathname
Expand All @@ -366,15 +369,15 @@ function getSpanStartOptions(
const sanitizedUrl = stripDataUrlContent(url);
return {
name: `${method} ${sanitizedUrl}`,
attributes: getFetchSpanAttributes(url, undefined, method, spanOrigin),
attributes: getFetchSpanAttributes(url, undefined, method, spanOrigin, client),
};
}

const parsedUrl = parseStringToURLObject(url);
const sanitizedUrl = parsedUrl ? getSanitizedUrlStringFromUrlObject(parsedUrl) : url;
return {
name: `${method} ${sanitizedUrl}`,
attributes: getFetchSpanAttributes(url, parsedUrl, method, spanOrigin),
attributes: getFetchSpanAttributes(url, parsedUrl, method, spanOrigin, client),
};
}

Expand All @@ -383,9 +386,10 @@ function getFetchSpanAttributes(
parsedUrl: ReturnType<typeof parseStringToURLObject>,
method: string,
spanOrigin: SpanOrigin,
client: Client | undefined,
): SpanAttributes {
const attributes: SpanAttributes = {
url: stripDataUrlContent(url),
url: filterCollectedUrl(stripDataUrlContent(url), client),
type: 'fetch',
'http.method': method,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin,
Expand All @@ -394,12 +398,12 @@ function getFetchSpanAttributes(
if (parsedUrl) {
if (!isURLObjectRelative(parsedUrl)) {
// oxlint-disable-next-line typescript/no-deprecated
attributes[HTTP_URL] = stripDataUrlContent(parsedUrl.href);
attributes[URL_FULL] = stripDataUrlContent(parsedUrl.href);
attributes[HTTP_URL] = filterCollectedUrl(stripDataUrlContent(parsedUrl.href), client);
attributes[URL_FULL] = filterCollectedUrl(stripDataUrlContent(parsedUrl.href), client);
attributes['server.address'] = parsedUrl.host;
}
if (parsedUrl.search) {
attributes['http.query'] = parsedUrl.search;
attributes['http.query'] = filterCollectedUrlQuery(parsedUrl.search, client);
}
if (parsedUrl.hash) {
attributes['http.fragment'] = parsedUrl.hash;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { addBreadcrumb } from '../../breadcrumbs';
import { getBreadcrumbLogLevelFromHttpStatusCode } from '../../utils/breadcrumb-log-level';
import { filterCollectedUrlQuery } from '../../utils/data-collection/filterCollectedUrl';
import { getSanitizedUrlString, parseUrl } from '../../utils/url';
import { getRequestUrlFromClientRequest } from './get-request-url';
import type { HttpClientRequest, HttpIncomingMessage } from './types';
Expand All @@ -24,7 +25,7 @@ export function addOutgoingRequestBreadcrumb(
status_code: statusCode,
url: getSanitizedUrlString(parsedUrl),
'http.method': request.method || 'GET',
...(parsedUrl.search ? { 'http.query': parsedUrl.search } : {}),
...(parsedUrl.search ? { 'http.query': filterCollectedUrlQuery(parsedUrl.search) } : {}),
...(parsedUrl.hash ? { 'http.fragment': parsedUrl.hash } : {}),
},
type: 'http',
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/integrations/http/get-outgoing-span-data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Span, SpanAttributes } from '../../types/span';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP } from '../../semanticAttributes';
import { filterCollectedUrl } from '../../utils/data-collection/filterCollectedUrl';
import { getHttpSpanDetailsFromUrlObject, parseStringToURLObject } from '../../utils/url';
import type { HttpClientRequest, HttpIncomingMessage } from './types';
import { getRequestUrlFromClientRequest } from './get-request-url';
Expand Down Expand Up @@ -27,9 +28,9 @@ export function getOutgoingRequestSpanData(request: HttpClientRequest): StartSpa
// https://getsentry.github.io/sentry-conventions/attributes/
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client',
'otel.kind': 'CLIENT',
'http.url': url,
'http.url': filterCollectedUrl(url),
'http.method': request.method,
'http.target': request.path || '/',
'http.target': filterCollectedUrl(request.path || '/'),
'net.peer.name': request.host,
'http.host': request.getHeader('host') as string | undefined,
...(userAgent ? { 'user_agent.original': userAgent as string } : {}),
Expand Down
10 changes: 7 additions & 3 deletions packages/core/src/integrations/http/server-subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { SPAN_KIND } from '../../spanKind';
import type { SpanAttributes } from '../../types/span';
import type { SpanStatus } from '../../types/spanStatus';
import { HTTP_URL, URL_FULL, URL_PATH } from '@sentry/conventions/attributes';
import { filterCollectedUrl } from '../../utils/data-collection/filterCollectedUrl';

// Tree-shakable guard to remove all code related to tracing
declare const __SENTRY_TRACING__: boolean;
Expand Down Expand Up @@ -300,12 +301,15 @@ function buildServerSpanWrap(
'net.peer.port': remotePort,
'sentry.http.prefetch': isKnownPrefetchRequest(request) || undefined,
// Old Semantic Conventions attributes for compatibility
[URL_FULL]: urlObj && !isURLObjectRelative(urlObj) ? urlObj.href : undefined,
[URL_FULL]: urlObj && !isURLObjectRelative(urlObj) ? filterCollectedUrl(urlObj.href, client) : undefined,
[URL_PATH]: urlObj?.pathname ?? httpTargetWithoutQueryFragment,
// oxlint-disable-next-line typescript-eslint(no-deprecated)
[HTTP_URL]: fullUrl,
[HTTP_URL]: filterCollectedUrl(fullUrl, client),
'http.method': method,
'http.target': urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,
'http.target': filterCollectedUrl(
urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,
client,
),
'http.host': host,
'net.host.name': hostname,
'http.client_ip': typeof ips === 'string' ? ips.split(',')[0] : undefined,
Expand Down
Loading
Loading