Skip to content

Commit 4e500cb

Browse files
committed
Add entry name protection feature
1 parent f3e4e10 commit 4e500cb

File tree

1 file changed

+46
-1
lines changed

1 file changed

+46
-1
lines changed

src/EasySign/Bundle.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Text;
1111
using System.Text.Json;
1212
using System.Text.Json.Serialization;
13+
using System.Text.RegularExpressions;
1314
using System.Threading.Tasks;
1415

1516
using EnsureThat;
@@ -48,6 +49,19 @@ public class Bundle
4849
/// </summary>
4950
protected ILogger Logger { get; }
5051

52+
/// <summary>
53+
/// Gets the list of sensitive names. Regex patterns are supported.
54+
/// </summary>
55+
/// <remarks>
56+
/// These names are not allowed for add or delete.
57+
/// The entries with these names are only resolved with <see cref="ReadSource.Bundle"/>.
58+
/// </remarks>
59+
protected List<string> ProtectedNames { get; } = new()
60+
{
61+
".manifest.ec",
62+
".signatures.ec",
63+
};
64+
5165
/// <summary>
5266
/// Gets the default name of the bundle.
5367
/// </summary>
@@ -140,6 +154,26 @@ protected void EnsureWritable()
140154
}
141155
}
142156

157+
/// <summary>
158+
/// Checks whether the entry name is protected and throws an exception if it is.
159+
/// </summary>
160+
/// <param name="entryName">The name of the entry to check.</param>
161+
/// <param name="throwException">Whether to throw an exception if the entry name is protected.</param>
162+
/// <exception cref="UnauthorizedAccessException"></exception>
163+
/// <returns>True if the entry name is not protected; otherwise, false.</returns>
164+
protected bool CheckEntryNameSecurity(string entryName, bool throwException = true)
165+
{
166+
foreach (var pattern in ProtectedNames)
167+
{
168+
if (Regex.IsMatch(entryName, pattern))
169+
{
170+
return throwException ? throw new UnauthorizedAccessException("Entry name is protected: " + entryName) : false;
171+
}
172+
}
173+
174+
return true;
175+
}
176+
143177
private void EvictIfNecessary(long incomingFileSize)
144178
{
145179
while (_currentCacheSize + incomingFileSize > _maxCacheSize && !_cache.IsEmpty)
@@ -317,14 +351,17 @@ public void AddEntry(string path, string destinationPath = "./", string? rootPat
317351

318352
using var file = File.OpenRead(path);
319353
string name = Manifest.GetNormalizedEntryName(Path.GetRelativePath(rootPath, path));
354+
355+
CheckEntryNameSecurity(name);
356+
320357
var hash = ComputeSHA512Hash(file);
321358

322359
if (Manifest.StoreOriginalFiles && !string.IsNullOrEmpty(destinationPath) && destinationPath != "./")
323360
{
324361
name = destinationPath + name;
325362
}
326363

327-
Logger.LogDebug("Adding entry: {name} with hash {hash} to manifest", name, string.Format("X2", hash));
364+
Logger.LogDebug("Adding entry: {name} with hash {hash} to manifest", name, BitConverter.ToString(hash).Replace("-", string.Empty));
328365
Manifest.AddEntry(name, hash);
329366

330367
if (Manifest.StoreOriginalFiles)
@@ -346,6 +383,9 @@ public void DeleteEntry(string entryName)
346383

347384
Logger.LogInformation("Deleting entry: {name}", entryName);
348385

386+
CheckEntryNameSecurity(entryName);
387+
388+
Logger.LogDebug("Deleting entry: {name} from manifest", entryName);
349389
Manifest.DeleteEntry(entryName);
350390

351391
if (Manifest.StoreOriginalFiles)
@@ -552,6 +592,11 @@ public Stream GetStream(string entryName, ReadSource readSource = ReadSource.Bot
552592

553593
Stream stream;
554594

595+
if (!CheckEntryNameSecurity(entryName, false))
596+
{
597+
readSource = ReadSource.Bundle;
598+
}
599+
555600
if (readSource != ReadSource.Disk && (readSource == ReadSource.Bundle || Manifest.StoreOriginalFiles))
556601
{
557602
Logger.LogDebug("Reading file: {name} from the bundle", entryName);

0 commit comments

Comments
 (0)