202 lines
7.4 KiB
C#
202 lines
7.4 KiB
C#
using Wiki.Core.Parsing;
|
|
|
|
namespace Wiki.Core.Tests;
|
|
|
|
public class LeakScannerTests
|
|
{
|
|
[Theory]
|
|
[InlineData("ipv4", "10.2.40.1", "Der Server 10.2.40.1 antwortet")]
|
|
[InlineData("ipv6", "fd00::1", "fd00::1")]
|
|
[InlineData("mac", "aa:bb:cc:dd:ee:ff", "aa:bb:cc:dd:ee:ff")]
|
|
[InlineData("fqdn", "wiki.renner.uno", "wiki.renner.uno")]
|
|
[InlineData("fqdn", "ak-outpost.authentik.svc.cluster.local", "ak-outpost.authentik.svc.cluster.local")]
|
|
[InlineData("entity-id", "light.eg_kuche_ambiente", "light.eg_kuche_ambiente")]
|
|
[InlineData("path", "/var/run/secrets/x", "/var/run/secrets/x")]
|
|
[InlineData("secret-path", "apps/ai/wiki-anthropic", "apps/ai/wiki-anthropic")]
|
|
public void Scan_FindsRule_WhenValueIsInSources(string rule, string value, string text)
|
|
{
|
|
var matches = LeakScanner.Scan(text, [value]);
|
|
Assert.Contains(matches, m => m.Rule == rule && m.BlockIndex == 0);
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_InlineCodeSpan_IsScanned()
|
|
{
|
|
var matches = LeakScanner.Scan("Host `nas-1.lan` ist da", ["nas-1.lan"]);
|
|
Assert.Contains(matches, m => m.Rule == "fqdn");
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_IgnoresPrivateBlocks()
|
|
{
|
|
var matches = LeakScanner.Scan("::: private\nDer Server 10.2.40.1 antwortet\n:::", ["10.2.40.1"]);
|
|
Assert.Empty(matches);
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_GenericExampleNotInSources_NoMatch()
|
|
{
|
|
var matches = LeakScanner.Scan("Eine Entität heißt zum Beispiel light.wohnzimmer", ["light.eg_kuche_ambiente"]);
|
|
Assert.Empty(matches);
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_MatchIsCaseInsensitive()
|
|
{
|
|
var matches = LeakScanner.Scan("LIGHT.EG_KUCHE_AMBIENTE", ["light.eg_kuche_ambiente"]);
|
|
Assert.Contains(matches, m => m.Rule == "entity-id");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("192.0.2.10")]
|
|
[InlineData("198.51.100.7")]
|
|
[InlineData("203.0.113.9")]
|
|
[InlineData("2001:db8::1")]
|
|
[InlineData("nas.example.com")]
|
|
[InlineData("a.b.example")]
|
|
public void Scan_ReservedExample_NoMatch(string value)
|
|
{
|
|
Assert.Empty(LeakScanner.Scan($"Beispiel {value} hier", [value]));
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_RennerUno_AlwaysMatches()
|
|
{
|
|
var matches = LeakScanner.Scan("gitea.renner.uno", []);
|
|
Assert.Contains(matches, m => m.Rule == "fqdn");
|
|
Assert.Contains(LeakScanner.Scan("Domain renner.uno", []), m => m.Rule == "fqdn");
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_NoFalsePositive_OnPlainProse()
|
|
{
|
|
var text = "Die Wärmepumpe heizt den Estrich. Version 2.5 ist aktuell.";
|
|
Assert.Empty(LeakScanner.Scan(text, [text, "2.5"]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Mail an claude-bot@renner.uno senden")]
|
|
[InlineData("Remote git@gitea.renner.uno:konairius/homelab.git")]
|
|
[InlineData("Zertifikat für *.renner.uno")]
|
|
[InlineData("Record _acme-challenge.renner.uno")]
|
|
[InlineData("SRV _ldap._tcp.renner.uno")]
|
|
public void Scan_RennerUnoForms_Match(string text)
|
|
{
|
|
Assert.Contains(LeakScanner.Scan(text, []), m => m.Rule == "fqdn");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Login user@host.lan geht", "host.lan")]
|
|
[InlineData("Dienst *.foo.svc.cluster.local", "foo.svc.cluster.local")]
|
|
public void Scan_HostSuffixForms_Match(string text, string source)
|
|
{
|
|
Assert.Contains(LeakScanner.Scan(text, [source]), m => m.Rule == "fqdn");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Daten liegen unter /var/lib/rancher.", "path")]
|
|
[InlineData("Siehe apps/ai/wiki-anthropic.", "secret-path")]
|
|
[InlineData("Pfad (/var/lib/rancher), ok", "path")]
|
|
public void Scan_TrailingPunctuation_IsTrimmed(string text, string rule)
|
|
{
|
|
var source = rule == "path" ? "/var/lib/rancher" : "apps/ai/wiki-anthropic";
|
|
Assert.Contains(LeakScanner.Scan(text, [source]), m => m.Rule == rule);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("mac", "aa-bb-cc-dd-ee-ff", "AA:BB:CC:DD:EE:FF")]
|
|
[InlineData("mac", "aabbccddeeff", "AA:BB:CC:DD:EE:FF")]
|
|
[InlineData("mac", "aabb.ccdd.eeff", "AA:BB:CC:DD:EE:FF")]
|
|
[InlineData("mac", "MAC:aa:bb:cc:dd:ee:ff", "aabbccddeeff")]
|
|
[InlineData("ipv6", "fd00::1", "fd00:0:0:0::1")]
|
|
[InlineData("ipv6", "fd00::1", "fd00::0001")]
|
|
[InlineData("ipv4", "10.2.40.1", "gateway 10.2.40.1.")]
|
|
public void Scan_ReformattedAddress_MatchesNormalizedSource(string rule, string text, string source)
|
|
{
|
|
Assert.Contains(LeakScanner.Scan(text, [source]), m => m.Rule == rule);
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_Ipv4_IsNotPrefixMatchedAgainstSources()
|
|
{
|
|
Assert.Empty(LeakScanner.Scan("Server 10.2.40.1", ["10.2.40.100"]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("path", "nas-1:/volume1/kubernetes", "/volume1/kubernetes")]
|
|
[InlineData("path", "Aus ../secrets/db laden", "../secrets/db")]
|
|
[InlineData("path", "smb://nas-1/share", "smb://nas-1/share")]
|
|
[InlineData("entity-id", "valve.heizkreis_og", "valve.heizkreis_og")]
|
|
[InlineData("entity-id", "weather.home", "weather.home")]
|
|
[InlineData("entity-id", "timer.waschmaschine", "timer.waschmaschine")]
|
|
[InlineData("entity-id", "light_group.eg", "light_group.eg")]
|
|
public void Scan_ExtendedForms_Match(string rule, string text, string source)
|
|
{
|
|
Assert.Contains(LeakScanner.Scan(text, [source]), m => m.Rule == rule);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("127.0.0.1")]
|
|
[InlineData("0.0.0.0")]
|
|
[InlineData("255.255.255.0")]
|
|
[InlineData("255.255.0.0")]
|
|
[InlineData("255.0.0.0")]
|
|
[InlineData("::1")]
|
|
public void Scan_MoreReservedValues_NoMatch(string value)
|
|
{
|
|
Assert.Empty(LeakScanner.Scan($"Adresse {value} hier", [value]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Netz 10.2.40.x", "Router 10.2.40.1")]
|
|
[InlineData("Netz 10.2.40.X", "Router 10.2.40.1")]
|
|
[InlineData("Netz 10.2.40.*", "Router 10.2.40.1")]
|
|
[InlineData("Netz 10.2.40.0/24", "Router 10.2.40.1")]
|
|
[InlineData("Das Netz 10.2.40. ist intern", "Router 10.2.40.1")]
|
|
[InlineData("Das Netz ist 10.2.40.", "Router 10.2.40.1")]
|
|
[InlineData("Netz 10.2.x", "Router 10.2.40.1")]
|
|
[InlineData("Netz 10.2.40.x", "Bereich 10.2.40.x")]
|
|
[InlineData("Netz 10.002.40.x", "Router 10.2.40.1")]
|
|
public void Scan_Ipv4Prefix_MatchesWhenInSources(string text, string source)
|
|
{
|
|
Assert.Contains(LeakScanner.Scan(text, [source]), m => m.Rule == "ipv4-prefix");
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_Ipv4Prefix_NotInSources_NoMatch()
|
|
{
|
|
Assert.Empty(LeakScanner.Scan("Netz 10.2.41.x", ["Router 10.2.40.1"]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("192.0.2.x", "192.0.2.1")]
|
|
[InlineData("198.51.100.0/24", "198.51.100.1")]
|
|
[InlineData("203.0.113.*", "203.0.113.1")]
|
|
[InlineData("127.0.0.", "127.0.0.1")]
|
|
public void Scan_Ipv4Prefix_Reserved_NoMatch(string text, string source)
|
|
{
|
|
Assert.Empty(LeakScanner.Scan($"Netz {text} hier", [source]));
|
|
}
|
|
|
|
[Fact]
|
|
public void Scan_VersionWildcard_NoMatch()
|
|
{
|
|
const string text = "Version 2.5.x ist aktuell.";
|
|
Assert.Empty(LeakScanner.Scan(text, [text]));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrivatizeLeaks_MovesOnlyLeakingBlock()
|
|
{
|
|
var body = "Allgemeiner Text.\n\nDer Server 10.2.40.1 antwortet.\n\nNoch ein Absatz.";
|
|
var result = LeakScanner.PrivatizeLeaks(body, ["10.2.40.1"]);
|
|
Assert.Equal("Allgemeiner Text.\n\n::: private\nDer Server 10.2.40.1 antwortet.\n:::\n\nNoch ein Absatz.", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrivatizeLeaks_NoMatch_ReturnsBodyUnchanged()
|
|
{
|
|
Assert.Equal("Nur Text.", LeakScanner.PrivatizeLeaks("Nur Text.", []));
|
|
}
|
|
}
|