Skip to content

Commit 9c51e1c

Browse files
committed
More robust handling of old and new delimiter
1 parent 2848c37 commit 9c51e1c

File tree

2 files changed

+104
-12
lines changed

2 files changed

+104
-12
lines changed

IPBanCore/Core/IPBan/IPBanFilter.cs

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ public class IPBanFilter : IIPBanFilter
4242
/// </summary>
4343
public const char ItemDelimiter = ',';
4444

45-
/// <summary>
46-
/// Item pieces delimiters including the legacy ? and the current |
47-
/// </summary>
48-
public static readonly char[] ItemPiecesDelimitersLegacy = ['?', '|'];
49-
5045
/// <summary>
5146
/// Item pieces delimiter |
5247
/// </summary>
@@ -57,6 +52,11 @@ public class IPBanFilter : IIPBanFilter
5752
/// </summary>
5853
public const char SubEntryDelimiter = ';';
5954

55+
/// <summary>
56+
/// Legacy item pieces delimiter ?
57+
/// </summary>
58+
private const char itemPiecesDelimiterLegacy = '?';
59+
6060
private static readonly HashSet<string> ignoreListEntries =
6161
[
6262
"0.0.0.0",
@@ -146,13 +146,7 @@ public IPBanFilter(string value, string regexValue, IHttpRequestMaker httpReques
146146
// | can be used as a sub delimiter instead of ? mark
147147
foreach (string entry in value.Split(ItemDelimiter, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
148148
{
149-
string entryWithoutComment = entry;
150-
int pos = entryWithoutComment.IndexOfAny(ItemPiecesDelimitersLegacy);
151-
if (pos >= 0)
152-
{
153-
entryWithoutComment = entryWithoutComment[..pos];
154-
}
155-
entryWithoutComment = entryWithoutComment.Trim();
149+
string entryWithoutComment = GetEntryWithoutComment(entry);
156150

157151
// sub entries (multiple ip addresses) are delimited by semi-colon
158152
foreach (string subEntry in entryWithoutComment.Split(SubEntryDelimiter, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
@@ -338,6 +332,63 @@ public bool IsFiltered(IPAddressRange range)
338332
return false;
339333
}
340334

335+
/// <summary>
336+
/// Get entry without comment
337+
/// </summary>
338+
/// <param name="entry">Entry</param>
339+
/// <returns>Entry without comment</returns>
340+
public static string GetEntryWithoutComment(string entry)
341+
{
342+
string entryWithoutComment = entry;
343+
int pos = entryWithoutComment.IndexOf(ItemPiecesDelimiter);
344+
345+
// if using new delimiter, remove it and everything after
346+
if (pos >= 0)
347+
{
348+
entryWithoutComment = entryWithoutComment[..pos];
349+
}
350+
// if using two or more of old delimiter, remove it and everything after
351+
else if (entryWithoutComment.Count(e => e == itemPiecesDelimiterLegacy) > 1)
352+
{
353+
pos = entryWithoutComment.IndexOf(itemPiecesDelimiterLegacy);
354+
entryWithoutComment = entryWithoutComment[..pos];
355+
}
356+
entryWithoutComment = entryWithoutComment.Trim();
357+
358+
return entryWithoutComment;
359+
}
360+
361+
/// <summary>
362+
/// Split entry on new delimiter. If it doesn't exist, legacy delimiter is used.
363+
/// </summary>
364+
/// <param name="entry">Entry</param>
365+
/// <returns>Pieces (always at least 3 items)</returns>
366+
public static string[] SplitEntry(string entry)
367+
{
368+
entry ??= string.Empty;
369+
string[] pieces = [];
370+
371+
// split on new delimiter if we have it
372+
if (entry.Contains(ItemPiecesDelimiter))
373+
{
374+
pieces = entry.Split(ItemPiecesDelimiter, StringSplitOptions.TrimEntries);
375+
}
376+
377+
// split on legacy delimiter if there's two or more
378+
else if (entry.Count(e => e == itemPiecesDelimiterLegacy) > 1)
379+
{
380+
pieces = entry.Split(itemPiecesDelimiterLegacy, StringSplitOptions.TrimEntries);
381+
}
382+
383+
// the split should have at least 3 pieces, if not, return the original entry and two empty ones
384+
if (pieces.Length < 3)
385+
{
386+
return [entry, string.Empty, string.Empty];
387+
}
388+
389+
return pieces;
390+
}
391+
341392
/// <summary>
342393
/// Get all ip address ranges in the filter
343394
/// </summary>

IPBanTests/IPBanFilterTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using DigitalRuby.IPBanCore;
2+
3+
using NUnit.Framework;
4+
5+
namespace DigitalRuby.IPBanTests
6+
{
7+
/// <summary>
8+
/// IPBan filter tests
9+
/// </summary>
10+
[TestFixture]
11+
public sealed class IPBanFilterTests
12+
{
13+
/// <summary>
14+
/// Ensure we can get entry without comment using old and new delimiter properly
15+
/// </summary>
16+
[TestCase("1.2.3.4?2022-01-01T01:01:01Z?notes", "1.2.3.4")]
17+
[TestCase("1.2.3.4|2022-01-01T01:01:01Z|notes", "1.2.3.4")]
18+
[TestCase("https://fakeurl.com/?t=123", "https://fakeurl.com/?t=123")]
19+
[TestCase("https://fakeurl.com/?t=123|2022-01-01T01:01:01Z|notes", "https://fakeurl.com/?t=123")]
20+
public void TestGetEntryWithoutComment(string entry, string expectedResult)
21+
{
22+
string result = IPBanFilter.GetEntryWithoutComment(entry);
23+
Assert.That(result, Is.EqualTo(expectedResult));
24+
}
25+
26+
/// <summary>
27+
/// Ensure we can split using old and new delimiters properly
28+
/// </summary>
29+
/// <param name="entry">Entry</param>
30+
/// <param name="expectedResult">Expected result (3 items, | separated)</param>
31+
[TestCase("1.2.3.4?2022-01-01T01:01:01Z?notes", "1.2.3.4|2022-01-01T01:01:01Z|notes")]
32+
[TestCase("1.2.3.4|2022-01-01T01:01:01Z|notes", "1.2.3.4|2022-01-01T01:01:01Z|notes")]
33+
[TestCase("https://fakeurl.com/?t=123", "https://fakeurl.com/?t=123||")]
34+
[TestCase("https://fakeurl.com/?t=123|2022-01-01T01:01:01Z|notes", "https://fakeurl.com/?t=123|2022-01-01T01:01:01Z|notes")]
35+
public void TestSplitEntry(string entry, string expectedResult)
36+
{
37+
string[] result = IPBanFilter.SplitEntry(entry);
38+
Assert.That(string.Join('|', result), Is.EqualTo(expectedResult));
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)