Skip to content
Open
24 changes: 24 additions & 0 deletions src/backends/native/sentry_crash_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -3571,6 +3571,30 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc)
#endif

cleanup:
// Send the staged session-replay envelope same-session, enriched from the
// crash event (`<run>/__sentry-event`) so it shares the crash's
// tags/contexts/trace.
if (options && options->transport) {
sentry_value_t crash_event = sentry_value_new_null();
if (run_folder) {
sentry_path_t *sentry_event_path
= sentry__path_join_str(run_folder, "__sentry-event");
if (sentry_event_path) {
size_t ev_len = 0;
char *ev_json
= sentry__path_read_to_buffer(sentry_event_path, &ev_len);
if (ev_json) {
crash_event = sentry__value_from_json(ev_json, ev_len);
sentry_free(ev_json);
}
sentry__path_free(sentry_event_path);
}
}
sentry__session_replay_flush_pending(
options, options->transport, crash_event);
sentry_value_decref(crash_event);
}
Comment thread
tustanivsky marked this conversation as resolved.

// Send all other envelopes from run folder (logs, etc.) before cleanup
if (run_folder && options && options->transport && options->run) {
SENTRY_DEBUG("Checking for additional envelopes in run folder");
Expand Down
1 change: 1 addition & 0 deletions src/sentry_client_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef enum {
SENTRY_DATA_CATEGORY_LOG_ITEM,
SENTRY_DATA_CATEGORY_FEEDBACK,
SENTRY_DATA_CATEGORY_TRACE_METRIC,
SENTRY_DATA_CATEGORY_REPLAY,
SENTRY_DATA_CATEGORY_MAX
} sentry_data_category_t;

Expand Down
6 changes: 6 additions & 0 deletions src/sentry_envelope.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ envelope_item_get_ratelimiter_category(const sentry_envelope_item_t *item)
return SENTRY_RL_CATEGORY_SESSION;
} else if (sentry__string_eq(ty, "transaction")) {
return SENTRY_RL_CATEGORY_TRANSACTION;
} else if (sentry__string_eq(ty, "replay_video")) {
return SENTRY_RL_CATEGORY_REPLAY;
} else if (sentry__string_eq(ty, "client_report")) {
// internal telemetry, bypass rate limiting
return -1;
Expand All @@ -160,6 +162,8 @@ item_type_to_data_category(const char *ty)
return SENTRY_DATA_CATEGORY_FEEDBACK;
} else if (sentry__string_eq(ty, "trace_metric")) {
return SENTRY_DATA_CATEGORY_TRACE_METRIC;
} else if (sentry__string_eq(ty, "replay_video")) {
return SENTRY_DATA_CATEGORY_REPLAY;
}
return SENTRY_DATA_CATEGORY_ERROR;
}
Expand Down Expand Up @@ -1347,6 +1351,8 @@ data_category_to_string(sentry_data_category_t category)
return "feedback";
case SENTRY_DATA_CATEGORY_TRACE_METRIC:
return "trace_metric";
case SENTRY_DATA_CATEGORY_REPLAY:
return "replay";
case SENTRY_DATA_CATEGORY_MAX:
default:
return "unknown";
Expand Down
5 changes: 4 additions & 1 deletion src/sentry_ratelimiter.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "sentry_slice.h"
#include "sentry_utils.h"

#define MAX_RATE_LIMITS 4
#define MAX_RATE_LIMITS 5
#define MAX_RETRY_AFTER (24 * 60 * 60) // 24h

struct sentry_rate_limiter_s {
Expand All @@ -28,6 +28,7 @@ sentry__rate_limiter_new(void)
rl->disabled_until[SENTRY_RL_CATEGORY_ERROR] = 0;
rl->disabled_until[SENTRY_RL_CATEGORY_SESSION] = 0;
rl->disabled_until[SENTRY_RL_CATEGORY_TRANSACTION] = 0;
rl->disabled_until[SENTRY_RL_CATEGORY_REPLAY] = 0;
}
return rl;
}
Expand Down Expand Up @@ -64,6 +65,8 @@ sentry__rate_limiter_update_from_header(
} else if (sentry__slice_eqs(category, "transaction")) {
rl->disabled_until[SENTRY_RL_CATEGORY_TRANSACTION]
= retry_after;
} else if (sentry__slice_eqs(category, "replay")) {
rl->disabled_until[SENTRY_RL_CATEGORY_REPLAY] = retry_after;
}

categories = sentry__slice_advance(categories, category.len);
Expand Down
1 change: 1 addition & 0 deletions src/sentry_ratelimiter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define SENTRY_RL_CATEGORY_ERROR 1
#define SENTRY_RL_CATEGORY_SESSION 2
#define SENTRY_RL_CATEGORY_TRANSACTION 3
#define SENTRY_RL_CATEGORY_REPLAY 4

typedef struct sentry_rate_limiter_s sentry_rate_limiter_t;

Expand Down
16 changes: 16 additions & 0 deletions src/sentry_session_replay.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,20 @@
*/
sentry_path_t *sentry__session_replay_get_path(const sentry_options_t *options);

/**
* Build and send the session-replay envelope(s) the embedder staged in
* `<database>/replays/` (a `replay-<id>.json` sidecar next to its mp4). Native-
* daemon-only: called out-of-process by the crash daemon, so it runs only on a
* crash and delivers same-session. Each consumed sidecar and its mp4 are
* removed once the envelope has been captured (malformed sidecars are removed
* too), so the flush is idempotent. The SDK owns this cleanup -- the embedder
* doesn't have to clear the `replays/` folder itself.
*
* `scope_source` is the crash event (`<run>/__sentry-event`); its scope fields
* and trace id are copied onto the replay, and its timestamp ends the replay
* window. Null skips enrichment.
*/

Check warning on line 39 in src/sentry_session_replay.h

View check run for this annotation

@sentry/warden / warden: security-review

[VZ7-3P5] Unsanitized `videoFilename` from sidecar JSON enables path traversal file read (additional location)

In `sentry__session_replay_flush_pending`, the `videoFilename` value read from the embedder-written JSON sidecar is passed directly to `sentry__path_join_str(dir, video_filename)` without validation; on Unix an absolute path is returned verbatim, so a sidecar with `"videoFilename": "/etc/shadow"` makes the crash daemon read an arbitrary file and exfiltrate it as the `replay_video` blob to Sentry. Validate that the resolved path stays within the `replays/` directory before reading.
void sentry__session_replay_flush_pending(const sentry_options_t *options,
sentry_transport_t *transport, sentry_value_t scope_source);

#endif
Loading
Loading