Skip to content

Commit 97ba8a5

Browse files
authored
Fix buffer overflow (#3)
* πŸ› MaybeEscapePattern: overflow * ⚑️ use ArrayPool<byte>.Shared * βœ… add tests suggested by Copilot 🀷 * πŸ‘· update actions, .NET version --------- Signed-off-by: Yves Bastide <[email protected]>
1 parent ec376c0 commit 97ba8a5

File tree

4 files changed

+27
-12
lines changed

4 files changed

+27
-12
lines changed

β€Ž.github/workflows/dotnet.ymlβ€Ž

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ jobs:
1515
runs-on: ubuntu-latest
1616

1717
steps:
18-
- uses: actions/checkout@v3
18+
- uses: actions/checkout@v6
1919
- name: Setup .NET
20-
uses: actions/setup-dotnet@v3
20+
uses: actions/setup-dotnet@v5
2121
with:
22-
dotnet-version: 9.0.x
22+
dotnet-version: 10
2323
- name: Restore dependencies
2424
run: dotnet restore
2525
- name: Build

β€ŽRobotsTxt/RobotsTxtParser.csβ€Ž

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Buffers;
2+
13
namespace RobotsTxt;
24

35
public class RobotsTxtParser(byte[] robotsBody, IRobotsParseHandler handler)
@@ -74,8 +76,9 @@ private void ParseAndEmitLine(int currentLine, ReadOnlySpan<byte> line)
7476
key.Parse(stringKey);
7577
if (NeedEscapeValueForKey(key))
7678
{
77-
var escapedValue = MaybeEscapePattern(value);
79+
var escapedValue = MaybeEscapePattern(value, out var dst);
7880
EmitKeyValueToHandler(currentLine, key, escapedValue);
81+
if (dst != null) ArrayPool<byte>.Shared.Return(dst);
7982
}
8083
else
8184
{
@@ -107,7 +110,7 @@ private void EmitKeyValueToHandler(int currentLine, ParsedRobotsKey key, ReadOnl
107110
}
108111
}
109112

110-
public static ReadOnlySpan<byte> MaybeEscapePattern(ReadOnlySpan<byte> src)
113+
public static ReadOnlySpan<byte> MaybeEscapePattern(ReadOnlySpan<byte> src, out byte[]? dst)
111114
{
112115
var numToEscape = 0;
113116
var needCapitalize = false;
@@ -131,19 +134,22 @@ public static ReadOnlySpan<byte> MaybeEscapePattern(ReadOnlySpan<byte> src)
131134

132135
if (numToEscape == 0 && !needCapitalize)
133136
{
137+
dst = null;
134138
return src;
135139
}
136140

137-
var dst = new byte[numToEscape * 2 + src.Length];
141+
dst = ArrayPool<byte>.Shared.Rent(numToEscape * 2 + src.Length);
142+
138143
var j = 0;
139144
for (var i = 0; i < src.Length; i++)
140145
{
141146
var c = src[i];
142147
if (c == '%' && i + 2 < src.Length && src[i + 1].IsXDigit() && src[i + 2].IsXDigit())
143148
{
144-
dst[j++] = src[i++];
145-
dst[j++] = src[i++].ToUpper();
146-
dst[j++] = src[i++].ToUpper();
149+
dst[j++] = (byte)'%';
150+
dst[j++] = src[i + 1].ToUpper();
151+
dst[j++] = src[i + 2].ToUpper();
152+
i += 2;
147153
}
148154
else if (c >= 0x80)
149155
{
@@ -157,7 +163,7 @@ public static ReadOnlySpan<byte> MaybeEscapePattern(ReadOnlySpan<byte> src)
157163
}
158164
}
159165

160-
return dst;
166+
return dst.AsSpan(0, j);
161167
}
162168

163169
private static bool NeedEscapeValueForKey(ParsedRobotsKey key)

β€ŽTestRobotsTxt/GoogleTests.csβ€Ž

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Buffers;
12
using System.Diagnostics;
23
using System.Text;
34
using RobotsTxt;
@@ -1011,11 +1012,15 @@ public void TestGetPathParamsQuery(string url, string expected)
10111012
[InlineData("/a/b/c", "/a/b/c")]
10121013
[InlineData("Γ‘", "%C3%A1")]
10131014
[InlineData("%aa", "%AA")]
1015+
[InlineData("%ab%c", "%AB%c")]
1016+
[InlineData("test%", "test%")]
1017+
[InlineData("%a", "%a")]
10141018
public void TestMaybeEscapePattern(string url, string expected)
10151019
{
10161020
var actual =
1017-
Encoding.ASCII.GetString(RobotsTxtParser.MaybeEscapePattern(Encoding.UTF8.GetBytes(url)).ToArray());
1021+
Encoding.ASCII.GetString(RobotsTxtParser.MaybeEscapePattern(Encoding.UTF8.GetBytes(url), out var dst).ToArray());
10181022
Assert.Equal(expected, actual);
1023+
if (dst != null) ArrayPool<byte>.Shared.Return(dst);
10191024
}
10201025
}
10211026
}

β€ŽTestRobotsTxt/TestRobotsTxtParser.csβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
using System.Buffers;
12
using System.Text;
3+
24
using RobotsTxt;
5+
36
using Xunit;
47

58
namespace TestRobotsTxt
@@ -56,8 +59,9 @@ public void TestGetKeyAndValueFrom(string line, bool rc, string expectedKey, str
5659
[InlineData("Γ©", "%C3%A9")]
5760
public void TestMaybeEscapePattern(string src, string expected)
5861
{
59-
var actual = RobotsTxtParser.MaybeEscapePattern(Encoding.UTF8.GetBytes(src));
62+
var actual = RobotsTxtParser.MaybeEscapePattern(Encoding.UTF8.GetBytes(src), out var dst);
6063
Assert.Equal(expected, Encoding.UTF8.GetString(actual.ToArray()));
64+
if (dst != null) ArrayPool<byte>.Shared.Return(dst);
6165
}
6266
}
6367
}

0 commit comments

Comments
Β (0)