namespace Agent.Windows.Security; [Flags] public enum FileCapability { None = 0, Read = 1, Write = 2, Execute = 4, Delete = 8 } public sealed record FilePolicyEntry(string Path, FileCapability Capabilities); public sealed class PathPolicy(IEnumerable entries) { private readonly IReadOnlyList _entries = entries.Select(entry => entry with { Path = CanonicalPath.Resolve(entry.Path, requireExisting: false) }).ToArray(); public bool IsAllowed(string path, FileCapability required) { string canonical = CanonicalPath.Resolve(path, requireExisting: required is FileCapability.Read or FileCapability.Execute or FileCapability.Delete); return _entries.Any(entry => IsAtOrBelow(canonical, entry.Path) && (entry.Capabilities & required) == required); } public void Demand(string path, FileCapability required) { if (!IsAllowed(path, required)) throw new SandboxPolicyException("policy.deny", $"'{path}' is not approved for {required}."); } private static bool IsAtOrBelow(string candidate, string root) => candidate.Equals(root, StringComparison.OrdinalIgnoreCase) || candidate.StartsWith(root.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase); } public static class CanonicalPath { public static string Resolve(string path, bool requireExisting = true) { if (string.IsNullOrWhiteSpace(path)) throw new SandboxPolicyException("path.invalid", "Path is required."); string fullPath = Path.GetFullPath(path); string? root = Path.GetPathRoot(fullPath); if (string.IsNullOrEmpty(root)) throw new SandboxPolicyException("path.invalid", "Path has no filesystem root."); string current = root; foreach (string segment in fullPath[root.Length..].Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries)) { current = Path.Combine(current, segment); if (!File.Exists(current) && !Directory.Exists(current)) continue; if ((File.GetAttributes(current) & FileAttributes.ReparsePoint) != 0) throw new SandboxPolicyException("path.reparse", $"Reparse points are not allowed: '{current}'."); } if (requireExisting && !File.Exists(fullPath) && !Directory.Exists(fullPath)) throw new FileNotFoundException("Approved path does not exist.", fullPath); return Path.TrimEndingDirectorySeparator(fullPath); } } public sealed class SandboxPolicyException(string code, string message) : UnauthorizedAccessException(message) { public string Code { get; } = code; }