Skip to content

Commit 0149eb0

Browse files
Merge branch 'priscila/chore/add-hit-tracker-columns-to-grouptombstone' into priscila/feat/add-logic-to-update-hit-counter-columns-in-grouptomstone
2 parents d590fda + 6a1b948 commit 0149eb0

File tree

84 files changed

+921
-728
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+921
-728
lines changed

devservices/config.yml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ x-sentry-service-config:
1313
branch: master
1414
repo_link: https://github.com/getsentry/snuba.git
1515
mode: containerized
16+
snuba-profiling:
17+
description: Service that provides fast aggregation and query capabilities on top of Clickhouse that includes profiling consumers
18+
remote:
19+
repo_name: snuba
20+
branch: master
21+
repo_link: https://github.com/getsentry/snuba.git
22+
mode: containerized-profiles
1623
relay:
1724
description: Service event forwarding and ingestion service
1825
remote:
@@ -131,7 +138,33 @@ x-sentry-service-config:
131138
rabbitmq: [postgres, snuba, rabbitmq, spotlight]
132139
symbolicator: [postgres, snuba, symbolicator, spotlight]
133140
memcached: [postgres, snuba, memcached, spotlight]
134-
profiling: [postgres, snuba, vroom, spotlight]
141+
crons:
142+
[
143+
postgres,
144+
snuba,
145+
relay,
146+
spotlight,
147+
ingest-monitors,
148+
monitors-clock-tick,
149+
monitors-clock-tasks,
150+
monitors-incident-occurrences,
151+
worker,
152+
]
153+
profiling:
154+
[
155+
postgres,
156+
snuba-profiling,
157+
relay,
158+
vroom,
159+
spotlight,
160+
ingest-events,
161+
ingest-transactions,
162+
ingest-profiles,
163+
ingest-occurrences,
164+
worker,
165+
post-process-forwarder-errors,
166+
post-process-forwarder-transactions,
167+
]
135168
ingest:
136169
[
137170
snuba,

eslint.config.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ export default typescript.config([
862862
name: 'files/sentry-stories',
863863
files: ['**/*.stories.tsx'],
864864
rules: {
865+
'import/no-webpack-loader-syntax': 'off', // type loader requires webpack syntax
865866
'no-loss-of-precision': 'off', // Sometimes we have wild numbers hard-coded in stories
866867
},
867868
},
@@ -952,6 +953,10 @@ export default typescript.config([
952953
...mdx.flat,
953954
name: 'files/mdx',
954955
files: ['**/*.mdx'],
956+
rules: {
957+
...mdx.flat.rules,
958+
'import/no-webpack-loader-syntax': 'off', // type loader requires webpack syntax
959+
},
955960
},
956961
{
957962
name: 'plugin/boundaries',
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
{
2+
"event_id": "5d6401994d7949d2ac3474f472564370",
3+
"platform": "node",
4+
"message": "",
5+
"datetime": "2025-05-12T22:42:38.642986+00:00",
6+
"breakdowns": {
7+
"span_ops": {
8+
"ops.db": {
9+
"value": 65.715075,
10+
"unit": "millisecond"
11+
},
12+
"total.time": {
13+
"value": 67.105293,
14+
"unit": "millisecond"
15+
}
16+
}
17+
},
18+
"request": {
19+
"url": "http://localhost:3001/vulnerable-login",
20+
"method": "GET",
21+
"query_string": [["type", "account"]]
22+
},
23+
"spans": [
24+
{
25+
"timestamp": 1747089758.567536,
26+
"start_timestamp": 1747089758.567,
27+
"exclusive_time": 0.536203,
28+
"op": "middleware.express",
29+
"span_id": "4a06692f4abc8dbe",
30+
"parent_span_id": "91fa92ff0205967d",
31+
"trace_id": "375a86eca09a4a4e91903838dd771f50",
32+
"status": "ok",
33+
"description": "corsMiddleware",
34+
"origin": "auto.http.otel.express",
35+
"data": {
36+
"express.name": "corsMiddleware",
37+
"express.type": "middleware",
38+
"sentry.op": "middleware.express",
39+
"sentry.origin": "auto.http.otel.express"
40+
},
41+
"sentry_tags": {
42+
"user": "ip:::1",
43+
"user.ip": "::1",
44+
"environment": "production",
45+
"transaction": "GET /vulnerable-login",
46+
"transaction.method": "GET",
47+
"transaction.op": "http.server",
48+
"browser.name": "Chrome",
49+
"sdk.name": "sentry.javascript.node",
50+
"sdk.version": "9.17.0",
51+
"platform": "node",
52+
"os.name": "macOS",
53+
"category": "middleware",
54+
"op": "middleware.express",
55+
"status": "ok",
56+
"trace.status": "ok"
57+
},
58+
"hash": "e6088cf8b370ed60"
59+
},
60+
{
61+
"timestamp": 1747089758.568761,
62+
"start_timestamp": 1747089758.568,
63+
"exclusive_time": 0.761032,
64+
"op": "middleware.express",
65+
"span_id": "92553d2584d250b8",
66+
"parent_span_id": "91fa92ff0205967d",
67+
"trace_id": "375a86eca09a4a4e91903838dd771f50",
68+
"status": "ok",
69+
"description": "jsonParser",
70+
"origin": "auto.http.otel.express",
71+
"data": {
72+
"express.name": "jsonParser",
73+
"express.type": "middleware",
74+
"sentry.op": "middleware.express",
75+
"sentry.origin": "auto.http.otel.express"
76+
},
77+
"sentry_tags": {
78+
"user": "ip:::1",
79+
"user.ip": "::1",
80+
"environment": "production",
81+
"transaction": "GET /vulnerable-login",
82+
"transaction.method": "GET",
83+
"transaction.op": "http.server",
84+
"browser.name": "Chrome",
85+
"sdk.name": "sentry.javascript.node",
86+
"sdk.version": "9.17.0",
87+
"platform": "node",
88+
"os.name": "macOS",
89+
"category": "middleware",
90+
"op": "middleware.express",
91+
"status": "ok",
92+
"trace.status": "ok"
93+
},
94+
"hash": "c81e963dad9ebc6c"
95+
},
96+
{
97+
"timestamp": 1747089758.569093,
98+
"start_timestamp": 1747089758.569,
99+
"exclusive_time": 0.092983,
100+
"op": "request_handler.express",
101+
"span_id": "435146ab0909419d",
102+
"parent_span_id": "91fa92ff0205967d",
103+
"trace_id": "375a86eca09a4a4e91903838dd771f50",
104+
"status": "ok",
105+
"description": "/vulnerable-login",
106+
"origin": "auto.http.otel.express",
107+
"data": {
108+
"express.name": "/vulnerable-login",
109+
"express.type": "request_handler",
110+
"http.route": "/vulnerable-login",
111+
"sentry.op": "request_handler.express",
112+
"sentry.origin": "auto.http.otel.express"
113+
},
114+
"sentry_tags": {
115+
"user": "ip:::1",
116+
"user.ip": "::1",
117+
"environment": "production",
118+
"transaction": "GET /vulnerable-login",
119+
"transaction.method": "GET",
120+
"transaction.op": "http.server",
121+
"browser.name": "Chrome",
122+
"sdk.name": "sentry.javascript.node",
123+
"sdk.version": "9.17.0",
124+
"platform": "node",
125+
"os.name": "macOS",
126+
"op": "request_handler.express",
127+
"status": "ok",
128+
"trace.status": "ok"
129+
},
130+
"hash": "872b0c84a6f1c590"
131+
},
132+
{
133+
"timestamp": 1747089758.637715,
134+
"start_timestamp": 1747089758.572,
135+
"exclusive_time": 65.715075,
136+
"op": "db",
137+
"span_id": "4703181ac343f71a",
138+
"parent_span_id": "91fa92ff0205967d",
139+
"trace_id": "375a86eca09a4a4e91903838dd771f50",
140+
"status": "ok",
141+
"description": "SELECT \"company_account\".\"account\", \"company_account\".\"id\", \"company_account\".\"type\" FROM \"company_account\" WHERE \"company_account\".\"owner\" = ? ORDER BY \"company_account\".\"account\"",
142+
"origin": "auto.db.otel.mysql2",
143+
"data": {
144+
"db.system": "mysql",
145+
"db.connection_string": "jdbc:mysql://localhost:3306/injection_test",
146+
"db.name": "injection_test",
147+
"db.statement": "SELECT \"company_account\".\"account\", \"company_account\".\"id\", \"company_account\".\"type\" FROM \"company_account\" WHERE \"company_account\".\"owner\" = ? ORDER BY \"company_account\".\"account\"",
148+
"db.user": "root",
149+
"net.peer.name": "localhost",
150+
"net.peer.port": 3306,
151+
"otel.kind": "CLIENT",
152+
"sentry.op": "db",
153+
"sentry.origin": "auto.db.otel.mysql2"
154+
},
155+
"hash": "45330ba0cafa5997"
156+
}
157+
]
158+
}

src/sentry/features/temporary.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,6 @@ def register_temporary_features(manager: FeatureManager):
303303
manager.add("organizations:related-issues", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
304304
# Enable the release details performance section
305305
manager.add("organizations:release-comparison-performance", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
306-
# Enable the new release bubbles UI on charts
307-
manager.add("organizations:release-bubbles-ui", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
308306
# Enable replay AI summaries
309307
manager.add("organizations:replay-ai-summaries", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
310308
# Enable version 2 of reprocessing (completely distinct from v1)

src/sentry/middleware/reporting_endpoint.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from django.http.request import HttpRequest
44
from django.http.response import HttpResponseBase
55

6+
from sentry import options
7+
68

79
class ReportingEndpointMiddleware:
810
"""
@@ -14,17 +16,16 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponseBase]):
1416

1517
def __call__(self, request: HttpRequest) -> HttpResponseBase:
1618
response = self.get_response(request)
17-
return self.process_response(request, response)
18-
19-
def process_response(
20-
self, request: HttpRequest, response: HttpResponseBase
21-
) -> HttpResponseBase:
22-
# Check if the request has staff attribute and if staff is active
23-
staff = getattr(request, "staff", None)
24-
if staff and staff.is_active:
25-
# This will enable crashes, intervention and deprecation warnings
26-
# They always report to the default endpoint
27-
response["Reporting-Endpoints"] = (
28-
"default=https://sentry.my.sentry.io/api/0/reporting-api-experiment/"
29-
)
19+
20+
try:
21+
enabled = options.get("issues.browser_reporting.reporting_endpoints_header_enabled")
22+
if enabled:
23+
# This will enable crashes, intervention and deprecation warnings
24+
# They always report to the default endpoint
25+
response["Reporting-Endpoints"] = (
26+
"default=https://sentry.my.sentry.io/api/0/reporting-api-experiment/"
27+
)
28+
except Exception:
29+
pass
30+
3031
return response

src/sentry/migrations/0930_add_hit_counter_columns_to_grouptombstone.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Generated by Django 5.2.1 on 2025-06-17 20:11
1+
# Generated by Django 5.2.1 on 2025-06-18 04:47
22

3-
import django.db.models.functions.datetime
3+
import django.utils.timezone
44
from django.db import migrations, models
55

66
import sentry.db.models.fields.bounded
@@ -30,15 +30,11 @@ class Migration(CheckedMigration):
3030
migrations.AddField(
3131
model_name="grouptombstone",
3232
name="last_seen",
33-
field=models.DateTimeField(
34-
db_default=django.db.models.functions.datetime.Now(), null=True
35-
),
33+
field=models.DateTimeField(default=django.utils.timezone.now, null=True),
3634
),
3735
migrations.AddField(
3836
model_name="grouptombstone",
3937
name="times_seen",
40-
field=sentry.db.models.fields.bounded.BoundedPositiveIntegerField(
41-
db_default=0, null=True
42-
),
38+
field=sentry.db.models.fields.bounded.BoundedPositiveIntegerField(db_default=0),
4339
),
4440
]

src/sentry/models/grouptombstone.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import Any
55

66
from django.db import models
7-
from django.db.models.functions import Now
87
from django.utils import timezone
98

109
from sentry.backup.scopes import RelocationScope
@@ -40,8 +39,8 @@ class GroupTombstone(Model):
4039
blank=True, null=True
4140
)
4241
actor_id = BoundedPositiveIntegerField(null=True)
43-
times_seen = BoundedPositiveIntegerField(db_default=0, null=True)
44-
last_seen = models.DateTimeField(db_default=Now(), null=True)
42+
times_seen = BoundedPositiveIntegerField(db_default=0)
43+
last_seen = models.DateTimeField(default=timezone.now, null=True)
4544

4645
class Meta:
4746
app_label = "sentry"

src/sentry/options/defaults.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3467,6 +3467,15 @@
34673467
flags=FLAG_ALLOW_EMPTY | FLAG_AUTOMATOR_MODIFIABLE,
34683468
)
34693469

3470+
# Enable adding the `Reporting-Endpoints` header, which will in turn enable the sending of Reporting
3471+
# API reports from the browser (as long as it's Chrome).
3472+
register(
3473+
"issues.browser_reporting.reporting_endpoints_header_enabled",
3474+
type=Bool,
3475+
default=False,
3476+
flags=FLAG_AUTOMATOR_MODIFIABLE,
3477+
)
3478+
34703479
# Enable the collection of Reporting API reports via the `/api/0/reporting-api-experiment/`
34713480
# endpoint. When this is false, the endpoint will just 404.
34723481
register(

src/sentry/performance_issues/detectors/sql_injection_detector.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,21 @@ def visit_span(self, span: Span) -> None:
119119
spans_involved = [span["span_id"]]
120120
vulnerable_parameters = []
121121

122+
if "WHERE" not in description.upper():
123+
return
124+
122125
for parameter in self.request_parameters:
123126
value = parameter[1]
124127
key = parameter[0]
125-
if re.search(rf'(?<![\w.])"?{re.escape(key)}"?(?![\w."])', description) and re.search(
126-
rf'(?<![\w.])"?{re.escape(value)}"?(?![\w."])', description
128+
regex_key = rf'(?<![\w.$])"?{re.escape(key)}"?(?![\w.$"])'
129+
regex_value = rf'(?<![\w.$])"?{re.escape(value)}"?(?![\w.$"])'
130+
where_index = description.upper().find("WHERE")
131+
if re.search(regex_key, description[where_index:]) and re.search(
132+
regex_value, description[where_index:]
127133
):
128-
description = description.replace(value, "?")
134+
description = description[:where_index] + re.sub(
135+
regex_value, "?", description[where_index:]
136+
)
129137
vulnerable_parameters.append(key)
130138

131139
if len(vulnerable_parameters) == 0:

src/sentry/uptime/detectors/result_handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def handle_onboarding_result(
9191
project_subscription,
9292
interval_seconds=int(AUTO_DETECTED_ACTIVE_SUBSCRIPTION_INTERVAL.total_seconds()),
9393
mode=ProjectUptimeSubscriptionMode.AUTO_DETECTED_ACTIVE,
94+
ensure_assignment=True,
9495
)
9596
create_system_audit_entry(
9697
organization=detector.project.organization,

src/sentry/uptime/subscriptions/subscriptions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def update_project_uptime_subscription(
282282
trace_sampling: bool | NotSet = NOT_SET,
283283
status: int = ObjectStatus.ACTIVE,
284284
mode: ProjectUptimeSubscriptionMode = ProjectUptimeSubscriptionMode.MANUAL,
285+
ensure_assignment: bool = False,
285286
):
286287
"""
287288
Links a project to an uptime subscription so that it can process results.
@@ -344,7 +345,7 @@ def update_project_uptime_subscription(
344345
case ObjectStatus.DISABLED:
345346
disable_uptime_detector(detector)
346347
case ObjectStatus.ACTIVE:
347-
enable_uptime_detector(detector)
348+
enable_uptime_detector(detector, ensure_assignment=ensure_assignment)
348349

349350
# ProjectUptimeSubscription may have been updated as part of
350351
# {enable,disable}_uptime_detector

src/sentry/workflow_engine/endpoints/validators/base/detector.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def create(self, validated_data):
104104
workflow_condition_group=condition_group,
105105
type=validated_data["type"].slug,
106106
config=validated_data.get("config", {}),
107+
created_by_id=self.context["request"].user.id,
107108
)
108109
DataSourceDetector.objects.create(data_source=detector_data_source, detector=detector)
109110

0 commit comments

Comments
 (0)