diff --git a/src/Jackett.Common/Extensions/StringExtensions.cs b/src/Jackett.Common/Extensions/StringExtensions.cs index b87aeb3f2..a430d3bc1 100644 --- a/src/Jackett.Common/Extensions/StringExtensions.cs +++ b/src/Jackett.Common/Extensions/StringExtensions.cs @@ -35,5 +35,13 @@ namespace Jackett.Common.Extensions return true; } + + public static string Replace(this string text, int index, int length, string replacement) + { + text = text.Remove(index, length); + text = text.Insert(index, replacement); + + return text; + } } } diff --git a/src/Jackett.Common/Services/LogCacheService.cs b/src/Jackett.Common/Services/LogCacheService.cs index 3625d7193..ff0c60376 100644 --- a/src/Jackett.Common/Services/LogCacheService.cs +++ b/src/Jackett.Common/Services/LogCacheService.cs @@ -2,43 +2,43 @@ using System.Collections.Generic; using System.Linq; using Jackett.Common.Models; using Jackett.Common.Services.Interfaces; +using Jackett.Common.Utils.Logging; using NLog; using NLog.Targets; namespace Jackett.Common.Services { - [Target("LogService")] public class LogCacheService : TargetWithLayout, ILogCacheService { - private static List logs = new List(); - - public void AddLog(LogEventInfo l) - { - lock (logs) - { - logs.Insert(0, new CachedLog() - { - Level = l.Level.Name, - Message = l.FormattedMessage, - When = l.TimeStamp - }); - logs = logs.Take(200).ToList(); - } - - } + private static List _Logs = new List(); public List Logs { get { - lock (logs) + lock (_Logs) { - return logs.ToList(); + return _Logs.ToList(); } } } protected override void Write(LogEventInfo logEvent) => AddLog(logEvent); + + private static void AddLog(LogEventInfo logEvent) + { + lock (_Logs) + { + _Logs.Insert(0, new CachedLog + { + Level = logEvent.Level.Name, + Message = CleanseLogMessage.Cleanse(logEvent.FormattedMessage), + When = logEvent.TimeStamp + }); + + _Logs = _Logs.Take(200).ToList(); + } + } } } diff --git a/src/Jackett.Common/Utils/Logging/CleanseFileTarget.cs b/src/Jackett.Common/Utils/Logging/CleanseFileTarget.cs new file mode 100644 index 000000000..74aec62e6 --- /dev/null +++ b/src/Jackett.Common/Utils/Logging/CleanseFileTarget.cs @@ -0,0 +1,15 @@ +using System.Text; +using NLog; +using NLog.Targets; + +namespace Jackett.Common.Utils.Logging +{ + public class CleanseFileTarget : FileTarget + { + protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) + { + var result = CleanseLogMessage.Cleanse(Layout.Render(logEvent)); + target.Append(result); + } + } +} diff --git a/src/Jackett.Common/Utils/Logging/CleanseLogMessage.cs b/src/Jackett.Common/Utils/Logging/CleanseLogMessage.cs new file mode 100644 index 000000000..4ece7e3ad --- /dev/null +++ b/src/Jackett.Common/Utils/Logging/CleanseLogMessage.cs @@ -0,0 +1,74 @@ +using System.Linq; +using System.Text.RegularExpressions; +using Jackett.Common.Extensions; + +namespace Jackett.Common.Utils.Logging +{ + // See https://github.com/Prowlarr/Prowlarr/blob/develop/src/NzbDrone.Common/Instrumentation/CleanseLogMessage.cs + public class CleanseLogMessage + { + private static readonly Regex[] _CleansingRules = + { + // Url + new Regex(@"(?<=[?&: ;])(apikey|api_key|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pid|pwd)=(?[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?<=[?& ;])[^=]*?(_?(?[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"rss\.torrentleech\.org/(?!rss)(?[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"rss\.torrentleech\.org/rss/download/[0-9]+/(?[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"/fetch/[a-z0-9]{32}/(?[a-z0-9]{32})", RegexOptions.Compiled), + new Regex(@"\b(\w*)?(_?(?[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?<=authkey = "")(?[^&=]+?)(?="")", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?<=beyond-hd\.[a-z]+/api/torrents/)(?[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?<=beyond-hd\.[a-z]+/torrent/download/[\w\d-]+[.]\d+[.])(?[a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + + // UNIT3D + new Regex(@"(?<=[a-z0-9-]+\.[a-z]+/torrent/download/\d+\.)(?[^&=][a-z0-9]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + + // Path + new Regex(@"""C:\\Users\\(?[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"""/(home|Users)/(?[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + + // uTorrent + new Regex(@"\[""[a-z._]*(username|password)"",\d,""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"\[""(boss_key|boss_key_salt|proxy\.proxy)"",\d,""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), + + // BroadcastheNet (;torrent_pass|torrents_notify_ is for MTV) + new Regex(@"""?method""?\s*:\s*""(getTorrents)"",\s*""?params""?\s*:\s*\[\s*""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"getTorrents\(""(?[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?<=\?|&|;|=)(authkey|torrent_pass|torrents_notify)[_=](?[^&=]+?)(?=""|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), + + // Indexer Responses + new Regex(@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}/rss/download/(?[^&=]+?)/(?[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"(?:animebytes)\.[a-z]{2,3}/torrent/[0-9]+/download/(?[^&=]+?)[""]", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@",""info_hash"":""(?[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@"""token"":""(?[^&=]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@",""pass[- _]?key"":""(?[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase), + new Regex(@",""rss[- _]?key"":""(?[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase), + }; + + public static string Cleanse(string message) + { + if (message.IsNullOrWhiteSpace()) + { + return message; + } + + foreach (var regex in _CleansingRules) + { + message = regex.Replace(message, m => + { + 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; + }); + } + + return message; + } + } +} diff --git a/src/Jackett.Common/Utils/LoggingSetup.cs b/src/Jackett.Common/Utils/LoggingSetup.cs index e028115c3..5e17cff24 100644 --- a/src/Jackett.Common/Utils/LoggingSetup.cs +++ b/src/Jackett.Common/Utils/LoggingSetup.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text; using Jackett.Common.Models.Config; using Jackett.Common.Services; +using Jackett.Common.Utils.Logging; using NLog; using NLog.Config; using NLog.LayoutRenderers; @@ -21,7 +22,7 @@ namespace Jackett.Common.Utils var logConfig = new LoggingConfiguration(); - var logFile = new FileTarget + var logFile = new CleanseFileTarget { Layout = "${longdate} ${level} ${message} ${onexception:inner=${newline}${newline}[v${assembly-version}] ${exception:format=ToString}${newline}${exception:format=Data}${newline}}", FileName = Path.Combine(settings.DataFolder, logFileName),