diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Agent.Windows/Security/TaskAclManager.cs | 23 | ||||
| -rw-r--r-- | src/Agent.Windows/Tools/WindowsToolBackend.cs | 32 |
2 files changed, 11 insertions, 44 deletions
diff --git a/src/Agent.Windows/Security/TaskAclManager.cs b/src/Agent.Windows/Security/TaskAclManager.cs index f01e859..2b091a6 100644 --- a/src/Agent.Windows/Security/TaskAclManager.cs +++ b/src/Agent.Windows/Security/TaskAclManager.cs @@ -26,7 +26,13 @@ public sealed class TaskAclManager(SecurityIdentifier appContainerSid, ISecurity security.AddAccessRule(rule); WriteSecurity(canonical, security); if (journalEntry is not null) journal!.MarkApplied(journalEntry.Value); - audit.Write(new(DateTimeOffset.UtcNow, taskId, "acl.apply", "applied", new Dictionary<string, string> { ["path"] = canonical, ["sid"] = appContainerSid.Value, ["rights"] = rights.ToString() })); + audit.Write(new(DateTimeOffset.UtcNow, taskId, "acl.apply", "applied", new Dictionary<string, string> + { + ["path"] = canonical, + ["sid"] = appContainerSid.Value, + ["rights"] = rights.ToString(), + ["scope"] = directory ? "root_inheritance" : "file" + })); return new(canonical, original, journalEntry); } @@ -39,21 +45,6 @@ public sealed class TaskAclManager(SecurityIdentifier appContainerSid, ISecurity audit.Write(new(DateTimeOffset.UtcNow, taskId, "acl.restore", "restored", new Dictionary<string, string> { ["path"] = snapshot.Path })); } - public void NormalizeNewEntry(Guid taskId, string path) - { - string canonical = CanonicalPath.Resolve(path); - FileSystemSecurity security = ReadSecurity(canonical); - security.SetAccessRuleProtection(isProtected: true, preserveInheritance: false); - foreach (FileSystemAccessRule rule in security.GetAccessRules(includeExplicit: true, includeInherited: false, typeof(SecurityIdentifier)) - .OfType<FileSystemAccessRule>() - .Where(rule => rule.IdentityReference == appContainerSid) - .ToArray()) - security.RemoveAccessRuleSpecific(rule); - security.SetAccessRuleProtection(isProtected: false, preserveInheritance: false); - WriteSecurity(canonical, security); - audit.Write(new(DateTimeOffset.UtcNow, taskId, "acl.normalize", "normalized", new Dictionary<string, string> { ["path"] = canonical })); - } - private static FileSystemSecurity ReadSecurity(string path) => Directory.Exists(path) ? FileSystemAclExtensions.GetAccessControl(new DirectoryInfo(path), AccessControlSections.Access) : FileSystemAclExtensions.GetAccessControl(new FileInfo(path), AccessControlSections.Access); diff --git a/src/Agent.Windows/Tools/WindowsToolBackend.cs b/src/Agent.Windows/Tools/WindowsToolBackend.cs index a21544b..c0d1561 100644 --- a/src/Agent.Windows/Tools/WindowsToolBackend.cs +++ b/src/Agent.Windows/Tools/WindowsToolBackend.cs @@ -49,8 +49,10 @@ public sealed class WindowsToolBackend(WindowsSandboxSettings settings, ISecurit // Do not rewrite a file ACL when execute is the only requested capability; CreateProcess remains // constrained by AppContainer access plus canonical path/SHA-256 approval. if (File.Exists(path) && capabilities == FileCapability.Execute) continue; - context.Roots.Add(path); - foreach (string target in ExistingAclTargets(path)) context.Acls.Add(acl.Grant(taskId, target, capabilities)); + // A single inheritable ACE on an approved directory covers existing and newly-created + // descendants. Snapshotting and rewriting every child ACL is both redundant and + // prohibitively expensive for runtime trees such as Python installations. + context.Acls.Add(acl.Grant(taskId, path, capabilities)); } IReadOnlyList<NetworkEndpoint> allowances = settings.NetworkMode == "allow" ? await NetworkAllowanceSnapshot.ResolveAsync(settings.NetworkAllowances, cancellationToken).ConfigureAwait(false) @@ -193,11 +195,6 @@ public sealed class WindowsToolBackend(WindowsSandboxSettings settings, ISecurit List<Exception> failures = []; try { context.Network?.Dispose(); } catch (Exception ex) { failures.Add(ex); } foreach (AclSnapshot snapshot in context.Acls.AsEnumerable().Reverse()) try { context.Acl.Restore(taskId, snapshot); } catch (Exception ex) { failures.Add(ex); } - var existingSnapshots = context.Acls.Select(static snapshot => snapshot.Path).ToHashSet(StringComparer.OrdinalIgnoreCase); - foreach (string entry in context.Roots.SelectMany(ExistingAclTargets).Distinct(StringComparer.OrdinalIgnoreCase) - .Where(entry => !existingSnapshots.Contains(entry)) - .OrderBy(static entry => entry.Count(character => character == Path.DirectorySeparatorChar))) - try { context.Acl.NormalizeNewEntry(taskId, entry); } catch (Exception ex) { failures.Add(ex); } if (failures.Count == 0) context.Journal.CompleteAndDelete(); await Task.CompletedTask; if (failures.Count > 0) throw new AggregateException("Task resource cleanup failed.", failures); @@ -215,26 +212,6 @@ public sealed class WindowsToolBackend(WindowsSandboxSettings settings, ISecurit var regex = new Regex(query, RegexOptions.CultureInvariant, TimeSpan.FromSeconds(1)); return files.Where(path => IsTextMatch(path, regex.IsMatch)); } - private static IEnumerable<string> ExistingAclTargets(string path) - { - if (File.Exists(path)) - { - yield return path; - yield break; - } - if (!Directory.Exists(path)) yield break; - var options = new EnumerationOptions - { - RecurseSubdirectories = true, - IgnoreInaccessible = false, - ReturnSpecialDirectories = false, - AttributesToSkip = FileAttributes.ReparsePoint - }; - foreach (string entry in Directory.EnumerateFileSystemEntries(path, "*", options) - .OrderByDescending(static entry => entry.Count(character => character == Path.DirectorySeparatorChar))) - yield return entry; - yield return path; - } private static string ApplyPatch(string path, string patch) { string current = File.Exists(path) ? File.ReadAllText(path) : string.Empty; @@ -251,7 +228,6 @@ public sealed class WindowsToolBackend(WindowsSandboxSettings settings, ISecurit public TaskResourceJournal Journal { get; } = journal; public TaskAclManager Acl { get; } = acl; public List<AclSnapshot> Acls { get; } = []; - public HashSet<string> Roots { get; } = new(StringComparer.OrdinalIgnoreCase); public TaskNetworkPolicy? Network { get; set; } } } |
