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
Expand Up @@ -9,6 +9,7 @@ Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
sendDefaultPii: true,
});

// Simulate database operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,37 +57,16 @@ test('Sends client-side Supabase db-operation spans and breadcrumbs to Sentry',

const transactionEvent = await pageloadTransactionPromise;

expect(transactionEvent.spans).toContainEqual(
expect.objectContaining({
description: 'select(*) filter(order, asc) from(todos)',
op: 'db',
data: expect.objectContaining({
'db.operation': 'select',
'db.query': ['select(*)', 'filter(order, asc)'],
'db.system': 'postgresql',
'sentry.op': 'db',
'sentry.origin': 'auto.db.supabase',
}),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
start_timestamp: expect.any(Number),
status: 'ok',
timestamp: expect.any(Number),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
origin: 'auto.db.supabase',
}),
);

expect(transactionEvent.spans).toContainEqual({
// Client uses default sendDefaultPii: false — URL filters and bodies are not attached to spans/breadcrumbs.
const redactedSelectSpan = expect.objectContaining({
description: '[redacted] from(todos)',
op: 'db',
data: expect.objectContaining({
'db.operation': 'select',
'db.query': ['select(*)', 'filter(order, asc)'],
'db.system': 'postgresql',
'sentry.op': 'db',
'sentry.origin': 'auto.db.supabase',
}),
description: 'select(*) filter(order, asc) from(todos)',
op: 'db',
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
start_timestamp: expect.any(Number),
Expand All @@ -97,20 +76,26 @@ test('Sends client-side Supabase db-operation spans and breadcrumbs to Sentry',
origin: 'auto.db.supabase',
});

expect(transactionEvent.spans).toContainEqual(redactedSelectSpan);

const selectSpan = transactionEvent.spans?.find(
(s: { description?: string }) => s.description === '[redacted] from(todos)',
);
expect(selectSpan).toBeDefined();
expect(selectSpan!.data).not.toHaveProperty('db.query');

expect(transactionEvent.breadcrumbs).toContainEqual({
timestamp: expect.any(Number),
type: 'supabase',
category: 'db.select',
message: 'select(*) filter(order, asc) from(todos)',
data: expect.any(Object),
message: '[redacted] from(todos)',
});

expect(transactionEvent.breadcrumbs).toContainEqual({
timestamp: expect.any(Number),
type: 'supabase',
category: 'db.insert',
message: 'insert(...) select(*) from(todos)',
data: expect.any(Object),
message: 'insert(...) [redacted] from(todos)',
});
});

Expand Down
51 changes: 39 additions & 12 deletions packages/core/src/integrations/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable max-lines */
import { addBreadcrumb } from '../breadcrumbs';
import { getClient } from '../currentScopes';
import { DEBUG_BUILD } from '../debug-build';
import { captureException } from '../exports';
import { defineIntegration } from '../integration';
Expand Down Expand Up @@ -148,6 +149,25 @@ function isInstrumented<T>(fn: T): boolean | undefined {
}
}

/**
* Plain-object bodies are copied into `plainBody`; array inserts (and other non-plain shapes) stay only on `rawBody`.
* Returns a payload suitable for span attributes / breadcrumbs when the client has `sendDefaultPii` enabled.
*/
function getMutationBodyPayloadForTelemetry(rawBody: unknown, plainBody: Record<string, unknown>): unknown | undefined {
if (Object.keys(plainBody).length > 0) {
return plainBody;
}
if (Array.isArray(rawBody) && rawBody.length > 0) {
return rawBody;
}
return undefined;
}

/** True when the PostgREST builder carries a mutation body (for `insert(...)`, etc. in span descriptions). */
function hasMutationBodyForDescription(rawBody: unknown, plainBody: Record<string, unknown>): boolean {
return getMutationBodyPayloadForTelemetry(rawBody, plainBody) !== undefined;
}

/**
* Extracts the database operation type from the HTTP method and headers
* @param method - The HTTP method of the request
Expand Down Expand Up @@ -361,12 +381,19 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
}
}

const sendDefaultPii = Boolean(getClient()?.getOptions().sendDefaultPii);
const bodyPayload = getMutationBodyPayloadForTelemetry(typedThis.body, body);

// Adding operation to the beginning of the description if it's not a `select` operation
// For example, it can be an `insert` or `update` operation but the query can be `select(...)`
// For `select` operations, we don't need repeat it in the description
const description = `${operation === 'select' ? '' : `${operation}${body ? '(...) ' : ''}`}${queryItems.join(
' ',
)} from(${table})`;
const mutationPart =
operation === 'select'
? ''
: `${operation}${hasMutationBodyForDescription(typedThis.body, body) ? '(...) ' : ''}`;
const queryPart = sendDefaultPii ? queryItems.join(' ') : queryItems.length > 0 ? '[redacted]' : '';
const descriptionMiddle = [mutationPart.trimEnd(), queryPart].filter(Boolean).join(' ');
const description = descriptionMiddle ? `${descriptionMiddle} from(${table})` : `from(${table})`;

const attributes: Record<string, any> = {
'db.table': table,
Expand All @@ -379,12 +406,12 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db',
};

if (queryItems.length) {
if (queryItems.length && sendDefaultPii) {
attributes['db.query'] = queryItems;
}

if (Object.keys(body).length) {
attributes['db.body'] = body;
if (bodyPayload !== undefined && sendDefaultPii) {
attributes['db.body'] = bodyPayload;
}

return startSpan(
Expand Down Expand Up @@ -413,11 +440,11 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
}

const supabaseContext: Record<string, any> = {};
if (queryItems.length) {
if (queryItems.length && sendDefaultPii) {
supabaseContext.query = queryItems;
}
if (Object.keys(body).length) {
supabaseContext.body = body;
if (bodyPayload !== undefined && sendDefaultPii) {
supabaseContext.body = bodyPayload;
}

captureException(err, scope => {
Expand All @@ -444,12 +471,12 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte

const data: Record<string, unknown> = {};

if (queryItems.length) {
if (queryItems.length && sendDefaultPii) {
data.query = queryItems;
}

if (Object.keys(body).length) {
data.body = body;
if (bodyPayload !== undefined && sendDefaultPii) {
data.body = bodyPayload;
}

if (Object.keys(data).length) {
Expand Down
Loading
Loading