summaryrefslogtreecommitdiff
path: root/src/Agent.Windows/Network
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-08-12 05:44:54 +0900
committertanaka takashi <isazaka@gmail.com>2026-08-12 05:44:54 +0900
commitb56614c814b904c224315a11f9686f0d47abd3d3 (patch)
treee6ae4feb852ea4492bf947eabba1e11ab157686a /src/Agent.Windows/Network
parent98b6a2aeedd591058c1d81e401d24f3ded162926 (diff)
feat: revise sandbox usability around semantic policy and LPAC process trees
Rewrite the specification and design vocabulary around User Sandbox Configuration, Environment Resolution, Executable Approval, Effective Sandbox Policy, and Windows Enforcement. Move the Internal Tool catalog to the design without treating its count as a requirement, and update README, implementation planning, traceability, audit history, prompts, and the Python skill. Introduce schema_version 1 profiles, workspaces, mounts, executable aliases, network modes, and resource presets. Add local override merging, resolution and approval stores with atomic user-only persistence, fail-closed fingerprints, localized diagnostics, and executable/sandbox management CLI commands. Change Windows execution to LPAC entry approval with inherited descendant confinement. Separate read and execute ACLs, preserve protected OS DACLs, apply Job process/memory/timeout limits to the tree, enforce WFP network modes, prevent credential environment inheritance, disable out-of-tree MSBuild reuse, and harden transactional ACL/recovery cleanup. Isolate planning prompts from execution instructions, reject split coding plans that cannot satisfy task-scoped evidence, preserve configured thinking, and prevent optional task_update failures from corrupting completion checks. Add and update configuration, resolution, approval, LPAC, descendant, readonly/development, Registry/COM, WFP, recovery, .NET/Python/PowerShell process-tree, and Operational E2E coverage. Publish reproducible bundles while preserving deployment-local configuration, prompts, skills, logs, state, approvals, and resolutions.
Diffstat (limited to 'src/Agent.Windows/Network')
-rw-r--r--src/Agent.Windows/Network/TaskNetworkPolicy.cs28
1 files changed, 16 insertions, 12 deletions
diff --git a/src/Agent.Windows/Network/TaskNetworkPolicy.cs b/src/Agent.Windows/Network/TaskNetworkPolicy.cs
index bc4d291..9f78b3d 100644
--- a/src/Agent.Windows/Network/TaskNetworkPolicy.cs
+++ b/src/Agent.Windows/Network/TaskNetworkPolicy.cs
@@ -11,24 +11,25 @@ public sealed class TaskNetworkPolicy : IDisposable
private readonly ISecurityAuditSink _audit;
private readonly Guid _entryId;
private readonly SecurityIdentifier _appContainerSid;
- private readonly Guid _loopbackEntryId;
+ private readonly Guid? _loopbackEntryId;
private readonly bool _loopbackAdded;
private readonly Guid? _sessionId;
private bool _disposed;
private TaskNetworkPolicy(WfpDynamicSession session, TaskResourceJournal journal, ISecurityAuditSink audit, Guid entryId,
- SecurityIdentifier appContainerSid, Guid loopbackEntryId, bool loopbackAdded, Guid? sessionId)
+ SecurityIdentifier appContainerSid, Guid? loopbackEntryId, bool loopbackAdded, Guid? sessionId)
{
_session = session; _journal = journal; _audit = audit; _entryId = entryId;
_appContainerSid = appContainerSid; _loopbackEntryId = loopbackEntryId; _loopbackAdded = loopbackAdded;
_sessionId = sessionId;
}
- public static TaskNetworkPolicy Apply(TaskResourceJournal journal, SecurityIdentifier appContainerSid, IReadOnlyList<NetworkEndpoint> allowances, ISecurityAuditSink audit, Guid? sessionId = null)
+ public static TaskNetworkPolicy Apply(TaskResourceJournal journal, SecurityIdentifier appContainerSid, IReadOnlyList<NetworkEndpoint> allowances,
+ ISecurityAuditSink audit, Guid? sessionId = null, bool enableLoopback = true)
{
- bool originallyExempt = LoopbackExemption.Contains(appContainerSid);
- Guid loopbackEntryId = journal.Prepare("network.loopback_exemption", appContainerSid.Value,
- new Dictionary<string, string> { ["originally_present"] = originallyExempt.ToString() });
+ bool originallyExempt = enableLoopback && LoopbackExemption.Contains(appContainerSid);
+ Guid? loopbackEntryId = enableLoopback ? journal.Prepare("network.loopback_exemption", appContainerSid.Value,
+ new Dictionary<string, string> { ["originally_present"] = originallyExempt.ToString() }) : null;
bool loopbackAdded = false;
Guid entryId = journal.Prepare("wfp.dynamic", appContainerSid.Value, new Dictionary<string, string>
{
@@ -37,15 +38,18 @@ public sealed class TaskNetworkPolicy : IDisposable
WfpDynamicSession session = WfpDynamicSession.Open();
try
{
- loopbackAdded = LoopbackExemption.Ensure(appContainerSid);
- journal.MarkApplied(loopbackEntryId, new Dictionary<string, string> { ["added"] = loopbackAdded.ToString() });
+ if (enableLoopback)
+ {
+ loopbackAdded = LoopbackExemption.Ensure(appContainerSid);
+ journal.MarkApplied(loopbackEntryId!.Value, new Dictionary<string, string> { ["added"] = loopbackAdded.ToString() });
+ }
IReadOnlyList<ulong> ids = session.ApplyDefaultBlock(appContainerSid, allowances);
journal.MarkApplied(entryId, new Dictionary<string, string> { ["filter_ids"] = string.Join(',', ids) });
audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.rule.apply", "applied", new Dictionary<string, string>
{
["ipv4_ipv6_default_block"] = "true", ["filter_count"] = ids.Count.ToString(), ["session"] = "dynamic"
}, SessionId: sessionId));
- audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.loopback.apply", loopbackAdded ? "added" : "preserved",
+ if (enableLoopback) audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.loopback.apply", loopbackAdded ? "added" : "preserved",
new Dictionary<string, string> { ["app_container_sid"] = appContainerSid.Value }, SessionId: sessionId));
return new(session, journal, audit, entryId, appContainerSid, loopbackEntryId, loopbackAdded, sessionId);
}
@@ -54,7 +58,7 @@ public sealed class TaskNetworkPolicy : IDisposable
session.Dispose();
journal.MarkCleaned(entryId);
if (loopbackAdded) LoopbackExemption.Remove(appContainerSid);
- journal.MarkCleaned(loopbackEntryId);
+ if (loopbackEntryId is not null) journal.MarkCleaned(loopbackEntryId.Value);
throw;
}
}
@@ -66,9 +70,9 @@ public sealed class TaskNetworkPolicy : IDisposable
_session.Dispose();
_journal.MarkCleaned(_entryId);
if (_loopbackAdded) LoopbackExemption.Remove(_appContainerSid);
- _journal.MarkCleaned(_loopbackEntryId);
+ if (_loopbackEntryId is not null) _journal.MarkCleaned(_loopbackEntryId.Value);
_audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.rule.remove", "removed", new Dictionary<string, string> { ["residual_filters"] = "0" }, SessionId: _sessionId));
- _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.loopback.remove", "removed",
+ if (_loopbackEntryId is not null) _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.loopback.remove", "removed",
new Dictionary<string, string> { ["residual_exemptions"] = LoopbackExemption.Contains(_appContainerSid) ? "preexisting" : "0" }, SessionId: _sessionId));
_disposed = true;
}