Skip to content

Commit 142f77d

Browse files
authored
fix(security): resolve dependency vulns and fix container exec tenant isolation (#54)
* fix(security): resolve dependency vulns and fix container exec tenant isolation Dependency updates: - Bump tar 0.4 → 0.4.45 (RUSTSEC-2026-0067, RUSTSEC-2026-0068: symlink follow + PAX header) - Update aws-lc-rs → 1.16.2 / aws-lc-sys → 0.39.1 (RUSTSEC-2026-0044: X.509 bypass) - Update rustls-webpki → 0.103.10 (RUSTSEC-2026-0049: CRL matching) - Update rkyv → 0.7.46 (RUSTSEC-2026-0001: undefined behavior on OOM) - Update rustls → 0.23.37 Code fixes: - container_exec: validate container belongs to project/environment before exec, preventing cross-tenant container access (HIGH severity) - FilesystemStorage: add path traversal protection to resolve_path(), rejecting keys with ".." components (MEDIUM severity) Remaining unfixable vulns documented in Cargo.toml comments. * docs(changelog): add security fixes to unreleased section * fix(email): make RequestMetadata optional in tracking handlers The track_open and track_click handlers are public routes (no auth) mounted via configure_public_routes(), which doesn't have the middleware that injects RequestMetadata. This caused a 500 error: "Missing request extension: RequestMetadata was not found". Fix: accept RequestMetadata as Option<Extension<RequestMetadata>> and fall back to extracting IP/UA from headers directly. * feat(email): store tracked HTML separately from original Add tracked_html_body column to emails table to store the final HTML sent to the provider (with tracking pixel + rewritten links), separate from the original html_body. This lets the dashboard show the tracked version without triggering a fake open event from the preview pixel. - Migration: add nullable tracked_html_body text column - Entity: add tracked_html_body field - EmailService: store tracked_html in new column during send - API: expose tracked_html_body in email list/detail responses * fix(email): add missing link_url and link_index columns to email_events The email_events table may have been created before link_url and link_index columns were added to the CREATE TABLE statement. This migration adds them with IF NOT EXISTS to fix tracking failures: "column link_url does not exist". * feat(email): show per-link click breakdown in email detail UI Add a "Link Clicks" section to the tracking card that fetches /emails/{id}/tracking/links and shows each tracked link with its click count badge. Links with clicks get a default badge, unclicked links get a secondary badge. * docs(changelog): add email tracking fixes and features to unreleased * fix(email): use external_url from config for tracking URLs The tracking service was using a hardcoded TEMPS_BASE_URL env var (defaulting to localhost:3000), which meant tracking pixels and click links in emails pointed to localhost — unreachable from Gmail's image proxy or any external email client. Now uses ConfigService.get_external_url_or_default() to dynamically resolve the external URL from the database settings, matching how other subsystems (error tracking, deployments) already work.
1 parent 4983a00 commit 142f77d

File tree

21 files changed

+460
-175
lines changed

21 files changed

+460
-175
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- **Database pool configuration**: env vars `TEMPS_DB_MAX_CONNECTIONS` (default 100), `TEMPS_DB_MIN_CONNECTIONS` (default 1), `TEMPS_DB_ACQUIRE_TIMEOUT` (default 30s), and `TEMPS_DB_IDLE_TIMEOUT` (default 600s) for tuning the SQLx connection pool on resource-constrained servers
1313
- **Enter-submit in wizards**: `useEnterSubmit` hook added to Domain, DNS Provider, Domain Creation, and Import wizards — pressing Enter advances to the next step or submits on the final step
1414
- Documentation for new pool environment variables in the environment variables reference
15+
- **Email tracking — tracked HTML storage**: new `tracked_html_body` column stores the final HTML sent to the provider (with tracking pixel and rewritten links), separate from the original `html_body` to avoid triggering fake opens in dashboard previews
16+
- **Email tracking — per-link click breakdown**: email detail page now shows each tracked link with its individual click count
17+
18+
### Security
19+
- **Container exec tenant isolation**: `exec_command` and `container_terminal` handlers now verify the container belongs to the requested project/environment before allowing access, preventing cross-tenant container exec
20+
- **Path traversal protection**: `FilesystemStorage::resolve_path` rejects storage keys containing `..` components, preventing potential directory escape
21+
- Bump `aws-lc-sys` 0.38.0 → 0.39.1 (RUSTSEC-2026-0044, RUSTSEC-2026-0048: X.509 name constraints bypass)
22+
- Bump `rustls-webpki` 0.103.7 → 0.103.10 (RUSTSEC-2026-0049: CRL distribution point matching)
23+
- Bump `tar` 0.4.44 → 0.4.45 (RUSTSEC-2026-0067, RUSTSEC-2026-0068: symlink follow and PAX header issues)
24+
- Bump `rkyv` 0.7.45 → 0.7.46 (RUSTSEC-2026-0001: undefined behavior on OOM)
25+
- Bump `rustls` 0.23.34 → 0.23.37
1526

1627
### Fixed
28+
- Email tracking open/click endpoints returned 500 (`RequestMetadata` extension not found) because public routes lacked the middleware; now gracefully falls back to extracting IP/UA from headers
29+
- Email tracking events failed with `column "link_url" does not exist` on databases where the column was missing; added migration to backfill
1730
- Database connections could accumulate without recycling due to missing `idle_timeout` on the connection pool; now defaults to 10 minutes
1831
- `gh release create` failure on duplicate tags: removed invalid `--clobber` flag, then re-added correctly
1932
- Install script and Homebrew formula pointed to `davidviejo/temps` instead of `gotempsh/temps`, causing 404s on binary download

Cargo.lock

Lines changed: 41 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ regex = "1.11"
240240
# Compression & Archive
241241
# ============================================================================
242242
flate2 = "1.0"
243-
tar = "0.4"
243+
tar = "0.4.45"
244244
zip = "2.2"
245245
zstd = "0.13"
246246

@@ -318,6 +318,15 @@ opt-level = 2 # Optimize dependencies
318318
# - atty 0.2.14: RESOLVED in Pingora 0.7.0 (upgraded to clap 4.5)
319319
# - idna 0.2.3: RESOLVED by upgrading check-if-email-exists 0.9 → 0.11
320320
# - lru 0.12.5: RESOLVED by upgrading aws-sdk-s3 (new version dropped lru 0.12)
321+
# - aws-lc-sys 0.38.0 (RUSTSEC-2026-0044, RUSTSEC-2026-0048): RESOLVED by cargo update aws-lc-rs → 1.16.2
322+
# - rustls-webpki 0.103.7 (RUSTSEC-2026-0049): RESOLVED by cargo update rustls-webpki → 0.103.10
323+
# - tar 0.4.44 (RUSTSEC-2026-0067, RUSTSEC-2026-0068): RESOLVED by bumping tar to 0.4.45
324+
# - rkyv 0.7.45 (RUSTSEC-2026-0001): RESOLVED by cargo update rkyv → 0.7.46
325+
#
326+
# 5. RUSTSEC-2023-0071: rsa 0.9.10 (MEDIUM severity)
327+
# - Title: Marvin Attack: potential key recovery through timing sidechannels
328+
# - Status: No fixed upgrade available upstream
329+
# - Mitigation: RSA used for SSH key operations only, not exposed to network timing attacks
321330
#
322331
# All remaining items are transitive dependencies from third-party crates that we
323332
# cannot directly patch without forking. Monitoring upstream for updates.

0 commit comments

Comments
 (0)