From 4c69b9b4769b1c4c62fec31649ff1d51826444f0 Mon Sep 17 00:00:00 2001 From: Milos Pesic Date: Fri, 14 Aug 2026 13:19:59 +0200 Subject: [PATCH 1/2] Redact mux/twitch shorthand stream keys and websocket url query values mux:// and twitch:// shorthand stream urls carry the stream key as the entire authority, but RedactStreamKey only matched rtmp(s):// urls, so shorthand keys passed through redaction unchanged and ended up raw in request logs. Track egress websocket urls can carry credentials in query parameters and were not redacted at all. Also fixes RedactUpload reading AssumeRoleExternalId instead of SessionToken when redacting SessionToken. Co-Authored-By: Claude Fable 5 --- egress/redact.go | 7 ++++++- egress/redact_test.go | 31 +++++++++++++++++++++++++++++++ utils/redact.go | 23 +++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/egress/redact.go b/egress/redact.go index 96ffe66c0..944e84c69 100644 --- a/egress/redact.go +++ b/egress/redact.go @@ -35,7 +35,7 @@ func RedactUpload(req UploadRequest) { s3.AccessKey = utils.Redact(s3.AccessKey, "{access_key}") s3.Secret = utils.Redact(s3.Secret, "{secret}") s3.AssumeRoleExternalId = utils.Redact(s3.AssumeRoleExternalId, "{external_id}") - s3.SessionToken = utils.Redact(s3.AssumeRoleExternalId, "{session_token}") + s3.SessionToken = utils.Redact(s3.SessionToken, "{session_token}") return } @@ -91,6 +91,11 @@ func RedactDirectOutputs(out DirectOutput) { if f := out.GetFile(); f != nil { RedactUpload(f) } + if track, ok := out.(*livekit.TrackEgressRequest); ok { + if ws, ok := track.Output.(*livekit.TrackEgressRequest_WebsocketUrl); ok { + ws.WebsocketUrl = utils.RedactUrlQueryValues(ws.WebsocketUrl) + } + } } func RedactStreamKeys(stream *livekit.StreamOutput) { diff --git a/egress/redact_test.go b/egress/redact_test.go index 561e67276..99e23c4fa 100644 --- a/egress/redact_test.go +++ b/egress/redact_test.go @@ -71,6 +71,20 @@ func TestRedactUpload(t *testing.T) { require.Equal(t, "{external_id}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_S3).S3.AssumeRoleExternalId) require.Equal(t, "{session_token}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_S3).S3.SessionToken) + sessionTokenOnly := &livekit.EncodedFileOutput{ + Output: &livekit.EncodedFileOutput_S3{ + S3: &livekit.S3Upload{ + AccessKey: "ACCESS_KEY", + Secret: "LONG_SECRET_STRING", + SessionToken: "SESSION_TOKEN", + }, + }, + } + cl = proto.Clone(sessionTokenOnly) + RedactUpload(cl.(UploadRequest)) + + require.Equal(t, "{session_token}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_S3).S3.SessionToken) + cl = proto.Clone(image) RedactUpload(cl.(UploadRequest)) @@ -93,11 +107,17 @@ func TestRedactStreamOutput(t *testing.T) { so := &livekit.StreamOutput{ Urls: []string{ "rtmps://foo.bar.com/app/secret_stream_key", + "mux://8e0b1b9c-50a2-d893-ec0a-102056d112ae", + "twitch://live_12345678_abcdefghijklmnop", + "srt://foo.bar.com:9999", }, } RedactStreamKeys(so) require.Equal(t, "rtmps://foo.bar.com/app/{sec...key}", so.Urls[0]) + require.Equal(t, "mux://{8e0...dae}", so.Urls[1]) + require.Equal(t, "twitch://{liv...nop}", so.Urls[2]) + require.Equal(t, "srt://foo.bar.com:9999", so.Urls[3]) } func TestRedactEncodedOutputs(t *testing.T) { @@ -154,4 +174,15 @@ func TestRedactDirectOutput(t *testing.T) { RedactDirectOutputs(track) require.Equal(t, "{access_key}", track.Output.(*livekit.TrackEgressRequest_File).File.Output.(*livekit.DirectFileOutput_S3).S3.AccessKey) require.Equal(t, "{secret}", track.Output.(*livekit.TrackEgressRequest_File).File.Output.(*livekit.DirectFileOutput_S3).S3.Secret) + + websocket := &livekit.TrackEgressRequest{ + Output: &livekit.TrackEgressRequest_WebsocketUrl{ + WebsocketUrl: "wss://foo.bar.com/audio?callId=12145&token=7df13dab0d4437602d6f7056b97e72b9", + }, + } + + RedactDirectOutputs(websocket) + require.Equal(t, + "wss://foo.bar.com/audio?callId={1...5}&token={7df...2b9}", + websocket.Output.(*livekit.TrackEgressRequest_WebsocketUrl).WebsocketUrl) } diff --git a/utils/redact.go b/utils/redact.go index d894f6efa..cf476b264 100644 --- a/utils/redact.go +++ b/utils/redact.go @@ -23,7 +23,14 @@ import ( // rtmp urls must be of format rtmp(s)://{host}(/{path})/{app}/{stream_key}( live=1) var rtmpRegexp = regexp.MustCompile(`^(rtmps?://)(.*/)(.*/)(\S*)( live=1)?$`) +// mux and twitch shorthand urls carry the stream key as the entire authority +var shorthandStreamRegexp = regexp.MustCompile(`^(mux|twitch)://(\S+)$`) + func RedactStreamKey(url string) (string, bool) { + if match := shorthandStreamRegexp.FindStringSubmatch(url); len(match) == 3 { + return match[1] + "://" + RedactIdentifier(match[2]), true + } + match := rtmpRegexp.FindStringSubmatch(url) if len(match) != 6 { return url, false @@ -33,6 +40,22 @@ func RedactStreamKey(url string) (string, bool) { return strings.Join(match[1:], ""), true } +// RedactUrlQueryValues redacts every query parameter value in rawUrl, keeping parameter names intact. +func RedactUrlQueryValues(rawUrl string) string { + base, query, found := strings.Cut(rawUrl, "?") + if !found || query == "" { + return rawUrl + } + + params := strings.Split(query, "&") + for i, p := range params { + if k, v, ok := strings.Cut(p, "="); ok && v != "" { + params[i] = k + "=" + RedactIdentifier(v) + } + } + return base + "?" + strings.Join(params, "&") +} + func RedactIdentifier(identifier string) string { var prefix, suffix string for i := 3; i > 0; i-- { From f65a9be57338108c34853c4640fad1ac2e70f242 Mon Sep 17 00:00:00 2001 From: Milos Pesic Date: Fri, 14 Aug 2026 13:33:52 +0200 Subject: [PATCH 2/2] Fix expected mux redaction suffix in TestRedactStreamOutput Co-Authored-By: Claude Fable 5 --- egress/redact_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egress/redact_test.go b/egress/redact_test.go index 99e23c4fa..56791ae07 100644 --- a/egress/redact_test.go +++ b/egress/redact_test.go @@ -115,7 +115,7 @@ func TestRedactStreamOutput(t *testing.T) { RedactStreamKeys(so) require.Equal(t, "rtmps://foo.bar.com/app/{sec...key}", so.Urls[0]) - require.Equal(t, "mux://{8e0...dae}", so.Urls[1]) + require.Equal(t, "mux://{8e0...2ae}", so.Urls[1]) require.Equal(t, "twitch://{liv...nop}", so.Urls[2]) require.Equal(t, "srt://foo.bar.com:9999", so.Urls[3]) }