Files
ersatztv/ErsatzTV/Middleware/RequestLogScrubber.cs
T
timothyandtimothy 0c063c23fb
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 7m45s
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 14m19s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 18m27s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m10s
harden(421,559): percent-encode access_token in IPTV URLs, redact from logs, no-store on tokened manifests (#574)
Co-authored-by: Timothy <timothy.look@gmail.com>
Co-committed-by: Timothy <timothy.look@gmail.com>
2026-07-23 16:30:59 +00:00

36 lines
1.4 KiB
C#

using Microsoft.Extensions.Primitives;
namespace ErsatzTV.Middleware;
/// <summary>
/// Builds the request path Serilog logs with the <c>access_token</c> query value redacted (#559).
/// The default <c>UseSerilogRequestLogging</c> template logs the full query for every request; the
/// <c>ex != null</c> and <c>StatusCode &gt; 499</c> branches log at Error regardless of path, so a 5xx
/// on an <c>/iptv/...?access_token=&lt;jwt&gt;</c> URL would otherwise write the replayable, globally
/// scoped token to the log. This keeps the rest of the query (e.g. <c>mode=segmenter</c>) for
/// debugging while masking only the credential.
/// </summary>
internal static class RequestLogScrubber
{
private const string TokenParameter = "access_token";
private const string Redacted = "***";
public static string ScrubbedPath(HttpRequest request)
{
string path = request.Path.ToString();
if (!request.QueryString.HasValue)
{
return path;
}
var pairs = new List<string>();
foreach (KeyValuePair<string, StringValues> pair in request.Query)
{
bool isToken = string.Equals(pair.Key, TokenParameter, StringComparison.OrdinalIgnoreCase);
pairs.Add($"{pair.Key}={(isToken ? Redacted : pair.Value.ToString())}");
}
return pairs.Count == 0 ? path : $"{path}?{string.Join('&', pairs)}";
}
}