36 lines
1.3 KiB
C#
36 lines
1.3 KiB
C#
namespace Nexus.Api.Helpers;
|
|
|
|
public static class PathSecurityHelper
|
|
{
|
|
/// <summary>Validates a path against directory traversal and resolves a safe absolute path.</summary>
|
|
public static bool TryResolveSafePath(string basePath, string userInput, out string? safePath)
|
|
{
|
|
safePath = null;
|
|
|
|
// URL-decode to catch encoded attacks like %2F, %2e%2e, %00
|
|
var decoded = Uri.UnescapeDataString(userInput);
|
|
|
|
// Reject null bytes
|
|
if (decoded.Contains('\0')) return false;
|
|
|
|
// Combine with base and resolve to canonical form
|
|
var combined = Path.Combine(basePath, decoded);
|
|
var full = Path.GetFullPath(combined);
|
|
var canonicalBase = Path.GetFullPath(basePath);
|
|
|
|
// Must stay within the allowed base directory
|
|
if (!full.StartsWith(canonicalBase + Path.DirectorySeparatorChar) && full != canonicalBase)
|
|
return false;
|
|
|
|
safePath = full;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Validates config filename against path-traversal; must be alphanumeric .md or .json.</summary>
|
|
public static bool IsValidConfigFileName(string fileName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fileName)) return false;
|
|
return System.Text.RegularExpressions.Regex.IsMatch(fileName, @"^[a-zA-Z0-9._-]+\.(md|json)$");
|
|
}
|
|
}
|