summaryrefslogtreecommitdiff
path: root/src/Agent.Windows/Security
diff options
context:
space:
mode:
Diffstat (limited to 'src/Agent.Windows/Security')
-rw-r--r--src/Agent.Windows/Security/ExecutablePolicy.cs6
-rw-r--r--src/Agent.Windows/Security/PathPolicy.cs13
2 files changed, 13 insertions, 6 deletions
diff --git a/src/Agent.Windows/Security/ExecutablePolicy.cs b/src/Agent.Windows/Security/ExecutablePolicy.cs
index 1f36164..6c72724 100644
--- a/src/Agent.Windows/Security/ExecutablePolicy.cs
+++ b/src/Agent.Windows/Security/ExecutablePolicy.cs
@@ -13,12 +13,12 @@ public sealed class ExecutablePolicy(IEnumerable<ExecutableApproval> approvals,
public VerifiedExecutable Verify(string executable)
{
string canonical = CanonicalPath.Resolve(executable);
- if (!allowShell && IsShell(canonical)) throw new SandboxPolicyException("process.shell_denied", "Shell execution is disabled.");
- if (!_approvals.TryGetValue(canonical, out string? approvedHash)) throw new SandboxPolicyException("process.path_denied", $"Executable path is not approved: '{canonical}'. Add its canonical path and SHA-256 to sandbox.process.approvals.");
+ if (!allowShell && IsShell(canonical)) throw new SandboxPolicyException("process.shell_denied", "Shell execution is disabled.", "process", canonical, "execute", "sandbox.process.allow_shell");
+ if (!_approvals.TryGetValue(canonical, out string? approvedHash)) throw new SandboxPolicyException("process.path_denied", $"Executable path is not approved: '{canonical}'. Add its canonical path and SHA-256 to sandbox.process.approvals.", "process", canonical, "execute", "sandbox.process.approvals");
using FileStream stream = new(canonical, FileMode.Open, FileAccess.Read, FileShare.Read);
string actualHash = Convert.ToHexString(SHA256.HashData(stream));
if (!CryptographicOperations.FixedTimeEquals(Convert.FromHexString(approvedHash), Convert.FromHexString(actualHash)))
- throw new SandboxPolicyException("process.hash_denied", $"Executable SHA-256 does not match its approval: '{canonical}'. Recalculate SHA-256 and update sandbox.process.approvals.");
+ throw new SandboxPolicyException("process.hash_denied", $"Executable SHA-256 does not match its approval: '{canonical}'. Recalculate SHA-256 and update sandbox.process.approvals.", "process", canonical, "execute", "sandbox.process.approvals");
return new(canonical, actualHash);
}
diff --git a/src/Agent.Windows/Security/PathPolicy.cs b/src/Agent.Windows/Security/PathPolicy.cs
index 4f6eec4..3de4804 100644
--- a/src/Agent.Windows/Security/PathPolicy.cs
+++ b/src/Agent.Windows/Security/PathPolicy.cs
@@ -18,7 +18,8 @@ public sealed class PathPolicy(IEnumerable<FilePolicyEntry> entries)
public void Demand(string path, FileCapability required)
{
if (!IsAllowed(path, required))
- throw new SandboxPolicyException("policy.deny", $"'{path}' is not approved for {required}. Add the canonical path to sandbox.filesystem.{required.ToString().ToLowerInvariant()} and restart the server.");
+ throw new SandboxPolicyException("policy.deny", $"'{path}' is not approved for {required}. Add the canonical path to sandbox.filesystem.{required.ToString().ToLowerInvariant()} and restart the server.",
+ "filesystem", Path.GetFullPath(path), required.ToString().ToLowerInvariant(), $"sandbox.filesystem.{required.ToString().ToLowerInvariant()}");
}
private static bool IsAtOrBelow(string candidate, string root) =>
@@ -43,7 +44,8 @@ public static class CanonicalPath
{
FileSystemInfo info = Directory.Exists(current) ? new DirectoryInfo(current) : new FileInfo(current);
string? target = info.ResolveLinkTarget(returnFinalTarget: true)?.FullName ?? info.LinkTarget;
- throw new SandboxPolicyException("path.reparse", $"Reparse points are not allowed: component='{current}'. Approve the resolved canonical target{(string.IsNullOrWhiteSpace(target) ? string.Empty : $" '{Path.GetFullPath(target)}'")} instead.");
+ throw new SandboxPolicyException("path.reparse", $"Reparse points are not allowed: component='{current}'. Approve the resolved canonical target{(string.IsNullOrWhiteSpace(target) ? string.Empty : $" '{Path.GetFullPath(target)}'")} instead.",
+ "filesystem", current, null, string.IsNullOrWhiteSpace(target) ? "sandbox.filesystem" : Path.GetFullPath(target));
}
}
if (requireExisting && !File.Exists(fullPath) && !Directory.Exists(fullPath)) throw new FileNotFoundException("Approved path does not exist.", fullPath);
@@ -51,7 +53,12 @@ public static class CanonicalPath
}
}
-public sealed class SandboxPolicyException(string code, string message) : UnauthorizedAccessException(message)
+public sealed class SandboxPolicyException(string code, string message, string category = "policy", string? targetPath = null,
+ string? requiredPermission = null, string? resolution = null) : UnauthorizedAccessException(message)
{
public string Code { get; } = code;
+ public string Category { get; } = category;
+ public string? TargetPath { get; } = targetPath;
+ public string? RequiredPermission { get; } = requiredPermission;
+ public string? Resolution { get; } = resolution;
}