From 97fbd3226d322cc20c6430ff629759be0947ad84 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Mon, 15 Sep 2025 11:22:37 +0500 Subject: [PATCH] monograph: add support for webrisk api for analyzing urls for pro users --- .../Controllers/MonographsController.cs | 19 +++++++++- Notesnook.API/Startup.cs | 3 ++ Streetwriters.Common/Constants.cs | 1 + .../Interfaces/IURLAnalyzer.cs | 13 +++++++ Streetwriters.Common/Services/URLAnalyzer.cs | 36 +++++++++++++++++++ .../Streetwriters.Common.csproj | 1 + 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Streetwriters.Common/Interfaces/IURLAnalyzer.cs create mode 100644 Streetwriters.Common/Services/URLAnalyzer.cs diff --git a/Notesnook.API/Controllers/MonographsController.cs b/Notesnook.API/Controllers/MonographsController.cs index 70be5ab..8fc3708 100644 --- a/Notesnook.API/Controllers/MonographsController.cs +++ b/Notesnook.API/Controllers/MonographsController.cs @@ -33,6 +33,7 @@ using Notesnook.API.Authorization; using Notesnook.API.Models; using Notesnook.API.Services; using Streetwriters.Common; +using Streetwriters.Common.Interfaces; using Streetwriters.Common.Messages; using Streetwriters.Data.Interfaces; using Streetwriters.Data.Repositories; @@ -46,12 +47,14 @@ namespace Notesnook.API.Controllers { const string SVG_PIXEL = ""; private Repository Monographs { get; set; } + private readonly IURLAnalyzer urlAnalyzer; private readonly IUnitOfWork unit; private const int MAX_DOC_SIZE = 15 * 1024 * 1024; - public MonographsController(Repository monographs, IUnitOfWork unitOfWork) + public MonographsController(Repository monographs, IUnitOfWork unitOfWork, IURLAnalyzer analyzer) { Monographs = monographs; unit = unitOfWork; + urlAnalyzer = analyzer; } private static FilterDefinition CreateMonographFilter(string userId, Monograph monograph) @@ -338,6 +341,20 @@ namespace Notesnook.API.Controllers } return document.ToHtml(); } + + if (ProUserRequirement.IsUserPro(User)) + { + var config = Configuration.Default.WithDefaultLoader(); + var context = BrowsingContext.New(config); + var document = await context.OpenAsync(r => r.Content(content)); + foreach (var element in document.QuerySelectorAll("a")) + { + var href = element.GetAttribute("href"); + if (string.IsNullOrEmpty(href)) continue; + if (!await urlAnalyzer.IsURLSafeAsync(href)) element.RemoveAttribute("href"); + } + return document.ToHtml(); + } return content; } } diff --git a/Notesnook.API/Startup.cs b/Notesnook.API/Startup.cs index e560f0d..ff7d37d 100644 --- a/Notesnook.API/Startup.cs +++ b/Notesnook.API/Startup.cs @@ -57,8 +57,10 @@ using OpenTelemetry.Resources; using Quartz; using Streetwriters.Common; using Streetwriters.Common.Extensions; +using Streetwriters.Common.Interfaces; using Streetwriters.Common.Messages; using Streetwriters.Common.Models; +using Streetwriters.Common.Services; using Streetwriters.Data; using Streetwriters.Data.DbContexts; using Streetwriters.Data.Interfaces; @@ -185,6 +187,7 @@ namespace Notesnook.API services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddControllers(); diff --git a/Streetwriters.Common/Constants.cs b/Streetwriters.Common/Constants.cs index 3631a54..2f87be0 100644 --- a/Streetwriters.Common/Constants.cs +++ b/Streetwriters.Common/Constants.cs @@ -70,6 +70,7 @@ namespace Streetwriters.Common public static string SSE_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SSE_CERT_KEY_PATH"); // internal + public static string WEBRISK_API_URI => Environment.GetEnvironmentVariable("WEBRISK_API_URI"); public static string MONGODB_CONNECTION_STRING => Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING"); public static string MONGODB_DATABASE_NAME => Environment.GetEnvironmentVariable("MONGODB_DATABASE_NAME"); public static int SUBSCRIPTIONS_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_PORT") ?? "80"); diff --git a/Streetwriters.Common/Interfaces/IURLAnalyzer.cs b/Streetwriters.Common/Interfaces/IURLAnalyzer.cs new file mode 100644 index 0000000..5c39663 --- /dev/null +++ b/Streetwriters.Common/Interfaces/IURLAnalyzer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using MimeKit; +using MimeKit.Cryptography; +using Streetwriters.Common.Models; + +namespace Streetwriters.Common.Interfaces +{ + public interface IURLAnalyzer + { + Task IsURLSafeAsync(string uri); + } +} diff --git a/Streetwriters.Common/Services/URLAnalyzer.cs b/Streetwriters.Common/Services/URLAnalyzer.cs new file mode 100644 index 0000000..496d958 --- /dev/null +++ b/Streetwriters.Common/Services/URLAnalyzer.cs @@ -0,0 +1,36 @@ +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; +using Streetwriters.Common.Interfaces; + +namespace Streetwriters.Common.Services +{ + struct Threat + { + public string[]? ThreatTypes { get; set; } + } + struct WebRiskAPIResponse + { + public Threat Threat { get; set; } + } + + public class URLAnalyzer : IURLAnalyzer, IDisposable + { + private readonly HttpClient httpClient = new(); + + public async Task IsURLSafeAsync(string uri) + { + if (string.IsNullOrEmpty(Constants.WEBRISK_API_URI)) return true; + var response = await httpClient.PostAsJsonAsync(Constants.WEBRISK_API_URI, new { uri }); + if (!response.IsSuccessStatusCode) return true; + var json = await response.Content.ReadFromJsonAsync(); + return json.Threat.ThreatTypes == null || json.Threat.ThreatTypes.Length == 0; + } + + void IDisposable.Dispose() + { + httpClient.Dispose(); + } + } +} diff --git a/Streetwriters.Common/Streetwriters.Common.csproj b/Streetwriters.Common/Streetwriters.Common.csproj index 0399971..78aa4d5 100644 --- a/Streetwriters.Common/Streetwriters.Common.csproj +++ b/Streetwriters.Common/Streetwriters.Common.csproj @@ -2,6 +2,7 @@ net8.0 + enable