Skip to content

[UPSTREAM CHANGES] latest changes as of Mon Apr 27 2026 00:59:58 GMT+0000 (Coordinated Universal Time) - #226

Open
github-actions[bot] wants to merge 5457 commits into
masterfrom
upstream-changes-2026-04-27
Open

[UPSTREAM CHANGES] latest changes as of Mon Apr 27 2026 00:59:58 GMT+0000 (Coordinated Universal Time)#226
github-actions[bot] wants to merge 5457 commits into
masterfrom
upstream-changes-2026-04-27

Conversation

@github-actions

Copy link
Copy Markdown

This PR is auto-generated by
actions/github-script.

bufke and others added 30 commits March 23, 2026 19:08
Adds a reproducible benchmark suite that measures web server memory
growth under high concurrency with artificial DB latency. Supports
ingest-only and mixed workload (ingest + API reads + uptime checks)
modes. Uses Docker Compose with tc netem for network simulation and
cgroup memory sampling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore(deps): update dependency duckdb to v1.5.1

See merge request glitchtip/glitchtip-backend!2228
chore(deps): update dependency boto3 to v1.42.74

See merge request glitchtip/glitchtip-backend!2227
chore(deps): update dependency symbolic to v12.17.3

See merge request glitchtip/glitchtip-backend!2229
Add MALLOC_MMAP_THRESHOLD_=65536 and MALLOC_TRIM_THRESHOLD_=65536
to tune-malloc.sh. Forces allocations >64KB to use mmap/munmap
directly instead of arena sub-allocation, so pages are returned
to the OS immediately on free.

Benchmarked effect: baseline RSS dropped from 483MB to 223MB (-260MB)
with no throughput impact. Growth rate under load was unchanged,
confirming the savings come from reduced arena fragmentation.

Production analysis (VictoriaMetrics, /proc/9/maps) showed 471MB of
anonymous mmap regions held by glibc arenas — 58% of RSS. The lower
mmap threshold prevents large transient allocations (JSON buffers,
DB result sets, pydantic model trees) from fragmenting these arenas.

Also adds memray ASGI wrapper and malloc comparison benchmark script
for future profiling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add memory growth benchmark with tc netem latency injection

See merge request glitchtip/glitchtip-backend!2226
fix: widen uptime monitor interval for weekly heartbeats

See merge request glitchtip/glitchtip-backend!2217
feat: support otel_log envelope item type for OpenTelemetry log ingestion

See merge request glitchtip/glitchtip-backend!2225
add previous period subscription usage endpoint

See merge request glitchtip/glitchtip-backend!2186
The ON DELETE CASCADE triggers on issueevent, issuetag, and
issueaggregate must scan every hash sub-partition when an Issue is
deleted. At 500+ partitions this exceeds statement_timeout, causing
OperationalError during maintenance cleanup.

Application code in delete_issues_in_batches() already pre-deletes
child rows before deleting parent Issues, making the DB-level
CASCADE redundant.

- Set db_constraint=False on issue FK for IssueEvent, IssueTag,
  IssueAggregate models
- Migration drops the three constraints on existing databases using
  SeparateDatabaseAndState to keep Django state in sync
- Raw SQL files updated to omit the constraints for new installs

Prepared with AI assistance. Human review required.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: drop issue_id FK constraints on partitioned tables

See merge request glitchtip/glitchtip-backend!2230
Previously, assemble_artifacts() raised AssembleArtifactsError on
validation failures (bad zip, missing manifest, org/release mismatch,
path traversal). This left the assemble status stuck at ASSEMBLING,
causing client SDKs to retry indefinitely — observed as 1,200+ repeat
errors from a single organization uploading a non-zip file.

Now all error paths set ChunkFileState.ERROR with a descriptive detail
message, clean up temp resources (including any File objects already
created during artifact processing), and return gracefully. Bare
except clauses log warnings with exc_info for diagnostics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore(deps): update dependency boto3 to v1.42.75

See merge request glitchtip/glitchtip-backend!2233
chore(deps): update dependency sentry-sdk to v2.56.0

See merge request glitchtip/glitchtip-backend!2235
fix: set ERROR status on artifact assembly failures

See merge request glitchtip/glitchtip-backend!2231
The create_release endpoint used acreate() which threw an IntegrityError
when a release with the same (organization_id, version) already existed.
Changed to aget_or_create to make the endpoint idempotent, matching the
behavior of create_project_release.

Fixes https://app.glitchtip.com/burke-software/issues/5534440

AI Disclosure: This commit was authored with the assistance of an AI agent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code's MCP client does not yet support automatic OAuth token
refresh (grant_type=refresh_token), so users had to re-authenticate
every hour. Increase access token lifetime to 8 hours to last a full
work session, matching GitHub's token lifetime.

AI Disclosure: This commit was authored with the assistance of an AI agent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: increase MCP OAuth access token lifetime to 8 hours

See merge request glitchtip/glitchtip-backend!2237
chore(deps): update dependency boto3 to v1.42.76

See merge request glitchtip/glitchtip-backend!2238
bufke and others added 30 commits April 21, 2026 22:34
Webhook alert recipients (generic webhook, Discord, Google Chat, ntfy,
Microsoft Teams, Zulip) previously accepted any URL, including loopback,
RFC1918, and cloud instance-metadata addresses. Any user with
alert-create permission could point a webhook at an internal service and
use a delivered alert payload as an exfiltration channel or to poke
unauthenticated internal APIs.

Introduce a shared url_validation module with is_ip_blocked,
check_url_safe (async, for runtime), and validate_public_url (sync, for
schema validation). Alerts schema rejects private-IP recipient URLs at
creation time so operators see a 400 up front; each send_* transport
also checks at delivery time to cover DNS rebinding and config changes.

Gated by a new GLITCHTIP_ALLOW_PRIVATE_IPS env var (default False),
separate from GLITCHTIP_UPTIME_ALLOW_PRIVATE_IPS: an operator may want
to monitor internal services without also opening webhooks as an
exfiltration channel.

Uptime's existing validator is unchanged in behaviour; it now imports
is_ip_blocked from the shared module so there is one source of truth.
chore(deps): update dependency boto3 to v1.42.93

See merge request glitchtip/glitchtip-backend!2325
…ault

`DATA_UPLOAD_MAX_MEMORY_SIZE` was set to 4294967295 with a `# TMP REMOVE
THIS` note. The value existed to paper over `DecompressBodyMiddleware`,
which sets `CONTENT_LENGTH` to the same 4 GB sentinel after wrapping the
request stream (the true decompressed length is unknown up front). A
single request could force Django to buffer up to 4 GB before any view
ran — a few concurrent requests could OOM the pod.

Tie the two ends together properly:

- Middleware now advertises `GLITCHTIP_MAX_UNZIPPED_PAYLOAD_SIZE` as the
  `CONTENT_LENGTH` sentinel. The decoder already enforces that cap at
  read time, so the advertised length matches the real hard ceiling.
- `DATA_UPLOAD_MAX_MEMORY_SIZE` default becomes
  `GLITCHTIP_MAX_UNZIPPED_PAYLOAD_SIZE + 1 MB`, env-overridable, so
  raising the unzipped cap via env automatically widens the Django check
  to match.
- Constants reordered so the dependent default compiles cleanly.

Multipart file uploads (minidumps, source-map chunks) go through
`FILE_UPLOAD_MAX_MEMORY_SIZE` and spill to disk, so this limit does not
need to cover them.
The envelope event path checked `isinstance(item.user, dict)` before
setting `user.ip_address = client_ip`, but `WebIngestIssueEvent.user`
is always an `EventUser` pydantic model or None — never a dict. The
branch never fired, so payload-supplied `user.ip_address` passed
through unchanged even with `scrub_ip_addresses=True` (the project /
organization setting anonymizes the connection IP but does not touch
payload-supplied IPs).

Mirror the `/store/` and `/security/` handlers: when the connection
IP is available, overwrite the payload field or construct a fresh
`EventUser`. The connection IP is already anonymized upstream in
`get_ip_address()` when scrubbing is enabled, so this restores the
intended privacy behavior.

Adds regression tests covering both scrub states and the case where
the payload does not include a `user` object.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The DNS rebinding test patches `BaseEventLoop.getaddrinfo` with a
stub that must accept the same positional and keyword arguments as
the real method but does not use them. Rename to the conventional
`*_, **__` to satisfy `vulture --min-confidence 100`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OAuth refresh tokens and paired access tokens were previously stored
in plaintext (DB column for the refresh token, cache key for the
access token). A read-only snapshot of either store yielded directly
usable credentials.

Store only digests at rest:

* ``OAuthRefreshToken.token`` and ``access_token_key`` are replaced
  with ``token_prefix`` (indexed, first 8 chars) + ``token_digest``
  and ``access_token_digest`` (SHA-256 hex). Lookup by prefix + a
  constant-time digest compare.
* ``_access_cache_key()`` now hashes the token string into the key,
  so the Valkey keyspace no longer contains access-token plaintext
  either.
* SHA-256 is sufficient here: inputs are 256 random bits from
  ``generate_token()``, so a KDF adds no value.

The migration truncates ``OAuthRefreshToken`` because the old
columns held the only plaintext we could have rehashed. Connected
OAuth clients will see their next access-token exchange fail and
transparently re-authorize via the OAuth flow. Access tokens are
cache-backed and expire within 8h regardless.

Minor behavioural change: client-initiated revocation via
``revoke_token(RefreshToken)`` can no longer proactively purge the
paired access cache entry (the digest is one-way). The cache entry
expires naturally; a simultaneous ``revoke_token(AccessToken)`` from
the client still purges it because the caller supplies the
plaintext.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(settings): replace 4 GB DATA_UPLOAD_MAX workaround with sized default

See merge request glitchtip/glitchtip-backend!2326
fix(alerts): validate outbound webhook URLs to block SSRF

See merge request glitchtip/glitchtip-backend!2324
refactor(event-ingest): apply client IP to envelope events consistently

See merge request glitchtip/glitchtip-backend!2328
chore(deps): update dependency boto3 to v1.42.94

See merge request glitchtip/glitchtip-backend!2331
chore(deps): update dependency symbolic to v12.18.3

See merge request glitchtip/glitchtip-backend!2332
…faults

Two related hardening changes that preserve out-of-the-box deployment
behaviour across k8s, reverse-proxied, and single-host topologies:

1. SESSION_COOKIE_SECURE and CSRF_COOKIE_SECURE default based on
   GLITCHTIP_URL.scheme. https deployments get Secure cookies with zero
   operator action; http deployments (local, internal LAN) still let
   users log in without manual config. Both remain env-overridable in
   either direction.

2. Startup RuntimeWarnings (stderr, not hard failures) when DEBUG=False
   and either SECRET_KEY is the placeholder "change_me" or ALLOWED_HOSTS
   is the wildcard default. Escalation to a hard boot-time check is
   deferred to a major release so existing self-hosters aren't broken
   on upgrade.

ALLOWED_HOSTS must stay wildcard-by-default because real deployments
need Service DNS + Ingress host + pod IP + LB healthcheck name to work
without orchestration changes; similarly SECURE_SSL_REDIRECT and HSTS
stay opt-in because TLS typically terminates upstream. Those move to
docs rather than defaults.
Having both `glitchtip/tests.py` and `glitchtip/tests/` in the same
Python package confuses unittest discovery with
"'tests' module incorrectly imported" on case-sensitive filesystems.
Move the deploy-defaults test classes back into the single
`glitchtip/tests.py` module, renaming the local helpers to avoid
colliding with any neighbours.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(settings): derive cookie Secure flags from URL; warn on unsafe defaults

See merge request glitchtip/glitchtip-backend!2327
chore(deps): update dependency boto3 to v1.42.95

See merge request glitchtip/glitchtip-backend!2334
chore(deps): update dependency granian to v2.7.4

See merge request glitchtip/glitchtip-backend!2335
The access-token cache is keyed by sha256(plaintext), and the paired
refresh-token row already stores that same sha256 in
access_token_digest. Reconstructing the cache key from the digest
preserves the original revocation semantics without needing the
plaintext: both exchange_refresh_token and revoke_token(RefreshToken)
can again delete the paired cache entry immediately rather than
waiting out ACCESS_TOKEN_LIFETIME.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore(deps): update dependency boto3 to v1.42.96

See merge request glitchtip/glitchtip-backend!2337
chore(deps): update dependency ruff to v0.15.12

See merge request glitchtip/glitchtip-backend!2338
chore(deps): update dependency ipython to v9.13.0

See merge request glitchtip/glitchtip-backend!2339
feat(oauth): hash refresh + access tokens at rest

See merge request glitchtip/glitchtip-backend!2330
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants