using System.Linq; using System.Net; using System.Text.RegularExpressions; using NzbDrone.Common.Extensions; namespace NzbDrone.Common.Instrumentation { public class CleanseLogMessage { private static readonly Regex[] CleansingRules = { // Url new (@"(?<=[?&: ;])((?:api|auth|pass)?key|(?:access[-_]?|refresh_)?token|auth|user|u?id|api|[a-z_]*apikey|account|passwd|pwd)=(?[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=[?& ])[^=]*?(username|passwo?rd)=(?[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"rss(24h)?\.torrentleech\.org/(?!rss)(?[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"torrentleech\.org/rss/download/[0-9]+/(?[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"/fetch/[a-z0-9]{32}/(?[a-z0-9]{32})", RegexOptions.Compiled), new (@"getnzb.*?(?<=\?|&)(r)=(?[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"\b(\w*)?(_?(?[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Trackers Announce Keys; Designed for Qbit Json; should work for all in theory new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?[a-z0-9]{16,})|(?[a-z0-9]{16,})(/|%2f)announce", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Path new (@"C:\\Users\\(?[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"/(home|Users)/(?[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Trackers Announce Keys; Designed for Qbit Json; should work for all in theory new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?[a-z0-9]{16,})|(?[a-z0-9]{16,})(/|%2f)announce"), // NzbGet new (@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Sabnzbd new (@"""[^""]*(username|password|api_?key|nzb_key)""\s*:\s*""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"""email_(account|to|from|pwd)""\s*:\s*""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), // uTorrent new (@"\[""[a-z._]*(username|password)"",\d,""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Deluge new (@"auth.login\(""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), // BroadcastheNet new (@"""?method""?\s*:\s*""(getTorrents)"",\s*""?params""?\s*:\s*\[\s*""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"getTorrents\(""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@"(?<=\?|&)(authkey|torrent_pass)=(?[^&=]+?)(?=""|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Plex new (@"(?<=\?|&)(X-Plex-Client-Identifier|X-Plex-Token)=(?[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Webhooks // Notifiarr new (@"api/v[0-9]/notification/lidarr/(?[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Indexer Responses new (@"avistaz\.[a-z]{2,3}\\\/rss\\\/download\\\/(?[^&=]+?)\\\/(?[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@",""info_hash"":""(?[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@",""pass[- _]?key"":""(?[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase), new (@",""rss[- _]?key"":""(?[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Discord new (@"discord.com/api/webhooks/((?[\w-]+)/)?(?[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase) }; private static readonly Regex CleanseRemoteIPRegex = new (@"(?:Auth-\w+(? { var value = m.Value; foreach (var capture in m.Groups["secret"].Captures.OfType().Reverse()) { value = value.Replace(capture.Index - m.Index, capture.Length, "(removed)"); } return value; }); } message = CleanseRemoteIPRegex.Replace(message, CleanseRemoteIP); return message; } private static string CleanseRemoteIP(Match match) { var group = match.Groups[1]; var valueIP = group.Value; if (IPAddress.TryParse(valueIP, out var address) && !address.IsLocalAddress()) { var prefix = match.Value.Substring(0, group.Index - match.Index); var postfix = match.Value.Substring(group.Index + group.Length - match.Index); var items = valueIP.Split('.'); return $"{prefix}{items[0]}.*.*.{items[3]}{postfix}"; } return match.Value; } } }