monograph: add support for webrisk api for analyzing urls for pro users

This commit is contained in:
Abdullah Atta
2025-09-15 11:22:37 +05:00
parent 0f43b3ee66
commit 97fbd3226d
6 changed files with 72 additions and 1 deletions

View File

@@ -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 = "<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'><circle r='9'/></svg>";
private Repository<Monograph> Monographs { get; set; }
private readonly IURLAnalyzer urlAnalyzer;
private readonly IUnitOfWork unit;
private const int MAX_DOC_SIZE = 15 * 1024 * 1024;
public MonographsController(Repository<Monograph> monographs, IUnitOfWork unitOfWork)
public MonographsController(Repository<Monograph> monographs, IUnitOfWork unitOfWork, IURLAnalyzer analyzer)
{
Monographs = monographs;
unit = unitOfWork;
urlAnalyzer = analyzer;
}
private static FilterDefinition<Monograph> 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;
}
}

View File

@@ -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<ISyncItemsRepositoryAccessor, SyncItemsRepositoryAccessor>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<IS3Service, S3Service>();
services.AddScoped<IURLAnalyzer, URLAnalyzer>();
services.AddControllers();

View File

@@ -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");

View File

@@ -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<bool> IsURLSafeAsync(string uri);
}
}

View File

@@ -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<bool> 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<WebRiskAPIResponse>();
return json.Threat.ThreatTypes == null || json.Threat.ThreatTypes.Length == 0;
}
void IDisposable.Dispose()
{
httpClient.Dispose();
}
}
}

View File

@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>