From b56614c814b904c224315a11f9686f0d47abd3d3 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Wed, 12 Aug 2026 05:44:54 +0900 Subject: 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. --- README.md | 120 +++-- config/agent.local.example.yaml | 16 +- config/agent.yaml | 23 +- config/locales/en.json | 28 +- config/locales/ja.json | 28 +- doc/AuditReport.md | 18 +- doc/Design.md | 249 +++++---- doc/Specifications.md | 131 +++-- plans/ImplementationPlan.md | 86 ++-- plans/RequirementTraceability.md | 34 +- plans/progress/Phase6.md | 1 + prompts/user-system.md | 5 +- scripts/Launch-ElevatedVerification.ps1 | 10 + scripts/Operational-E2E.ps1 | 271 ++++------ scripts/Publish-Release.ps1 | 47 +- scripts/Restart-PublishedServer.ps1 | 30 ++ scripts/Run-Final-ElevatedVerification.ps1 | 20 + skills/python/SKILL.md | 4 +- src/Agent.App/Agent.App.csproj | 1 + src/Agent.App/Cli/CommandLine.cs | 102 +++- src/Agent.App/Configuration/AgentConfiguration.cs | 31 +- .../Configuration/ConfigurationException.cs | 11 +- src/Agent.App/Configuration/ConfigurationLoader.cs | 86 +++- src/Agent.App/Program.cs | 225 +++++++-- src/Agent.App/Sandbox/SandboxServices.cs | 361 ++++++++++++++ src/Agent.Core/Runtime/AgentRuntime.cs | 12 +- src/Agent.Core/Runtime/PlannerService.cs | 38 +- src/Agent.Windows/NativeMethods.txt | 9 +- src/Agent.Windows/Network/TaskNetworkPolicy.cs | 28 +- .../Recovery/TransactionalFileEditor.cs | 12 +- src/Agent.Windows/Security/ExecutablePolicy.cs | 87 +++- src/Agent.Windows/Security/PathPolicy.cs | 37 +- .../Security/RestrictedProcessLauncher.cs | 208 ++++++-- src/Agent.Windows/Security/TaskAclManager.cs | 74 ++- src/Agent.Windows/Tools/WindowsToolBackend.cs | 60 ++- tests/Agent.Core.Tests/ConfigurationTests.cs | 250 +++++++++- tests/Agent.Core.Tests/PromptAndRuntimeTests.cs | 27 +- .../Agent.Windows.Tests/Agent.Windows.Tests.csproj | 1 + .../Agent.Windows.Tests/CrashScenarioBootstrap.cs | 4 +- tests/Agent.Windows.Tests/SecurityPolicyTests.cs | 555 ++++++++++++++++++--- 40 files changed, 2538 insertions(+), 802 deletions(-) create mode 100644 scripts/Launch-ElevatedVerification.ps1 create mode 100644 scripts/Restart-PublishedServer.ps1 create mode 100644 src/Agent.App/Sandbox/SandboxServices.cs diff --git a/README.md b/README.md index 3dede12..d1bde9f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AI Agent Sandbox MCP Server v1 -Windows 11上でlocal Ollamaを利用し、明示的に承認したFilesystem、Process、Network操作だけをAppContainer内で実行するMCP Serverです。MCP `2026-07-28`のstateless HTTPと公式Tasks Extensionに対応し、MCP公開Toolは `agent_run` と `agent_status` の2つです。 +Windows 11上でOllamaを利用し、利用者が確認したWorkspace、Mount、Executable、Network、Resourceの範囲だけをLPAC内で実行するMCP Serverです。MCP `2026-07-28`のstateless HTTPと公式Tasks Extensionに対応し、MCP公開Toolは `agent_run` と `agent_status` の2つです。 ## 動作要件 @@ -8,7 +8,7 @@ Windows 11上でlocal Ollamaを利用し、明示的に承認したFilesystem、 - 管理者権限(TaskごとのWFP filter追加・削除に必要) - Base Filtering Engine (BFE)が実行中 - Ollama。tested versionは実測した `0.32.6`。minimum supported versionは未確定です。 -- Release既定model: `deepseek-v4-flash:cloud`(native Tool calling対応) +- Release既定model: `deepseek-v4-flash:cloud`(native Internal Tool calling対応) サービス、常駐helper、恒久Firewall ruleは使用しません。WFP objectはdynamic sessionだけに作成されます。AppContainerからlocalhostへ接続するTaskでは、固定AppContainer SIDのloopback exemptionをTask中だけ追加し、終了時または次回起動Recoveryで削除します。 @@ -22,9 +22,14 @@ ollama pull deepseek-v4-flash:cloud ollama serve ``` -別の管理者PowerShellで診断します。 +初回はUser Sandbox ConfigurationのLogical Pathを確認してから、別の管理者PowerShellで診断します。 ```powershell +.\agent-mcp.exe sandbox resolve --config .\agent.yaml +.\agent-mcp.exe executable resolve python --config .\agent.yaml +.\agent-mcp.exe executable approve python --path C:\Python314\python.exe --runtime-root C:\Python314 --config .\agent.yaml +.\agent-mcp.exe sandbox confirm --config .\agent.yaml +.\agent-mcp.exe sandbox show --format text --config .\agent.yaml .\agent-mcp.exe check --config .\agent.yaml --lang ja ``` @@ -32,9 +37,67 @@ ollama serve `serve`も待受開始前に同じprobeを必ず実行します。失敗時はendpointをbindせず、失敗項目・Win32 error・対処方法をstderrと`logs/application.log`へ出してexit code 2で終了します。 -## agent.yaml +## User Sandbox Configuration -Filesystem pathは能力ごとに指定します。未指定能力はdenyです。 +`schema_version: 1`のSandbox設定は利用者が理解できるsemantic単位で記述します。未指定capabilityはdenyです。 + +```yaml +sandbox: + profile: development + workspaces: + - C:/work/project + mounts: + - path: C:/data/reference + access: read_only + execution: + executables: [python, git] + shells: [powershell] + network: + access: local + local_endpoints: + - port: 11434 + restricted_endpoints: [] + resources: + profile: normal + # memory_mb: 4096 + # process_limit: 16 + # execution_timeout_seconds: 1800 +``` + +`readonly` ProfileはWorkspaceをread-onlyにし、Workspace ArtifactのEntry起動を禁止します。`development`はWorkspaceのread/write/deleteとArtifact起動を許可します。Mountは`read_only`、`read_write`、`read_write_delete`から選び、Mount上のExecutableをEntry Processとして起動することはできません。 + +Resource Profileは`constrained`(1024 MiB/4 Process/300秒)、`normal`(4096 MiB/16 Process/1800秒)、`extended`(8192 MiB/32 Process/3600秒)です。正数overrideを指定できます。 + +Network accessは`none`、`local`、`restricted`です。`none`はLoopbackを含め全deny、`local`は列挙portの`127.0.0.1`と`::1`だけ、`restricted`はTask開始時にIP snapshot化したhost/portだけを許可します。Ollama通信はBroker側から行うため、Sandbox Process TreeのNetwork設定とは独立です。 + +Workspace/Mountはreparse chainをResolved Pathへ解決します。`sandbox resolve`で差分を確認し、`sandbox confirm`でconfig別のResolution Storeへ保存してください。config fingerprintまたはResolved Pathが変わると`serve`は再確認までfail-closedになります。 + +```powershell +.\agent-mcp.exe sandbox resolve --config .\agent.yaml +.\agent-mcp.exe sandbox confirm --config .\agent.yaml +.\agent-mcp.exe sandbox show --format json --config .\agent.yaml +``` + +## Executable Approval + +`sandbox.execution`には人間向けAliasを記載します。PATH候補は提示だけで自動選択されません。選んだResolved PathとSHA-256をuser-local Approval Storeへ固定します。 + +```powershell +.\agent-mcp.exe executable resolve python --config .\agent.yaml +.\agent-mcp.exe executable approve python --path C:\Python314\python.exe --runtime-root C:\Python314 --config .\agent.yaml +.\agent-mcp.exe executable list --config .\agent.yaml +.\agent-mcp.exe executable revoke python --config .\agent.yaml +``` + +Approved Entry Executable/Approved Shell ExecutableはAlias、Resolved Path、SHA-256一致が必要です。確認済みRuntime Root内のRuntime Componentと、development Workspace内のWorkspace ArtifactもEntry Processにできます。その他のExternal Unknown ExecutableをBrokerから直接起動することはできません。 + +ApprovalはEntry Processにだけ適用されます。Descendant Processは個別Approvalを要求せず、Entry Processと同じLPAC token、Filesystem ACL、WFP、Job Objectを継承します。Jobからのbreakawayは禁止され、Process数・memory・timeout・`KILL_ON_JOB_CLOSE`はProcess Tree全体へ適用されます。 + +Sandbox Process Treeへは最小限のWindows/runtime環境変数だけを渡し、Brokerの任意token、password、SSH/API credential環境変数は継承しません。User profile等のpathが環境に含まれても、そのpathへFilesystem Capabilityは付与されません。 + +Executable Approval StoreとResolution Storeは`%LOCALAPPDATA%\AgentSandboxMcp`にuser-only DACL、WriteThrough、atomic replaceで保存されます。publishやupgradeはこれらのStoreを上書きしません。 + +## その他のagent.yaml設定 MCP Taskの保持とlog出力は次のtyped settingで変更できます。 @@ -62,47 +125,9 @@ logging: Task状態は`state/mcp-tasks/`へatomic保存されます。再起動時の未完了Taskは`failed/server_restarted`となり、自動再実行されません。 -```yaml -sandbox: - filesystem: - read: [C:/work/fixture] - write: [C:/work/fixture] - execute: - - C:/Program Files/dotnet/dotnet.exe - delete: [C:/work/fixture] -``` - -実行ファイルはcanonical absolute pathとSHA-256の両方を承認します。 - -```powershell -(Get-FileHash -Algorithm SHA256 -LiteralPath 'C:\Program Files\dotnet\dotnet.exe').Hash -``` - -```yaml -sandbox: - process: - allow_shell: false - approvals: - - path: C:/Program Files/dotnet/dotnet.exe - sha256: <64桁の実測値> -``` - -Sandbox processからの子process生成は、承認済みpathであってもOSのchild-process restrictionで拒否されます。build/testで必要な各実行ファイルは事前にpath/hash承認し、個別の `process_run` としてBrokerから起動してください。E2Eではbuild server、node reuse、shared compilationを無効化し、in-process buildまたはBrokerから直接起動できるself-testを使用します。Networkは既定でlocalhostを含めdenyです。allow時はTask開始時にhostをIPへsnapshot化します。 - -```yaml -sandbox: - network: - mode: allow - allow: - - host: 127.0.0.1 - ports: [11434] -``` - -Ollama通信は管理Broker側から行うため、Sandbox processのnetwork許可とは独立です。 - ## Agent Skills -`skills//SKILL.md`形式だけを読み込みます。legacyの`skills/*.md`は読み込みません。SkillはLLMへの補助指示であり、Filesystem/Process/Network権限は付与しません。Skillが使用する実行ファイルは、別途`agent.yaml`でabsolute pathとSHA-256を承認してください。 +`skills//SKILL.md`形式だけを読み込みます。legacyの`skills/*.md`は読み込みません。SkillはLLMへの補助指示であり、Sandbox capabilityは付与しません。Skillが使うHost ExecutableはAliasを設定し、Entry起動前に承認してください。 ## stdio @@ -162,8 +187,9 @@ LLM/Audit JSONLはtask、session、event、call単位で絞り込めます。 - `error 5` / administrator=false: 管理者PowerShellから起動してください。 - BFE open失敗: `Get-Service BFE` でBase Filtering Engineを確認してください。 - Ollama接続失敗: `ollama list`、endpoint、model名を確認してください。 -- executable deny: pathをabsolute/canonicalにし、更新後のSHA-256を再取得してください。 -- file deny: 対象pathを必要なread/write/execute/delete能力へ追加し、再起動してください。 +- resolution未確認/変更: `sandbox resolve`のLogical/Resolved Path差分を確認し、`sandbox confirm`を実行してください。 +- Alias不明/Approval不足/hash変更: `executable resolve`で候補を確認し、明示したpathを`executable approve`で再承認してください。 +- Filesystem deny: 対象を適切なWorkspaceまたはMountとして設定し、Resolutionを再確認してください。 - rollback拒否: Task中に外部processが同じfileを変更しています。Audit Logとjournalを確認し、手動reviewしてください。 ## ソースからのbuild/release @@ -187,4 +213,4 @@ dotnet test AgentSandboxMcp.slnx -c Release --no-build --no-restore `artifacts/` はローカルの試験証跡・デバッグログ用です。`artifacts/` と `build/` はどちらもGit管理外です。 -Release scriptはwin-x64 self-contained directory、version付きZIP、ZIP checksum、publish manifestを生成し、ソースツリー外のfresh temporary directoryでBundleを検査します。完全な `agent-mcp check` は管理者PowerShellで実行してください。 +Release scriptは再現可能な既定値だけを含むwin-x64 self-contained ZIP、checksum、publish manifestをstagingから生成し、ソースツリー外のfresh temporary directoryでBundleを検査します。deployment directory更新時は`agent.local.yaml`、`local/`、`logs/`、`state/`を削除・上書きしません。完全な `agent-mcp check` はResolution確認とExecutable Approval後に管理者PowerShellで実行してください。 diff --git a/config/agent.local.example.yaml b/config/agent.local.example.yaml index f974dbe..4961428 100644 --- a/config/agent.local.example.yaml +++ b/config/agent.local.example.yaml @@ -7,12 +7,10 @@ agent: skills: directory: local/skills sandbox: - filesystem: - read: - - C:/work/project - write: - - C:/work/project - execute: - - C:/Program Files/dotnet/dotnet.exe - delete: - - C:/work/project + profile: development + workspaces: + - C:/work/project + mounts: [] + execution: + executables: [python] + shells: [] diff --git a/config/agent.yaml b/config/agent.yaml index 64c5f9d..b38e693 100644 --- a/config/agent.yaml +++ b/config/agent.yaml @@ -29,21 +29,18 @@ ollama: planner: max_tasks: 32 sandbox: - filesystem: - read: [] - write: [] - execute: [] - delete: [] - process: - allow_shell: true - approvals: [] - max_processes: 16 + profile: readonly + workspaces: [] + mounts: [] + execution: + executables: [] + shells: [] network: - mode: deny - allow: [] + access: none + local_endpoints: [] + restricted_endpoints: [] resources: - memory_mb: 4096 - process_timeout_seconds: 1800 + profile: normal file_edit: memory_backup_limit_mb: 16 limits: diff --git a/config/locales/en.json b/config/locales/en.json index 4330cb7..3e9a5fa 100644 --- a/config/locales/en.json +++ b/config/locales/en.json @@ -26,5 +26,31 @@ "runtime.policy_denied": "The sandbox policy denied the requested operation (code={code}, path={path}, required_permission={permission}). Resolution: {resolution}", "runtime.policy_replan_exhausted": "The sandbox policy denial remained unresolved after {message} replans.", "runtime.policy_repeated": "The same sandbox policy denial occurred again, so replanning was stopped.", - "runtime.replan_unchanged": "Replanning did not change the incomplete task plan." + "runtime.replan_unchanged": "Replanning did not change the incomplete task plan.", + "runtime.policy_resolution.filesystem": "Review the Workspace or Mount and its required Filesystem Capability, then confirm the Sandbox Environment Resolution.", + "runtime.policy_resolution.process": "Use a configured Alias or an allowed Runtime Component or development Workspace Artifact; re-approve a changed Executable identity.", + "runtime.policy_resolution.network": "Review the selected Network access mode and endpoint list; the Agent cannot widen it during a Task.", + "runtime.policy_resolution.default": "Review the User Sandbox Configuration and confirmed Effective Sandbox Policy.", + "management.approval_diff": "Alias: {alias}\nClassification: {classification}\nResolved Path: {path}\nSHA-256: {sha256}\nRuntime Root: {runtime_root}", + "management.approve_prompt": "Approve this Executable Alias?", + "management.revoke_diff": "Revoke {alias}: {path}", + "management.revoke_prompt": "Revoke this Executable Alias?", + "management.sandbox_confirm_prompt": "Confirm this Sandbox Environment Resolution?", + "management.resolution_header": "Configuration: {config}\nFingerprint: {fingerprint}", + "management.confirmed": "Confirmed: {value}", + "management.yes": "yes", + "management.no": "no", + "management.ambiguous_candidates": "Multiple candidates were found. No candidate was selected; re-run with --path .", + "sandbox.error.resolution_unconfirmed": "The Sandbox Environment Resolution is not confirmed.", + "sandbox.error.resolution_unconfirmed.resolution": "Review sandbox resolve, then run sandbox confirm.", + "sandbox.error.resolution_changed": "The Sandbox Environment Resolution changed after confirmation.", + "sandbox.error.resolution_changed.resolution": "Review the changed Logical and Resolved Paths, then confirm again.", + "sandbox.error.approval_missing": "The Executable Alias is not approved: {target}", + "sandbox.error.approval_missing.resolution": "Resolve the Alias, review one candidate, and explicitly approve it.", + "sandbox.error.executable_resolution_changed": "The Executable Resolved Path changed: {target}", + "sandbox.error.executable_resolution_changed.resolution": "Verify the new identity and explicitly re-approve it.", + "sandbox.error.executable_hash_changed": "The Executable SHA-256 changed: {target}", + "sandbox.error.executable_hash_changed.resolution": "Verify the updated file and explicitly re-approve it.", + "sandbox.error.shell_unsupported": "The selected file is not a supported Shell: {target}", + "sandbox.error.shell_unsupported.resolution": "Select cmd.exe, powershell.exe, or pwsh.exe." } diff --git a/config/locales/ja.json b/config/locales/ja.json index c5a6931..7d51334 100644 --- a/config/locales/ja.json +++ b/config/locales/ja.json @@ -26,5 +26,31 @@ "runtime.policy_denied": "要求された操作はSandbox Policyにより拒否されました(code={code}、path={path}、必要権限={permission})。解決方法: {resolution}", "runtime.policy_replan_exhausted": "Sandbox Policyによる拒否を{message}回の再計画で解消できませんでした。", "runtime.policy_repeated": "同一のSandbox Policy拒否が再発したため、再計画を停止しました。", - "runtime.replan_unchanged": "再計画後も未完了のタスク計画が変更されませんでした。" + "runtime.replan_unchanged": "再計画後も未完了のタスク計画が変更されませんでした。", + "runtime.policy_resolution.filesystem": "WorkspaceまたはMountと必要なFilesystem Capabilityを確認し、Sandbox Environment Resolutionを再確認してください。", + "runtime.policy_resolution.process": "設定済みAlias、許可されたRuntime Component、またはdevelopment Workspace Artifactを使用し、identityが変わったExecutableは再承認してください。", + "runtime.policy_resolution.network": "Network access modeとendpoint一覧を確認してください。AgentはTask中に許可範囲を拡張できません。", + "runtime.policy_resolution.default": "User Sandbox Configurationと確認済みEffective Sandbox Policyを確認してください。", + "management.approval_diff": "Alias: {alias}\n分類: {classification}\n解決済みpath: {path}\nSHA-256: {sha256}\nRuntime Root: {runtime_root}", + "management.approve_prompt": "このExecutable Aliasを承認しますか?", + "management.revoke_diff": "承認取消 {alias}: {path}", + "management.revoke_prompt": "このExecutable Aliasの承認を取り消しますか?", + "management.sandbox_confirm_prompt": "このSandbox Environment Resolutionを確認済みにしますか?", + "management.resolution_header": "設定: {config}\nfingerprint: {fingerprint}", + "management.confirmed": "確認済み: {value}", + "management.yes": "はい", + "management.no": "いいえ", + "management.ambiguous_candidates": "複数の候補が見つかりました。自動選択していません。--path を指定して再実行してください。", + "sandbox.error.resolution_unconfirmed": "Sandbox Environment Resolutionが未確認です。", + "sandbox.error.resolution_unconfirmed.resolution": "sandbox resolveの差分を確認してからsandbox confirmを実行してください。", + "sandbox.error.resolution_changed": "確認後にSandbox Environment Resolutionが変更されました。", + "sandbox.error.resolution_changed.resolution": "Logical PathとResolved Pathの変更を確認してから、再度confirmしてください。", + "sandbox.error.approval_missing": "Executable Aliasが未承認です: {target}", + "sandbox.error.approval_missing.resolution": "Alias候補をresolveし、1つの候補を確認して明示的にapproveしてください。", + "sandbox.error.executable_resolution_changed": "ExecutableのResolved Pathが変更されました: {target}", + "sandbox.error.executable_resolution_changed.resolution": "新しいidentityを確認し、明示的に再承認してください。", + "sandbox.error.executable_hash_changed": "ExecutableのSHA-256が変更されました: {target}", + "sandbox.error.executable_hash_changed.resolution": "更新されたfileを確認し、明示的に再承認してください。", + "sandbox.error.shell_unsupported": "選択されたfileは対応Shellではありません: {target}", + "sandbox.error.shell_unsupported.resolution": "cmd.exe、powershell.exe、pwsh.exeのいずれかを選択してください。" } diff --git a/doc/AuditReport.md b/doc/AuditReport.md index c98694e..47ee218 100644 --- a/doc/AuditReport.md +++ b/doc/AuditReport.md @@ -1,8 +1,16 @@ -# v1再監査報告 +# v1再監査報告とSandbox Usability Revision状況 ## 基準 -2026-08-11、commit `37386b8`を現行実装の検証対象とした。2026-08-09のPhase/Release証跡は履歴として扱い、現行PASS判定には流用していない。 +commit `37386b8`に対する2026-08-11の再監査結果は履歴として以下に保持する。2026-08-12に開始したSandbox Usability RevisionはUser Sandbox Configuration、Resolution、Executable Approval、Effective Sandbox Policy、LPAC Process Treeへ安全境界を改定する破壊的変更であり、旧commitのPASSを現行実装へ流用しない。 + +## Sandbox Usability Revision + +- 仕様書から先に用語、安全不変条件、semantic schema、CLI、Store、LPAC/ACL/Job/WFP強制条件を改定した。 +- 設計書へUser Configuration → Environment Resolution → Approval Resolution → Effective Policy → Broker → LPAC/ACL/Job/WFPを定義した。 +- 旧`filesystem`/`process.approvals` schemaは移行診断付きで拒否する。 +- Entry ProcessだけをApproval対象とし、Descendant Processは個別Approvalなしで同じLPAC identity、ACL、WFP、Job confinementを継承する。 +- 2026-08-12の最終working-tree検証でRelease buildはwarning 0/error 0、Core 103件、Planner 10件、Windows Security/Recovery 53件がすべてPASS、skip 0。`deepseek-v4-flash:cloud` + `think:true` + Python 3.14.3によるstdio、HTTP IPv4、HTTP IPv6 Operational E2EもすべてPASSし、各経路で複雑なPython実装、子Python、独立unittestを完走した。Task終了後のJournal、backup、published process残留は0。証跡は`artifacts/evidence/sandbox-usability-final-working-tree-2/`に保存した。 ## 確認した矛盾と是正 @@ -21,11 +29,13 @@ | Configuration | validationのみでruntime未接続、local overrideなし | max_tasks/num_ctx/ratio/timeoutを接続し、`agent.local.yaml` mergeを追加 | | CLI/運用 | `serve`必須、publish後の環境値復旧が必要 | 引数なし/先頭optionをserve化。local prompt/Skill/configをBundle対象外に分離 | | Logging/Audit | 相関field・iteration・event名が不一致 | LLM相関/rotationとAudit必須eventへ統一。read-only JSONL viewerを追加 | +| Credential環境 | Brokerのenvironment blockをSandbox Processへ継承 | Windows/runtimeに必要な明示的Unicode environmentだけを渡し、任意token/password/SSH/API credentialを除外。実Processでsecret非継承を検証 | +| Descendant実行 | 旧child process全面禁止がprompt/Skill/testに残存 | EntryだけをApproval対象とし、同一LPAC SID/Job内のPowerShell、Python、.NET build/test Process Treeを許容する設計へ統一 | -## 現在の判定 +## 旧commit `37386b8`の判定(履歴) Release buildはwarning 0/error 0。管理者suiteでCore 82件、Planner 10件、Windows Security/Recovery 42件がすべてPASSし、skipは0だった。self-contained Bundleのruntime check、stdio、HTTP IPv4/IPv6 Operational E2Eも`deepseek-v4-flash:cloud`でPASSした。各E2Eでは実file read/edit、in-process build、独立self-test、Task進捗、ACL/WFP/temp cleanupを確認した。 MCP conformance runnerはapplication failure 0だった。runner専用fixtureを本番MCP Toolとして公開しないため実施不能なcheckと、Windows runner終了時のlibuv assertionはrunner limitationとして分離し、PASSには数えていない。Requirementの受入根拠は製品のCore/MCP integration、Windows suite、Operational E2Eで満たしている。詳細と現行証跡は`plans/RequirementTraceability.md`を正本とする。 -既存の仕様語彙で表現できない独立概念は実装へ追加していない。local override、status診断field、ログ相関field、Task固有Tool証跡はSpecifications/Designの責務に従って記録した。 +この節の結果はSandbox Usability Revision後の現行実装のPASS根拠ではない。 diff --git a/doc/Design.md b/doc/Design.md index 9ad4ae3..8f76ca6 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -69,21 +69,35 @@ Agent Runtime ─── Session Store (memory) ├── Prompt Composer ── Skill Selector/Loader ├── Ollama Client ───── Ollama localhost ├── Token Planner ───── TaskGraph / PERT / CPM - └── Internal Tool Dispatcher - │ - ▼ - Policy Engine + └── Internal Tool Dispatcher + │ + ▼ + User Sandbox Configuration + │ + ▼ + Sandbox Environment Resolution + ├── Workspace / Mount / Reparse Resolution + ├── Executable Alias / Runtime Resolution + ├── Executable Approval Resolution + ├── Network Resolution + └── Resource Resolution + │ + ▼ + Effective Sandbox Policy + │ + ▼ + Policy Engine │ ▼ Sandbox Broker - ├── AppContainer + ├── Less Privileged AppContainer (LPAC) ├── NTFS ACL ├── Job Object ├── WFP └── Named Pipe │ ▼ - Sandboxed Process Tree + Sandbox Process Tree Audit Writer / Recovery Manager は各副作用境界を横断して記録・復旧する。 ``` ### 3.1 信頼境界 @@ -252,14 +266,14 @@ overrideはTask objectにcopyし、global configurationを変更しない。Sess - mime_type、Base64妥当性、設定上限size/countを事前検査する。 - 既定上限は4画像、1画像10 MiB、request合計20 MiBとする。 - model capability確認でVision非対応と判定した場合はOllama呼出し前にlocalized error。能力確認不能時は呼出しを行い、Ollama errorを正規化して返す。 -### 6.5 Tool calling -Ollamaに公開する現行Internal Tool catalogは次のとおりとする。個々のOS command (`grep.exe`等) をtoolとして増殖させない。catalogの構成は設計事項であり、仕様上の固定数として扱わない。 -| Tool | 責務 | 主なoperation | +### 6.5 Internal Tool calling +Ollamaに公開する現行Internal Tool catalogは次のとおりとする。個々のOS Executable (`grep.exe`等) をInternal Toolとして増殖させない。catalogの構成は設計事項であり、仕様上の固定数として扱わない。 +| Internal Tool | 責務 | 主なoperation | | --- | --- | --- | | file_search | 検索 | glob / name / text / regex / tail相当 | | file_read | 読取 | whole / range / metadata | | file_edit | 編集 | overwrite / patch / atomic_replace / delete | -| process_run | 承認済Process実行 | exe + args + cwd + timeout | +| process_run | Entry Process実行 | Executable + args + cwd + timeout | | task_update | TaskGraph更新 | add / dependency / estimate | | dir | ディレクトリ操作 | mkdir / rmdir / list | --- @@ -278,7 +292,7 @@ Ollamaに公開する現行Internal Tool catalogは次のとおりとする。 ``` - wholeはsize上限を超えた場合range利用を要求する。 - whole readの既定上限は1 MiBとする。 -- read前にcanonical pathとreparse pointを検査する。 +- read前にLogical Pathをreparse resolutionし、確認済みResolved PathとEffective Filesystem Policyを検査する。 ### 7.3 file_edit ```json { @@ -290,10 +304,10 @@ Ollamaに公開する現行Internal Tool catalogは次のとおりとする。 "new_path":"string|null" } ``` -- overwrite/patch/atomic_replaceはwrite permissionを要求する。 -- `patch` operationの`patch` fieldは、JSON文字列化した`{"old_text":"...","new_text":"..."}`を受け取る。unified diffは受け付けない。`old_text`は対象file内で正確に1回だけ一致しなければならない。不正形式または一致数不正は再試行可能なTool failureとする。 -- deleteはdelete permissionを要求する。 -- renameはsource/destination双方にwrite permissionを要求する。destinationが既に存在する場合は拒否する(overwrite不可)。cross-volume renameは不支持でTool failureとして返す。 +- overwrite/patch/atomic_replaceはwrite Filesystem Capabilityを要求する。 +- `patch` operationの`patch` fieldは、JSON文字列化した`{"old_text":"...","new_text":"..."}`を受け取る。unified diffは受け付けない。`old_text`は対象file内で正確に1回だけ一致しなければならない。不正形式または一致数不正は再試行可能なInternal Tool failureとする。 +- deleteはdelete Filesystem Capabilityを要求する。 +- renameはsource/destination双方にwrite Filesystem Capabilityを要求する。destinationが既に存在する場合は拒否する(overwrite不可)。cross-volume renameは不支持でInternal Tool failureとして返す。 - rename transaction: PREPARE(source/destination検査+journal) → APPLY(File.Move) → VERIFY(source不存在/destination存在/hash一致) → COMMIT。crash recoveryはdestination hashがjournal記録と一致する場合のみdestinationからsourceへ戻す。 - `expected_sha256` 指定時はoptimistic concurrency guardとして利用する。 ### 7.4 process_run @@ -306,10 +320,11 @@ Ollamaに公開する現行Internal Tool catalogは次のとおりとする。 "stdin":"string|null" } ``` -- shell command line 1本ではなくexeとargsを分離する。 -- executableはcanonical path + SHA-256 approval必須。 -- shell category disabled時はcmd/powershell/pwshを拒否する。 -- cwdはfilesystem policy内でなければならない。 +- Shell Command 1本ではなくExecutableとargsを分離する。 +- `executable`はApproved Entry Executable/ShellのAlias、Runtime ComponentのLogical/Resolved Path、またはdevelopment Workspace ArtifactのLogical/Resolved Pathとする。Alias以外のrelative Logical Pathは`cwd`を基準に解決する。Approved Entry ExecutableのResolved PathをAliasの代用として直接指定することはできない。 +- Approved Entry Executable/ShellはResolved Path + SHA-256のExecutable Approval必須。Runtime ComponentとWorkspace Artifactは分類rootによって判定する。 +- External Unknown ExecutableをEntry Processとして直接起動しない。Descendant Processは個別分類せずLPAC/ACL/WFP/Job confinementを継承する。 +- cwdはEffective Filesystem Policy内でなければならない。 ### 7.5 task_update ```json { @@ -334,13 +349,13 @@ PlannerがDAG、不変条件、completed immutable、max_tasksを再検査して - mkdir は Write capabilityを要求し、`Directory.CreateDirectory` で親ディレクトリも含めて作成する。 - rmdir は Delete capabilityを要求し、**空ディレクトリのみ**削除する。非空の場合は `ToolValidationException` で拒否する。内容復元の複雑さとMemoryBackupLimit超過リスクを排除するため、再帰削除は行わない。 - list は Read capabilityを要求し、直下のエントリ一覧を返す。外部効果ではない。 -- 既存の PathPolicy / CanonicalPath / TaskAclManager (root継承ACE) / TaskResourceJournal / RecoveryManager 枠組みを利用する。新規作成ディレクトリは親の継承ACEを自動取得するため追加ACL操作は不要。 +- 既存の `ResolvedPath` / `PathPolicy` / `TaskAclManager`(root継承ACE)/ `TaskResourceJournal` / `RecoveryManager` 枠組みを利用する。新規作成directoryは親の継承ACEを自動取得するため追加ACL操作は不要。 - mkdir後crash → ディレクトリ削除、rmdir後crash → 空ディレクトリ再作成、でRecoveryする。 -### 7.7 Tool error表現 -既存のTool resultへ、`error_code`、`error_category`、`target_path`、`required_permission`、`retry_allowed`、`resolution`をoptional fieldとして保持する。これは独立したerror体系ではなく、既存の`SandboxPolicyException`と`ToolExecutionResult`の分類情報を失わずRuntime、Audit、localized messageへ渡す表現である。内部messageと利用者向けlocalized messageは分離し、内部Tool名は利用者向け結果へ含めない。 +### 7.7 Internal Tool error表現 +既存のInternal Tool resultへ、`error_code`、`error_category`、`target_path`、`required_permission`、`retry_allowed`、`resolution`をoptional fieldとして保持する。これは独立したerror体系ではなく、既存の`SandboxPolicyException`と`ToolExecutionResult`の分類情報を失わずRuntime、Audit、localized messageへ渡す表現である。内部messageと利用者向けlocalized messageは分離し、Internal Tool名は利用者向け結果へ含めない。 ### 7.8 ToolUseGuard -Taskには `requires_external_effect` をPlanner内部属性として持たせる。**Planning フェーズで LLM structured output により Task ごとに判定・設定される**(keyword heuristic ではない)。trueのTaskで、当該iteration中のtool callが0かつ完了証拠がない場合、LLMへ「未実行」を示すsystem-generated observationを追加して再試行する。同一失敗fingerprintが5回に達したらTaskをfailedにする。ただし、Policy EngineのdenyおよびSandbox backendの`UnauthorizedAccessException`は`RetryAllowed=false`のterminal Tool resultへ変換する。Runtimeはobserverへ結果を1回記録した後、同一Tool呼出しへ再投入せず、§8.4の条件付きreplanへ遷移する。 +Taskには `requires_external_effect` をPlanner内部属性として持たせる。**Planning フェーズで LLM structured output により Task ごとに判定・設定される**(keyword heuristic ではない)。trueのTaskで、当該iteration中のInternal Tool Callが0かつ完了証拠がない場合、LLMへ「未実行」を示すsystem-generated observationを追加して再試行する。同一失敗fingerprintが5回に達したらTaskをfailedにする。ただし、Policy EngineのdenyおよびSandbox backendの`UnauthorizedAccessException`は`RetryAllowed=false`のterminal Internal Tool resultへ変換する。Runtimeはobserverへ結果を1回記録した後、同一Internal Tool Callへ再投入せず、§8.4の条件付きreplanへ遷移する。 --- ## 8. Token Planner設計 ### 8.1 Task model @@ -360,6 +375,8 @@ Task RequiredTools: set ``` Taskは、完了に成功証跡が必要なTool名を`RequiredTools`として持つ。Planner structured outputの`required_tools`から構築し、Runtimeは集合内の全Toolが成功した場合だけTaskの操作を検証済みとする。read/search/list Taskは副作用不要でも対応Toolを持ち、純粋な説明Taskだけが空集合となる。`RequiresExternalEffect=true`では集合内の副作用Toolから外部効果も確認する。例えば`process_run`が必要なTask中の`file_edit`成功は完了根拠にしない。current Taskの集合に含まれない副作用ToolはBackendへdispatchせず、Task scope外のTool resultとしてLLMへ返してqueued Taskの先取りを決定的に防ぐ。scope外結果はAudit/LLM相関には残すが、次Taskのactive Execution Result contextへは引き継がない。 + +codingの編集・検証・失敗修正は同一Taskへまとめ、`file_edit`と`process_run`をRequiredToolsにする。両Toolを要求する検証Taskが、別の`file_edit` Taskへ直接または間接に依存するsplit planはこの規則に反するためPlanner validationで拒否し、具体的な理由を付けて1回だけ訂正させる。 ### 8.2 PERT計算 ```text ExpectedTokens = (O + 4*M + P) / 6.0 @@ -376,7 +393,7 @@ topological sort後にforward passで各Taskの累積expected token costを計 - 実績がPを超過しても即失敗にはせず、remaining Taskのestimate見直しをLLMへ要求できる。 - `max_tasks=32`超過提案は拒否し、既存Taskの統合をLLMへ求める。 - Budget Classは意味的Taskを分割する唯一の理由にしない。Taskが複数独立成果物を含む場合に分割する。 -- Policy denyは同一Tool呼出しへ再投入しない。許可済み能力だけで代替できる可能性がある場合、`running -> planning -> running` と遷移してreplanを最大3回まで試行できる。 +- Effective Sandbox Policy denyは同一Internal Tool Callへ再投入しない。許可済みcapabilityだけで代替できる可能性がある場合、`running -> planning -> running` と遷移してreplanを最大3回まで試行できる。 - completed TaskのID、状態、usageを保持する。failed Taskをdependency解決済みとして扱わない。 - 同一deny fingerprintの再発、計画未変更、実行可能Taskなし、structured replan失敗は即Failedとする。全Taskがcompletedの場合だけAgent Taskをcompletedにする。 - structured planが空または不正な場合は、設定された`think`を維持したまま訂正instruction付きで1回だけ再試行し、それでも失敗した場合はplanning failureとする。外部効果要否が不明なfallback graphは作成しない。 @@ -397,7 +414,7 @@ AgentSession CreatedAt / LastAccessAt ``` ### 9.2 Context assembly -順序を次に固定する。 +実行phaseの順序を次に固定する。 1. Fixed System Prompt 2. User System Prompt 3. locale instruction @@ -410,6 +427,8 @@ AgentSession 10. current Taskの`requires_external_effect`に基づく実行要件 11. recent Conversation +Planning/replanning phaseはread-onlyのTaskGraph生成契約を最初のSystem messageとして使用し、locale、Skill catalog、Sandbox capability summary、original MCP Task requestだけを組み立てる。file調査やInternal Tool実行を指示するFixed/User System PromptはPlanning契約と矛盾するため、このphaseへ混在させない。生成後のTask実行では上記1〜11の優先順位を適用する。 + 実行要件は、現在runningのTaskだけを実行してqueued Taskを先取りしないことをSystemおよび末尾のcurrent Task user messageで指示する。末尾messageにはoriginal MCP Task requestもexact値の参照元として併記する。外部効果が必要なTaskでは文章上の宣言、計画、JSON例を実行済みと扱わず、対応するInternal Toolを直ちに呼び出すよう指示する。成功済みのread結果はExecution Resultから再利用し、不要な再読込を避けてwrite/processへ進む。外部効果が不要なTaskにはToolを強制しない。未解決のTool失敗後にToolを呼ばない応答も同一失敗として数え、既定の同一失敗上限でfailedにする。 ### 9.3 Summary policy `num_ctx`に対する利用率が設定閾値を超えたとき、古いConversation/Execution Resultを要約する。利用率 = latest `prompt_eval_count` / `num_ctx`(累積 InputTokens ではない)。既定閾値は80%とする。次は削除・曖昧化してはならない。 @@ -422,7 +441,7 @@ AgentSession - 承認/Policyそのものではなく現在利用可能なcapability summary raw chain-of-thoughtは保存・要約対象としない。 --- -## 10. SandboxPolicy YAML設計 +## 10. User Sandbox Configuration・Resolution・Effective Policy設計 ### 10.1 schema概要 ```yaml schema_version: 1 @@ -453,21 +472,21 @@ ollama: planner: max_tasks: 32 sandbox: - filesystem: - read: [] - write: [] - execute: [] - delete: [] - process: - allow_shell: true - approvals: [] - max_processes: 16 + profile: development + workspaces: [] + mounts: [] + execution: + executables: [] + shells: [] network: - mode: deny - allow: [] + access: none + local_endpoints: [] + restricted_endpoints: [] resources: - memory_mb: 4096 - process_timeout_seconds: 1800 + profile: normal + memory_mb: + process_limit: + execution_timeout_seconds: file_edit: memory_backup_limit_mb: 16 limits: @@ -484,59 +503,90 @@ logging: directory: logs ``` base YAMLと同じdirectoryに`agent.local.yaml`が存在する場合、YAML nodeを読み取ってmapを再帰mergeし、scalarとsequenceはlocal nodeで置換してからtyped configurationへdeserializeする。unknown propertyを含むvalidationはmerge後に行い、全相対pathのbaseはbase YAMLのdirectoryとする。local file、そこから参照するUser System PromptおよびSkill directoryはRelease Bundleのpublish入力に含めない。 -### 10.2 Filesystem entry -各list itemはpath文字列とする。起動時に絶対canonical pathへ正規化して保持する。file/directoryは実在時に種別を記録し、未作成write targetは最も近い既存親directoryまで検査する。 -### 10.3 Process approval entry +### 10.2 Workspace・Mount・Profile +- `workspaces`はLogical Path文字列のsequenceとする。`readonly`はreadのみ、`development`はread/write/delete/Artifact executeへcompileする。 +- `mounts`は`path`と`access: read_only|read_write|read_write_delete`を持つ。readonly Profileではread_onlyだけを許可し、Mountにexecuteを付与しない。 +- relative Logical Pathはbase YAML directory基準。実在するlocal directoryだけを許可する。 +- 同一Resolved Rootはcapabilityを統合する。親の継承capabilityより狭いnested rootは拒否する。 +### 10.3 Executable Alias・Approval Store ```yaml -approvals: - - path: C:/Program Files/Git/cmd/git.exe - sha256: "<64 hex chars>" - - path: C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe - sha256: "<64 hex chars>" +execution: + executables: [python, git] + shells: [powershell] ``` -署名はaudit metadataとして取得可能だが、v1のallow判定はpath + SHA-256を必須条件とする。 +- AliasはApprovalではない。CLIが候補を提示し、人間が選択したResolved Executable PathとSHA-256をWindows user単位Storeへ固定する。 +- Store pathは`%LOCALAPPDATA%/AgentSandboxMcp/executable-approvals.json`。user-only DACL、WriteThrough、atomic replace、schema versionを持つ。 +- recordはAlias、Approved Entry Executable/Approved Shell Executable分類、Logical Path、Resolved Path、SHA-256、optional Runtime Root、承認日時を持つ。 +- Runtime RootはCLI提案後の明示確認対象とし、その配下のRuntime Componentは個別hashなしでEntry起動できる。 +- Aliasはcase-insensitiveに一意とし、Executable/Shell双方への重複指定を拒否する。ShellのResolved file nameは`cmd.exe|powershell.exe|pwsh.exe`だけを許可する。 ### 10.4 Network entry ```yaml network: - mode: allow - allow: + access: restricted + local_endpoints: [] + restricted_endpoints: - host: github.com ports: [443] - - host: api.github.com - ports: [443] ``` -`allow` modeでもlist外はdeny。`deny`ではlistを無視して全denyする。localhostも特別扱いしない。 +`none`は全deny、`local`は列挙portを`127.0.0.1`/`::1`へ展開、`restricted`はhostをTask開始時にIP snapshot化する。mode不一致または空endpointを拒否する。 +### 10.5 Resource Profile +`constrained=1024 MiB/4/300秒`、`normal=4096 MiB/16/1800秒`、`extended=8192 MiB/32/3600秒`とする。`memory_mb`、`process_limit`、`execution_timeout_seconds`の正数overrideはpreset値を置換する。 +### 10.6 Resolution Result +- Workspace/MountのLogical→Resolved mapping、config絶対path、semantic sandbox fingerprint、確認日時を`%LOCALAPPDATA%/AgentSandboxMcp/environment-resolutions/.json`へ保存する。 +- reparse chainを最終targetまで解決しcycle、missing target、非local filesystemを拒否する。確認後のfingerprintまたはResolved Path変化はserve/checkをfail-closedにする。 +- `sandbox resolve`はpreview、`sandbox confirm`だけがStoreを更新し、`sandbox show`はEffective Sandbox Policyをtext/JSON表示する。 + +### 10.7 Effective Sandbox Policy + +確認済みResolution Resultと現在のApproval Storeをcompileし、次を由来付きで保持する。 + +- Filesystem Root: `workspace`、`mount`、`approved_entry_executable`、`approved_shell_executable`、`runtime_root`、`os_runtime_root`の由来、Resolved Path、Filesystem Capability。 +- Entry Executable: Alias、分類、Resolved Path、SHA-256、optional Runtime Root。 +- Artifact RootとRuntime Root: Entry分類およびACL生成に使用するResolved Root。 +- Network: `none|local|restricted`とTask開始時にsnapshot化するendpoint。 +- Resource: Resource Profile名、override適用後のmemory/process/timeout。 +- baseline capability: 固定された`registryRead`と`lpacInstrumentation`。 + +同じResolved RootのFilesystem Capabilityは和集合にし、由来も併記する。親rootから継承するCapabilityより狭いnested rootは表現不能としてcompileを拒否する。`sandbox show`はこのcompile結果をtextまたはJSONで表示するが、Storeや設定を変更しない。 --- ## 11. Windows強制設計 -### 11.1 AppContainer +### 11.1 Less Privileged AppContainer - Application Profileを固定名で作成/取得する。 - 起動時にprofile SIDを解決する。 - Taskごとにprofileを作り直さない。 -- AppContainer外の通常user tokenに戻るbreakawayを許可しない。 +- standard `CreateProcess`の`PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES`に加え、`PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY=PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT`を指定してLPAC tokenを生成する。 +- Experimental CreateProcessInSandbox、AppLocker、WDACへ依存しない。LPAC生成または互換性検査失敗時に通常AppContainerへfallbackしない。 +- baseline capabilityは`registryRead`と、PowerShell Runtimeが起動時のETW provider登録に必要とする`lpacInstrumentation`とする。Registry writeと`lpacCom`は付与しない。Network capabilityはEffective Network Policyが必要な場合だけ付与する。`lpacInstrumentation`追加後もRegistry writeとCOM activationの実拒否をWindows Security suiteで検証する。 +- AppContainer外の通常user tokenに戻るbreakawayを許可しない。DescendantはLPAC tokenを継承する。 +- `CreateProcess`へBrokerのenvironment blockをそのまま継承しない。Windows/runtime起動に必要なsystem/profile path、`ComSpec`、System32だけのPATH、Task `cwd`を指すTEMP/TMPと`DOTNET_CLI_HOME`、非対話かつnetwork非依存の.NET初回起動・telemetry・workload resolver値、Process Tree外の共有MSBuild server/node reuseを無効化する値から成る明示的なUnicode environment blockを渡し、任意のtoken、password、SSH/API credential環境変数を除外する。profile path自体はFilesystem Capabilityを付与しない。 ### 11.2 NTFS ACL -- Policy Engineが許可pathから必要ACEを導出する。 -- 許可pathがdirectoryの場合、そのrootへ`ContainerInherit | ObjectInherit`付きのApplication SID ACEを1件だけtransactionalに追加し、既存およびTask中に作成されたdescendantへ継承させる。descendantごとのDACL列挙・snapshot・明示ACE追加は行わない。 +- Effective Sandbox PolicyのFilesystem RootからApplication SID ACEを導出する。 +- read-only directoryはdirectory traverse/list/readとfile readを別ACEとして付与し、fileのExecuteFileを含めない。execute rootだけがfile read/execute ACEを取得する。 +- development Workspace/Artifact Rootはread/write/delete/execute、Runtime RootとResolved `%SystemRoot%/System32`はread/execute、Mountは指定accessのみを取得する。 +- 許可pathがdirectoryの場合、rootへのinheritance ACEで既存およびTask中に作成されたdescendantを覆い、descendantごとのDACL列挙は行わない。 - 許可pathがfileの場合は、そのfileだけへ継承なしACEを追加する。異なる権限を持つnested rootは別scopeとして逆順cleanupする。 - protected DACLを持つdescendantの継承保護は解除しない。そのpathはOS拒否のままとし、Sandbox権限を暗黙に拡張しない。 - rootの元security descriptorをRecovery Journalへ1件記録する。 - Task終了で追加分を除去し、元状態との整合を検査する。 ### 11.3 Job Object - TaskごとにJob Objectを1つ作成する。 -- root sandbox processをJobへ割り当てる。descendantを同一Jobへ継承させる。 +- Entry ProcessをJobへ割り当て、全Descendantを同一Job chainへ継承させる。 - breakawayを許可しない。 - process count / memory limitをJobへ設定する。 - `KILL_ON_JOB_CLOSE`相当を利用し、application終了時にprocess treeを残さない。 -### 11.4 Process approval -Sandbox Brokerは実行直前にcanonical pathを取得し、ファイルを開いた状態でSHA-256を計算し、approvalと一致確認してから起動する。TOCTOU低減のため、検証対象と実行対象のpath解決がずれないよう実装時にWin32のfile identity確認を追加する。具体API選択は実装時に確定する。 +### 11.4 Entry Process分類・Executable Approval +- AliasはApproval StoreのApproved Entry Executable/Shellへ解決し、起動直前とsuspended生成後・resume前にResolved PathとSHA-256を再検証する。 +- Runtime Componentは確認済みRuntime Root containment、Workspace Artifactはdevelopment Artifact Root containmentで分類する。MountおよびExternal Unknown Executableからの直接Entry起動は拒否する。 +- Descendant Processは個別分類・Approvalを行わず、LPAC/ACL/WFP/Job confinementをSecurity Boundaryとする。`PROCESS_CREATION_CHILD_PROCESS_RESTRICTED`は使用しない。 ### 11.5 WFP方針 WFP実装は複雑な汎用Firewallにしない。設計上の変換規則だけを固定する。 -1. `deny`ならApplication Profile/Task processに対するoutbound blockを作成する。 -2. `allow`ならdefault blockを置き、列挙hostをTask開始時にDNS解決する。 +1. `none`ならnetwork capabilityを付与せず、Application Profile/Sandbox Process Treeへoutbound blockを作成する。 +2. `local`は列挙portをIPv4/IPv6 Loopbackへ、`restricted`は列挙hostをTask開始時にIP解決する。 3. 解決したIP集合とportの組にだけallow filterを作成する。 4. Task実行中はそのIP snapshotを使用し、複雑な動的DNS追随は行わない。 5. DNS解決失敗はdeny。 6. Task終了でTask固有WFP filterを削除する。 -7. localhostはallow-listに明示されない限り許可しない。 +7. LoopbackもEffective Network Policyに明示されない限り許可しない。LANをlocalへ含めない。 具体的なWFP layer、sublayer、filter condition、transaction APIは実装時にCodexがWindows SDKを確認して決定し、Security Integration Testで仕様を検証する。 ### 11.6 Named Pipe Sandbox workerとのIPCが必要な場合はtask固有Named Pipeを使用し、DACLをApplication SIDとBrokerだけに限定する。TCP localhostを内部IPCに使わない。 @@ -546,7 +596,7 @@ Sandbox workerとのIPCが必要な場合はtask固有Named Pipeを使用し、D | 性質 | 本システムでの意味 | | --- | --- | | Atomicity | Task Resource適用が途中失敗した場合、適用済み変更をrollbackする。 | -| Consistency | SandboxPolicyとSecurity Invariantを満たす状態だけを実行可能状態とする。 | +| Consistency | Effective Sandbox PolicyとSecurity Invariantを満たす状態だけを実行可能状態とする。 | | Isolation | Task固有ResourceをTask IDで識別し、他Task cleanupで誤削除しない。 | | Durability | Task Resource自体の永続化ではなく、cleanupに必要なjournalをcrashに耐えて保持する。 | ### 12.2 状態機械 @@ -625,7 +675,7 @@ call ID、session ID、task ID、Planner Task ID、phase、iteration、replan番 ### 15.3 Audit JSONL Auditはappend-only JSONLを採用する。1 event = 1 line。 ```json -{"ts":"...","event":"process.start","session_id":"...","task_id":"...","exe":"...","sha256":"...","args":["..."],"decision":"allow"} +{"ts":"...","event":"process.start","session_id":"...","task_id":"...","exe":"...","sha256":"...","arguments":"redacted","argument_count":2,"decision":"allow"} {"ts":"...","event":"file.write","task_id":"...","path":"...","before_sha256":"...","after_sha256":"..."} {"ts":"...","event":"policy.deny","task_id":"...","kind":"network","target":"example.com:443","reason":"not_allowlisted"} ``` @@ -672,7 +722,7 @@ closed Sessionは再利用しない。プロセス再起動後に以前のSessio - locale baseline/placeholder - Ollama endpoint URI - Sandbox path canonicalization -- process approval path/hash format +- Executable Approval StoreのAlias/Logical Path/Resolved Path/hash format - network host/port syntax - resource limit range - Fixed/User prompt file state @@ -684,8 +734,8 @@ closed Sessionは再利用しない。プロセス再起動後に以前のSessio | TokenPERT | O<=M<=P<=budget、式のunit test | | TokenCPM | 既知DAGに対するcritical path unit test | | Filesystem | allow/deny/reparse point integration test | -| Process | hash mismatch、path mismatch、shell toggle | -| Network | deny loopback、allow-list host/port | +| Process | Alias不明、Approval不足、hash/Resolved Path変更、Entry分類拒否、LPAC不一致 | +| Network | noneのLoopback、local/restrictedのPolicy外address/port | | Recovery | 各transaction stateで強制crashして再起動cleanup | | MCP | stdio purity、Streamable HTTP loopback、GET 405 | | Localization | fallback、unknown key fatal、placeholder validation | @@ -693,26 +743,30 @@ closed Sessionは再利用しない。プロセス再起動後に以前のSessio ## 19. Security Test Matrix | ID | ケース | 期待 | | --- | --- | --- | -| ST-01 | read許可file | 成功 | -| ST-02 | read未許可file | ACCESS_DENIED | -| ST-03 | write許可file | 成功 | -| ST-04 | delete権限なしdelete | 拒否 | +| ST-01 | readonly Workspaceのread | 成功 | +| ST-02 | readonly Workspaceのwrite/delete/execute | すべて拒否 | +| ST-03 | development Workspaceのread/write/delete | 成功 | +| ST-04 | development Workspace ArtifactのEntry起動 | Approvalなしで成功 | | ST-05 | junctionで許可外へ脱出 | 拒否 | -| ST-06 | symlink chainで許可外へ脱出 | 拒否 | +| ST-06 | symlink chain/cycle | 許可外は拒否、cycleはfail-closed | | ST-07 | 承認exe/hash一致 | 成功 | | ST-08 | exe更新/hash不一致 | 拒否 | -| ST-09 | shell disabledでPowerShell | 拒否 | -| ST-10 | 子が未承認exe起動 | 拒否 | -| ST-11 | Job close | 全descendant終了 | -| ST-12 | network denyで127.0.0.1 | 拒否 | -| ST-13 | network allowで許可host:port | 成功 | -| ST-14 | network allowで未許可port | 拒否 | +| ST-09 | Approved Shell分類 | cmd.exe/powershell.exe/pwsh.exeだけ直接起動可 | +| ST-10 | Descendantが個別未承認Executableを起動 | 同じLPAC identity/Job内で成功 | +| ST-11 | Job close/breakaway/process/memory/timeout | breakaway不可、制限時は全Process Tree停止 | +| ST-12 | Network noneで127.0.0.1 | 拒否 | +| ST-13 | Network local/restrictedで許可address:port | 成功 | +| ST-14 | Network local/restrictedでPolicy外port | 拒否 | | ST-15 | MCPからpolicy変更試行 | schema/機能上不可能 | -| ST-16 | Ollama tool arg path脱出 | Policyで拒否 | +| ST-16 | Ollama Internal Tool CallのResolved Path脱出 | Policyで拒否 | | ST-17 | crash after ACL apply | 再起動cleanup | | ST-18 | crash after WFP apply | 再起動cleanup | -| ST-19 | malformed locale | 起動失敗 | -| ST-20 | non-loopback HTTP bind | 起動失敗 | +| ST-19 | LPAC baselineでRegistry read/writeとCOM | readのみ成功、write/COM拒否 | +| ST-20 | External Unknown/System32 Executableの直接Entry | Approvalなしでは拒否 | +| ST-21 | malformed locale | 起動失敗 | +| ST-22 | non-loopback HTTP bind | 起動失敗 | +| ST-23 | Approval/Resolution Store | user-only DACL、atomic replace、破損record拒否 | +| ST-24 | config fingerprint/Resolved Path/hash変更 | 再確認または再承認までfail-closed | --- ## 20. エラー分類 | Category | 例 | Task結果 | Retry | @@ -724,7 +778,7 @@ closed Sessionは再利用しない。プロセス再起動後に以前のSessio | tool_failure | process nonzero/patch mismatch | running→replan | 最大同一5 | | resource_limit | timeout/memory/process limit | failedまたはreplan | 原則No | | recovery_failure | ACL/WFP cleanup不能 | application fatal | No | -errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MCPにはlocalized textを返す。 +errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MCPにはlocalized textを返す。Sandbox Configuration/Resolution/Approvalの失敗は少なくとも`configuration.invalid`、`sandbox.resolution_unconfirmed`、`sandbox.resolution_changed`、`executable.approval_missing`、`executable.resolution_changed`、`executable.hash_changed`、`executable.shell_unsupported`、`process.lpac_unsupported`を既存分類へ統合し、category、対象、解決方法を保持する。 --- ## 21. 実装順序 | Phase | 実装 | 完了条件 | @@ -735,7 +789,7 @@ errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MC | 4 | Internal Tool dispatcher | tool schemaとToolUseGuard | | 5 | Filesystem/Process Sandbox | ACL/AppContainer/Job integration test | | 6 | Resource Transaction/Recovery | 強制crash recovery test | -| 7 | WFP | deny/allow-list integration test | +| 7 | WFP | none/local/restricted integration test | | 8 | Vision/Streamable HTTP | image optional、loopback HTTP | | 9 | Audit/Hardening | security matrix全PASS | | 10 | Release/Operational E2E | self-contained Bundle単独起動、stdio/HTTP実利用 | @@ -744,7 +798,7 @@ errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MC - `dotnet publish -c Release -r win-x64 --self-contained true` のfolder publishを配布元とする。 - Bundleはversion付きZIP、展開済みdirectory、file manifest、SHA-256 checksumから成る。 - 起動時の相対pathは実行ファイルdirectoryを基準に解決し、ソースツリーを参照しない。 -- 必須Gateはlocal Ollamaだけで実行し、cloud modelは任意互換試験に限定する。 +- 現行Operational E2Eは配布既定の`deepseek-v4-flash:cloud`を使用する。rate limit、接続失敗、model unavailableをapplication PASSとして扱わない。 --- ## 22. Codex実装制約 - 新規NuGet dependencyを勝手に追加しない。追加時は標準ライブラリで不能な理由を示す。 @@ -820,17 +874,18 @@ errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MC | ollama.options.* | typed runtime options | | ollama.extra_options.* | future passthrough options | | planner.max_tasks | 32 default | -| sandbox.filesystem.read | read allow-list | -| sandbox.filesystem.write | write allow-list | -| sandbox.filesystem.execute | execute path allow-list | -| sandbox.filesystem.delete | delete allow-list | -| sandbox.process.allow_shell | shell toggle | -| sandbox.process.approvals | path+sha256 approvals | -| sandbox.process.max_processes | Job process limit | -| sandbox.network.mode | deny/allow | -| sandbox.network.allow | host/port list | -| sandbox.resources.memory_mb | Job memory limit | -| sandbox.resources.process_timeout_seconds | process timeout | +| sandbox.profile | readonly/development Workspace preset | +| sandbox.workspaces | Workspace Logical Path list | +| sandbox.mounts | Mount Logical Path + semantic access | +| sandbox.execution.executables | Approved Entry Executable Alias list | +| sandbox.execution.shells | Approved Shell Executable Alias list | +| sandbox.network.access | none/local/restricted | +| sandbox.network.local_endpoints | Loopback port list | +| sandbox.network.restricted_endpoints | host/port list | +| sandbox.resources.profile | constrained/normal/extended | +| sandbox.resources.memory_mb | optional memory override | +| sandbox.resources.process_limit | optional Job process override | +| sandbox.resources.execution_timeout_seconds | optional execution timeout override | | file_edit.memory_backup_limit_mb | memory/temp threshold | | localization.directory | locale directory | | localization.fallback | en | @@ -875,7 +930,7 @@ errorは内部codeとlocalized messageを分離し、ログ/auditにはcode、MC - [ ] process hashは実行直前に再計算しているか。 - [ ] shell有効でもFilesystem/Network policyを迂回できないか。 - [ ] Job breakawayがないか。 -- [ ] Network allow-list外がdefault denyか。 +- [ ] Effective Network Policy外がdefault denyか。 - [ ] WFP/ACL変更前にjournalを書いているか。 - [ ] 起動時Recoveryがserveより前か。 - [ ] file rollback後にhash検証しているか。 diff --git a/doc/Specifications.md b/doc/Specifications.md index 9f6e9b8..79cb653 100644 --- a/doc/Specifications.md +++ b/doc/Specifications.md @@ -77,12 +77,22 @@ IPA「厳密な仕様記述入門」の考え方に従い、課題・仕様・ | Token CPM | TaskGraph上でexpected token costを重みとしてcritical pathを求めるモデル。 | | Token Budget Class | 1K~128KのTask単位token予算階級。Ollama `num_ctx`とは別概念。 | | Session | 会話、TaskGraph、Execution Resultをメモリ上に保持する論理単位。 | -| SandboxPolicy | Filesystem、Process、Network、Resourceの許可範囲を表すホワイトリスト型ポリシー。 | -| Sandbox | SandboxPolicyをWindows機構で強制したプロセス実行環境。 | +| User Sandbox Configuration | Workspace、Mount、Execution、Network、Resourceを利用者の意図で表すsemantic設定。 | +| Sandbox Environment Resolution | Logical Path、Executable Alias、Runtime等をHost上のResolved Pathへ解決する処理。 | +| Effective Sandbox Policy | ResolutionとExecutable Approvalの結果から生成され、Policy Engineが使用する解決済みホワイトリスト。従来のSandboxPolicyはこの意味でのみ使用する。 | +| Sandbox | Effective Sandbox PolicyをWindows機構で強制したExecution Environment。 | | Sandbox Broker | プロセス起動・許可判定・Windows強制機構の調停を担当する信頼境界。 | -| Application Profile | アプリケーション固定のAppContainer profile。Taskごとには作成・削除しない。 | +| Application Profile | アプリケーション固定のLess Privileged AppContainer (LPAC) profile。Taskごとには作成・削除しない。 | | Task Resource | Task中だけ存在するACL、WFP filter、Job Object、一時ファイル等。 | -| Approval | 実行ファイルのcanonical pathとSHA-256を信頼対象として登録する操作。 | +| Executable | Windows上でProcessとして起動可能な実行対象。Internal Toolとは区別する。 | +| Executable Alias | 利用者がHost Executableを指定する人間向け名称。Alias解決だけではApprovalにならない。 | +| Approved Entry Executable | BrokerがEntry Processとして直接起動できる、Executable Approval済みHost Executable。 | +| Executable Approval | Entry ExecutableのResolved PathとSHA-256を人間が信頼対象として登録する操作。 | +| Runtime / Runtime Root / Runtime Component | Entry Executableの動作に必要な実行環境、そのFilesystem root、その配下のExecutable/resource。 | +| Workspace Artifact | development Workspace内で生成または配置され、Host Executable ApprovalなしでSandbox Entry Processとして起動できる実行物。 | +| Shell | command interpreterとして動作するExecutable category。 | +| Sandbox Process Tree | Brokerが起動したEntry Processと、同じconfinementを継承する全Descendant Process。 | +| Logical Path / Resolved Path | 利用者が指定したpathと、reparse resolution後にPolicy評価へ使用する実体path。 | | Fixed System Prompt | 実行ファイルに組み込まれ、利用者が変更できない最上位指示。 | | User System Prompt | 設定で指定可能だがFixed System Promptを上書きできない追加指示。 | | Skill | LLMへの補助指示を記述したMarkdown。実行権限そのものを与えない。 | @@ -100,8 +110,8 @@ IPA「厳密な仕様記述入門」の考え方に従い、課題・仕様・ | UC-02 | MCP Client | Task状態を照会する | 状態と、完了時には最終回答をテキストで得る | | UC-03 | 利用者 | 画像付き依頼を送信する | Vision対応modelなら処理される | | UC-04 | 利用者 | 言語を指定して起動する | CLI/診断/MCP応答/LLM最終回答が指定言語になる | -| UC-05 | 管理者 | SandboxPolicyを設定する | 未許可能力はdenyされる | -| UC-06 | 管理者 | 実行ファイルを承認する | pathとSHA-256一致時だけ起動できる | +| UC-05 | 利用者 | User Sandbox Configurationを設定・確認する | semantic設定からEffective Sandbox Policyが可視化され、未許可能力はdenyされる | +| UC-06 | 利用者 | Entry Executableを承認する | Alias、Resolved Path、SHA-256一致時だけBrokerから直接起動できる | | UC-07 | Agent | 計画を作成・更新する | TaskGraphがDAGを維持しToken CPMを再計算する | | UC-08 | Agent | Internal Toolを呼ぶ | Policy検査後にのみ副作用が発生する | | UC-09 | システム | 異常終了から復旧する | 残留Task Resourceを除去してから受付開始する | @@ -171,9 +181,16 @@ v1のCLI surfaceは次に限定する。 ```text agent-mcp [serve] [--config ] [--lang ] [--transport ] agent-mcp check [--config ] [--lang ] +agent-mcp executable resolve [--path ] --config +agent-mcp executable approve [--path ] [--runtime-root |--no-runtime] [--yes] --config +agent-mcp executable list --config +agent-mcp executable revoke [--yes] --config +agent-mcp sandbox resolve --config +agent-mcp sandbox confirm [--yes] --config +agent-mcp sandbox show [--format ] --config ``` -command省略時、および先頭引数がoptionの場合は `serve` として扱う。CLIは起動方式と表示言語を変更するためのI/Fであり、SandboxPolicyやOllama sampling値を個別overrideするI/Fにしない。 +command省略時、および先頭引数がoptionの場合は `serve` として扱う。`resolve`、`list`、`show`、`check`はread-onlyとする。`approve`、`revoke`、`confirm`は差分表示後の対話確認を既定とし、明示的な`--yes`を許可してAuditへ記録する。MCP ClientおよびLLMからこれらの変更操作を呼び出せない。 ### 5.7 MCP 2026-07-28とTasks Extension @@ -319,22 +336,25 @@ Context要約を開始する利用率閾値は設定可能でなければなら - frontmatterなしのSkillは推測せずrejectする。malformed YAML、duplicate skill nameは起動/check時fatalとする。 - Progressive Disclosure: Stage 1 Discovery (name + descriptionのみ) → Stage 2 Selection (LLM) → Stage 3 Instructions Load (選択SKILL.md本文) → Stage 4 Resources (scripts/references/assets)。 - Skill選択はLLMにより行う。選択可能集合は設定されたSkill directoryに限定する。Runtimeは選択結果を検証し、unknown Skill名を読み込まない。 -- Skillロードは権限付与を意味しない。`allowed-tools` metadataはSandbox permissionとして解釈しない。 -- Skillに `scripts/` が含まれていても自動実行許可しない。実行には通常どおりFilesystem execute capability、Process Approval、path + SHA-256、Sandbox Brokerを必須とする。 +- Skillロードはcapability付与を意味しない。`allowed-tools` metadataはSandbox Capabilityとして解釈しない。 +- Skillに `scripts/` が含まれていても自動実行許可しない。Entry起動には通常どおりEffective Sandbox Policy上のFilesystem execute capability、Entry Process分類、必要なExecutable Approval、Sandbox Brokerを必須とする。 - Skill directory内resourceについてcanonicalization、reparse point検査、Skill root外へのescape禁止を行う。 - Skill選択・本文ロードはInternal Toolとして公開せず、Agent Runtime / Prompt Context Managementの責務として実装する。 - Internal Toolの具体的なcatalogとschemaは設計事項とする。MCP Clientへ公開せず、Internal Toolによる副作用は必ずPolicy検査を通過しなければならない。 --- -## 11. SandboxPolicy仕様 +## 11. User Sandbox Configuration・Effective Sandbox Policy仕様 ### 11.1 基本原則 -- **ホワイトリスト方式**を採用する。未指定能力はdenyする。 -- 子・孫プロセスは親より強い権限を取得できない。 -- LLMはSandboxPolicyを変更できない。 -- Policy変更は設定変更とアプリ再起動を必要とする。 +- User Sandbox Configurationは`readonly`または`development` Profile、Workspace、Mount、Execution、Network、Resourceを表す。 +- Sandbox Environment ResolutionとExecutable Approval Resolutionの成功後だけEffective Sandbox Policyを生成する。 +- Effective Sandbox PolicyはResolved Filesystem Rootのcapabilityと由来、Approved Entry Executable、Runtime Root、Artifact Root、Network endpoint、Resource limit、固定baseline capabilityを利用者が確認可能な形で保持する。 +- **ホワイトリスト方式**を採用し、未指定能力はdenyする。 +- Descendant ProcessはEntry Processと同じLPAC、Filesystem、Network、Job confinementを継承し、親より強いcapabilityを取得できない。 +- LLMとMCP ClientはUser Sandbox Configuration、Resolution Result、Executable Approval、Effective Sandbox Policyを変更できない。 +- User Sandbox Configuration変更はconfig別Resolution Resultの再確認とアプリ再起動を必要とする。 - v1で開放可能なのはFilesystem、Process、Network、Resourceとする。その他能力はdenyとする。 ### 11.2 Filesystem Policy @@ -343,37 +363,43 @@ Context要約を開始する利用率閾値は設定可能でなければなら | --- | --- | | read | ファイル内容読取、directory列挙。 | | write | create、overwrite、renameを含む。deleteは含まない。 | -| execute | 対象ファイルを実行対象として利用できる。Process approvalも別途必要。 | +| execute | 対象fileをExecutable imageとして利用できる。Entry Processの分類に応じてExecutable Approvalも必要。 | | delete | ファイルまたはdirectoryを削除できる。 | | ID | 要件 | | --- | --- | -| FS-01 | 指定粒度はdirectoryまたはfile。workspaceという特別な権限単位は持たない。 | -| FS-02 | pathはcanonicalize後にPolicy評価する。 | -| FS-03 | symlink、junction、その他reparse pointを経由した許可範囲外への脱出を禁止する。 | +| FS-01 | Workspace Root、Mount Root、Runtime Root、Artifact Root、個別Executable fileをFilesystem Rootへ変換する。 | +| FS-02 | Logical Pathはreparse chainとcycleを検査してResolved Pathへ変換した後にPolicy評価する。 | +| FS-03 | 未確認または確認後に解決先が変化したLogical Pathを使用せず、許可範囲外への脱出を禁止する。 | | FS-04 | write権限だけではdeleteできない。 | | FS-05 | ACLとAppContainerの両方で強制可能な形にする。 | | FS-06 | ディレクトリ作成 (mkdir) はwrite権限で許可する。ディレクトリ削除 (rmdir) はdelete権限で許可するが、空ディレクトリのみとする。ディレクトリ列挙 (list) はread権限で許可する。 | | FS-07 | renameはsource/destination双方にwrite権限を要求する。destination既存時は拒否する。cross-volume renameは不支持とする。 | +| FS-08 | readonly ProfileのWorkspaceはreadのみ、development ProfileのWorkspaceはread/write/deleteおよびWorkspace Artifact実行を許可する。 | +| FS-09 | Mount accessはread_only、read_write、read_write_deleteのいずれかとし、MountからのExecutable起動を許可しない。 | +| FS-10 | read Filesystem CapabilityをWindows ACL execute permissionへ暗黙変換しない。 | ### 11.3 Process Policy | ID | 要件 | | --- | --- | -| PRC-01 | 実行対象はcanonical pathとSHA-256が承認記録と一致しなければならない。 | -| PRC-02 | 実行ファイル更新でhashが変化した場合は自動許可せず再承認を要求する。 | -| PRC-03 | path変更も再承認を要求する。 | -| PRC-04 | cmd.exe、powershell.exe、pwsh.exeは機能上デフォルト許可とするが、承認照合を免除しない。 | -| PRC-05 | shell categoryは設定で禁止可能。 | -| PRC-06 | すべてのdescendant processに同等以下のSandboxPolicyを適用する。 | -| PRC-07 | 実行前判定はSandbox Brokerが行う。 | +| PRC-01 | Approved Entry ExecutableとApproved Shell ExecutableはAlias、Resolved Path、SHA-256がExecutable Approvalと一致しなければならない。 | +| PRC-02 | Entry Executable更新でhashが変化した場合は自動許可せず再承認を要求する。pathまたはreparse解決先変更も同様とする。 | +| PRC-03 | 確認済みRuntime Root内のRuntime Componentは個別hashなしでEntry Processとして起動可能とする。 | +| PRC-04 | development Workspace内のWorkspace ArtifactはHost Executable ApprovalなしでEntry Processとして起動可能とする。 | +| PRC-05 | External Unknown ExecutableをBrokerからEntry Processとして直接起動してはならない。 | +| PRC-06 | cmd.exe、powershell.exe、pwsh.exeをShellとして分類し、直接起動にはExecutable Approvalを要求する。Shellの許可は追加capabilityを付与しない。 | +| PRC-07 | Descendant Processは個別Executable Approvalを要求せず許可するが、同じLPAC token、ACL、WFP、Job Objectを継承しなければならない。 | +| PRC-08 | 全DescendantをJob chain内へ閉じ込め、breakawayを許可しない。 | +| PRC-09 | Entry Processの分類とApproval判定はSandbox Brokerが実行前に行う。 | ### 11.4 Network Policy -- Network modeは `deny` と `allow` の2値とする。 -- `deny` はlocalhostを含む全通信を拒否する。 -- `allow` はhost/port allow-listに一致するoutboundだけを許可する。 -- allow-listが空の `allow` は実質denyとして扱い、全開放を意味しない。 +- Network Access Modeは `none`、`local`、`restricted` の3値とする。 +- `none` はLoopbackを含む全通信を拒否する。 +- `local` は列挙portに対するIPv4/IPv6 Loopbackだけを許可し、LANを含めない。 +- `restricted` は列挙host/portに一致するoutboundだけを許可する。 +- endpointが空またはmodeと矛盾する設定は拒否し、全開放を意味するfallbackを持たない。 - host名はhostsファイルに近い単純な列挙思想で扱い、汎用Firewall言語は実装しない。 ### 11.5 Resource Policy @@ -385,6 +411,8 @@ Context要約を開始する利用率閾値は設定可能でなければなら | wall-clock timeout | TaskまたはProcessの停止条件として設定可能。 | | child process | 親と同等以下の制約下でのみ許可。 | +Resource Profileは`constrained = 1024 MiB/4 Process/300秒`、`normal = 4096 MiB/16 Process/1800秒`、`extended = 8192 MiB/32 Process/3600秒`とし、各正数値を個別override可能とする。 + ### 11.6 v1で明示denyする能力 - Registry write @@ -411,7 +439,9 @@ Context要約を開始する利用率閾値は設定可能でなければなら ## 13. Application Profile・Task Resource仕様 -- AppContainer profileはapplication固定とする。 +- Application Profileはapplication固定のLPACとし、`ALL APPLICATION PACKAGES`由来の既定accessを除外する。 +- 全Profileの固定baselineとしてRegistry readとSandbox Process Tree自身のinstrumentation registrationを許可し、Registry writeとCOM capabilityはdenyする。 +- Resolved `%SystemRoot%/System32`をOS Runtime RootとしてSandbox Process Treeへread/execute公開する。Brokerからの直接Entry起動は別途Executable Approvalを要求する。 - Task終了時にApplication Profileを削除しない。 - Task固有ACL、WFP filter、Job Object、一時ファイル等はTask Resourceとして管理する。 - Task Resourceは正常終了・失敗・cancelのいずれでも残留させない。 @@ -441,13 +471,15 @@ Context要約を開始する利用率閾値は設定可能でなければなら ## 15. Configuration仕様 - 主設定形式はYAML。 -- `schema_version` を必須とする。 +- `schema_version: 1` を必須とする。本改訂前の`filesystem`および`process.approvals`形式は破壊的に廃止し、移行方法付きで拒否する。 - 設定変更の反映には再起動を必須とする。 - Hot Reloadは明示的に非対応。 - CLIの `--config` で設定ファイルpathを選択可能。 - base設定と同じdirectoryに `agent.local.yaml` が存在する場合は任意overrideとして読み込む。mapは再帰mergeし、scalarと配列はlocal側で置換する。merge後の設定全体をvalidationし、相対pathはbase設定directoryを基準に解決する。 - Release Bundleは既定設定だけを配布し、利用者のlocal設定、User System Prompt、Skillをpublishで上書きしてはならない。 -- 起動時にschema、path、hash approval、locale、policyの静的validationを行う。 +- 起動時にschema、Resolution Result、Executable Approval、locale、Effective Sandbox Policyのvalidationを行う。 +- Executable Approval StoreはWindows user単位のuser-only領域へ保存し、Release Bundleへ含めない。 +- Workspace/Mountの確認済Resolution Resultはconfig別のuser-only領域へ保存し、config fingerprintまたは解決先変更時はserveをfail-closedにする。 --- @@ -457,7 +489,7 @@ Context要約を開始する利用率閾値は設定可能でなければなら | --- | --- | | Application Log | stderrに出力可能。stdio時stdoutへログを出してはならない。ファイルにも保存する。 | | LLM Log | promptとresponseを記録する。call、Session、Task、Planner Task、phase、iteration、replan、model、token usageを相関できなければならない。secretを継承しない前提でも、ログ出力先は保護する。 | -| Audit Log | 通常ログと分離する。Task lifecycle、Tool、process、args、exit code、file write、delete、denied access、network rule、recoveryをSession/Taskと相関して記録する。 | +| Audit Log | 通常ログと分離する。Task lifecycle、Internal Tool Call、Sandbox Process、秘密情報を除いたargument metadata、exit code、file write、delete、denied access、network rule、recoveryをSession/Taskと相関して記録する。argument本文、画像、Base64、credentialは記録しない。 | | Token Metrics | Task/Call単位のinput/output/total tokenを記録する。 | --- @@ -481,17 +513,18 @@ Context要約を開始する利用率閾値は設定可能でなければなら | ID | 不変条件 | | --- | --- | | SEC-INV-01 | LLMの出力だけでは権限を増加できない。 | -| SEC-INV-02 | MCP ClientはSandboxPolicyを変更できない。 | +| SEC-INV-02 | MCP ClientはUser Sandbox Configuration、Resolution Result、Executable Approval、Effective Sandbox Policyを変更できない。 | | SEC-INV-03 | descendant processは親より強い権限を持てない。 | | SEC-INV-04 | 未許可Filesystem pathへR/W/E/Deleteできない。 | -| SEC-INV-05 | 承認hashと一致しない実行ファイルを起動できない。 | -| SEC-INV-06 | Network denyではlocalhostを含め通信できない。 | -| SEC-INV-07 | Network allowでもallow-list外host/portへ通信できない。 | +| SEC-INV-05 | 承認hashと一致しないHost ExecutableをBrokerからEntry Processとして直接起動できない。 | +| SEC-INV-06 | Network noneではLoopbackを含め通信できない。 | +| SEC-INV-07 | Network local/restrictedでもEffective Network Policy外のaddress/portへ通信できない。 | | SEC-INV-08 | CredentialをSandboxへ暗黙継承しない。 | | SEC-INV-09 | Fixed System Promptは利用者入力で置換できない。 | | SEC-INV-10 | Task終了後にTask Resourceを残さない。 | | SEC-INV-11 | Recovery未完了状態でMCP受付を開始しない。 | | SEC-INV-12 | stdio stdoutにはMCP protocol以外を書かない。 | +| SEC-INV-13 | Sandbox Process TreeのどのProcessもEffective Sandbox Policy外のresourceまたはcapabilityを取得できない。 | --- @@ -505,7 +538,7 @@ Context要約を開始する利用率閾値は設定可能でなければなら | NFR-04 | Maintainability | 外部MCP Toolを2個に固定し、内部責務を分離する。 | | NFR-05 | Dependency Control | 標準ライブラリで実現可能な機能に新規依存を追加しない。 | | NFR-06 | Portability | v1はWindows 11 x64専用。Windows機構を抽象化し過ぎない。 | -| NFR-07 | Observability | Task、Tool、Process、Token、Policy denialを追跡可能にする。 | +| NFR-07 | Observability | Task、Internal Tool Call、Sandbox Process、Token、Effective Sandbox Policy denialを追跡可能にする。 | | NFR-08 | Localization | 英語・日本語を標準提供し、追加localeをコード変更なしで導入可能。 | | NFR-09 | Readability | 設定、ログ、仕様語彙を一貫させる。 | @@ -522,11 +555,13 @@ Context要約を開始する利用率閾値は設定可能でなければなら | AT-05 | MCP temperature overrideがTask scopeのみで有効。 | | AT-06 | 非Vision modelへ画像指定時、選択localeのerrorが返る。 | | AT-07 | 未許可file read/write/deleteがOSまたはBrokerで拒否されAuditに残る。 | -| AT-08 | reparse point経由で許可path外へ到達できない。 | -| AT-09 | 承認hash変更後のexe実行が拒否される。 | -| AT-10 | Network denyでloopbackを含む通信が拒否される。 | -| AT-11 | allow-list外host/portへの通信が拒否される。 | -| AT-12 | 子プロセス・孫プロセスがJobとSandbox制約から逸脱できない。 | +| AT-08 | reparse解決結果が未確認または変更されたLogical Pathは拒否される。 | +| AT-09 | Approved Entry Executableのhash変更後の直接起動が拒否される。 | +| AT-10 | Network noneでLoopbackを含む通信が拒否される。 | +| AT-11 | local/restrictedのEffective Network Policy外address/portへの通信が拒否される。 | +| AT-12 | Entry/Descendant ProcessがLPACとJob confinementを継承し、breakawayまたはPolicy外resource accessができない。 | +| AT-21 | Approved Python、Approved PowerShell、Approved .NETから通常のDescendant Processを生成できる。 | +| AT-22 | development Workspace ArtifactはApprovalなしでEntry起動でき、External Unknown Executableの直接Entry起動は拒否される。 | | AT-13 | Task crash後、次回起動RecoveryでTask Resourceが除去される。 | | AT-14 | TaskGraph cycle追加が拒否される。 | | AT-15 | completed Taskが変更されない。 | @@ -540,10 +575,10 @@ Context要約を開始する利用率閾値は設定可能でなければなら | ID | 検証条件 | | --- | --- | -| E2E-01 | ソースツリー外へ展開したRelease Bundleだけを使い、local Ollamaとstdio MCP Clientから実際のfixture修正、build、test、日本語応答、Audit追跡、Task Resource cleanupまで完走する。 | -| E2E-02 | E2E-01と同等の作業をloopback限定Streamable HTTPで完走する。 | +| E2E-01 | ソースツリー外へ展開したRelease Bundle、Python 3.14 Approval、配布既定model、stdio MCP Clientを使い、複雑なPython programとsubprocessを含むtestの作成・実行、日本語応答、Audit追跡、Task Resource cleanupまで完走する。 | +| E2E-02 | E2E-01と同等の作業をloopback限定Streamable HTTPのIPv4/IPv6で完走する。 | -v1の完了には、win-x64向けself-contained Release Bundleが単独起動でき、E2E-01とE2E-02がInternetまたはcloud modelに依存せずPASSすることを必須とする。 +v1の完了には、win-x64向けself-contained Release Bundleが単独でEnvironment Resolutionを実行でき、確認済みOperational配置でE2E-01とE2E-02が`deepseek-v4-flash:cloud`を使ってPASSすることを必須とする。rate limit、接続失敗、model unavailableをPASSへ読み替えない。 --- @@ -573,7 +608,7 @@ v1の完了には、win-x64向けself-contained Release Bundleが単独起動で ## 23. 変更管理 -- 本仕様の変更は `schema_version`、MCP Tool schema、SandboxPolicy、安全不変条件への影響を必ず確認する。 +- 本仕様の変更は `schema_version`、MCP Tool schema、Effective Sandbox Policy、安全不変条件への影響を必ず確認する。 - Design.mdが本仕様と矛盾した場合は本仕様を優先し、Design.mdを修正する。 - 実装都合で仕様を暗黙変更してはならない。必要な場合は先に仕様を変更する。 @@ -603,7 +638,7 @@ v1の完了には、win-x64向けself-contained Release Bundleが単独起動で - `temperature` overrideを永続設定へ書き戻すこと。 - Legacy HTTP+SSEを互換目的で自動有効化すること。 - Streamable HTTPをloopback外にbindすること。 -- write permissionだけでdeleteを許可すること。 +- write Filesystem Capabilityだけでdeleteを許可すること。 - reparse point解決前の文字列pathだけでallow判定すること。 - hash不一致の実行ファイルを自動再承認すること。 - Network `allow`をunrestricted outboundとして解釈すること。 diff --git a/plans/ImplementationPlan.md b/plans/ImplementationPlan.md index b9cda6d..c2c348e 100644 --- a/plans/ImplementationPlan.md +++ b/plans/ImplementationPlan.md @@ -115,10 +115,10 @@ | 2 | MCP Public Interface | stdio、非同期Task受付、`agent_run/status` | G2 | | 3 | Session / Ollama / Prompt | text会話、Session、token usage、Prompt優先順位 | G3 | | 4 | Token Planner | TaskGraph、Token PERT/CPM、再計画 | G4 | -| 5 | Internal Tool / Policy Boundary | Design.mdのTool catalog、ToolUseGuard、Policy Engine | G5 | +| 5 | Internal Tool / Policy Boundary | Design.mdのInternal Tool catalog、ToolUseGuard、Policy Engine | G5 | | 6 | Filesystem / Process Sandbox | AppContainer、ACL、Job、hash approval | G6 | | 7 | Transaction / Recovery | ACL Resource Transaction、File rollback、Recovery Journal | G7 | -| 8 | Network Sandbox | WFP deny / host-port allow-list | G8 | +| 8 | Network Sandbox | WFP none / local / restricted | G8 | | 9 | Vision / Streamable HTTP | optional画像、loopback HTTP | G9 | | 10 | Audit / Security Hardening | Audit JSONL、全Security Matrix | G10 | | 11 | Acceptance / Release Audit | AT全件、文書整合、未完了ゼロ確認 | G11 | @@ -327,25 +327,25 @@ Planner deterministic test全PASS。 - OLL-10〜11 - SEC-INV-01〜02 ## 10.2 Internal Tool catalog -| Tool | Operation | +| Internal Tool | Operation | | --- | --- | | `file_search` | glob / name / text / regex / tail | | `file_read` | whole / range / metadata | | `file_edit` | overwrite / patch / atomic_replace / delete | -| `process_run` | exe + args + cwd + timeout | +| `process_run` | Executable + args + cwd + timeout | | `task_update` | add / dependency / estimate | -個々の `grep`、`find`、`tail` 等を独立Toolにしない。 +個々の `grep.exe`、`find.exe`、`tail.exe` 等を独立Internal Toolにしない。 ## 10.3 実装対象 - JSON Schema - Ollama native tool calling - Internal Tool Dispatcher - Policy Engine I/F -- Tool argument validation +- Internal Tool argument validation - ToolUseGuard - `requires_external_effect` — **Planning フェーズで LLM structured output により Task ごとに判定・設定**(keyword heuristic 廃止) - same failure fingerprint limit=5 - Plan/Run フェーズ分離: PlanAsync (structured output) → RunAsync (ready Task 順次実行) -- replan: Policy deny 時に最大3回まで再計画可能 +- replan: Effective Sandbox Policy deny時に最大3回まで再計画可能 - Context Summary: `prompt_eval_count / num_ctx` ≥ 80% で古い Conversation/ExecutionResult を compact ## 10.4 重要な段階制約 このPhaseの副作用backendはfake/in-memory実装を使用可能とし、Windows sandboxを迂回した本物の無制限file/process操作を実装してはならない。 @@ -353,13 +353,13 @@ Planner deterministic test全PASS。 - [ ] MCP ClientからInternal Toolが見えない - [ ] OllamaにはDesign.md 7章のcatalogだけを公開 - [ ] malformed tool args拒否 -- [ ] Policy deny時に副作用なし -- [ ] Policy denyは同一Tool呼出しへ再投入せず、許可済み能力内の条件付き再計画を最大3回行い、同一deny・計画未変更・実行可能Taskなし・再計画失敗でfailedとなる +- [ ] Effective Sandbox Policy deny時に副作用なし +- [ ] Effective Sandbox Policy denyは同一Internal Tool Callへ再投入せず、許可済みcapability内の条件付き再計画を最大3回行い、同一deny・計画未変更・実行可能Taskなし・再計画失敗でfailedとなる - [ ] external effect必須Taskでtool未使用ならcompletedにならない - [ ] 同一失敗5回でfailed - [ ] pure analysis TaskにはToolを強制しない ### Gate G5 -LLM→Tool→Policyの境界が固定され、Policyを通らない副作用経路が存在しないこと。 +LLM→Internal Tool Call→Policy Engineの境界が固定され、Effective Sandbox Policyを通らない副作用経路が存在しないこと。 --- # 11. Phase 6 — Filesystem / Process Sandbox ## 11.1 対応仕様 @@ -367,39 +367,36 @@ LLM→Tool→Policyの境界が固定され、Policyを通らない副作用経 - SEC-INV-03〜05、08 - ST-01〜11、ST-16 ## 11.2 実装対象 -- Application固定AppContainer Profile -- Sandbox Broker -- FilesystemPolicy R/W/E/Delete -- canonical path -- reparse point防御 -- NTFS ACL(承認directory rootへの継承ACE、root単位snapshot、protected descendantのfail-safe維持) -- Process approval canonical path + SHA-256 -- shell toggle -- Job Object -- descendant制御 -- process count / memory limit -- KILL_ON_JOB_CLOSE +- Application固定LPAC Profileと`ALL APPLICATION PACKAGES` opt-out +- User Sandbox ConfigurationからEffective Sandbox Policyへの変換 +- Logical Pathのreparse chain解決、config別Resolution Store、変更時fail-closed +- Workspace/Mount/Runtime/Artifact/OS RuntimeのFilesystem capability +- NTFS ACL(readとexecuteを分離した継承ACE、root単位snapshot、protected descendantのfail-safe維持) +- Executable Alias、Approved Entry ExecutableのResolved Path + SHA-256、user-local Approval Store +- Runtime Component/development Workspace Artifact/External Unknown Entryの分類 +- Entry ProcessとDescendant ProcessへLPAC、ACL、WFP、Job Objectを継承 +- breakaway禁止、process count / memory limit / timeout / KILL_ON_JOB_CLOSE - Named Pipeが必要ならtask固有DACL -## 11.3 Process approval -承認条件は以下を両方満たすこと。 +## 11.3 Executable Approval +BrokerがApproved Entry ExecutableまたはApproved Shell Executableを直接起動する条件は以下をすべて満たすこと。 ```text -canonical path == approval.path -SHA-256 == approval.sha256 +Alias == approval.alias +Resolved Path == approval.resolved_path +SHA-256 == approval.sha256 ``` -hash変更、path変更は再承認を必要とする。 +hash、Logical Path、Resolved Pathの変更は再承認を必要とする。Descendant Processには個別Approvalを要求せず、Sandbox Process Treeのconfinementで制御する。 ## 11.4 必須Security Test -- [ ] ST-01 read許可成功 -- [ ] ST-02 read未許可拒否 -- [ ] ST-03 write許可成功 -- [ ] ST-04 delete権限なし拒否 -- [ ] ST-05 junction脱出拒否 -- [ ] ST-06 symlink chain脱出拒否 -- [ ] ST-07 approved exe成功 -- [ ] ST-08 hash mismatch拒否 -- [ ] ST-09 shell disabled拒否 -- [ ] ST-10 descendant未承認exe拒否 -- [ ] ST-11 Job closeで全descendant終了 -- [ ] ST-16 Ollama tool path脱出拒否 +- [ ] readonly Workspaceでread成功、write/delete/Artifact Entry拒否 +- [ ] development Workspaceでread/write/delete/Artifact Entry成功 +- [ ] Mount accessとEntry起動禁止 +- [ ] reparse chain/cycle、Resolution変更、fingerprint変更をfail-closed +- [ ] Approved EntryのAlias/Resolved Path/hash一致、stale hash拒否 +- [ ] Runtime Component、Workspace Artifact、OS Runtime、External Unknown Entryの分類 +- [ ] Entry/Descendant tokenが同じLPAC identityでJob chain内に残る +- [ ] Approved Python→Python subprocess、Approved PowerShell→cmd、Approved .NET→build/testが成功 +- [ ] breakaway、未許可Filesystem、Registry write、COM、Networkを拒否 +- [ ] Job close/cancel/timeoutで全Descendant終了 +- [ ] Ollama Internal Tool CallのResolved Path脱出拒否 ## 11.5 監査証跡 Security testでは「例外になった」だけでなく、OS/Brokerで実際に対象資源へ副作用が発生していない証跡を残す。 ### Gate G6 @@ -460,7 +457,7 @@ mode=deny mode=allow -> default deny - -> allow-list hostをTask開始時にDNS resolve + -> restricted endpoint hostをTask開始時にDNS resolveしてIP snapshot化 -> IP snapshot + portだけallow ``` ## 13.3 実装時にCodexが選択してよい範囲 @@ -565,12 +562,11 @@ AT-01〜AT-20をすべて再実行する。 ## 16.2.1 Release Bundle - `dotnet publish -c Release -r win-x64 --self-contained true` を実行する。 - `agent-mcp.exe`、runtime/dependency、`agent.yaml`、en/ja locale、User Prompt、`skills/`、READMEを含むversion付きZIP、manifest、SHA-256を生成する。 -- ソースツリー外の空directoryへ展開し、Bundle内の資源だけで `agent-mcp check`、stdio initialize、loopback HTTP起動を実行する。 +- ソースツリー外の空directoryへ展開し、Bundle内の資源だけで`agent-mcp sandbox resolve`まで実行する。`check`、stdio、HTTPは、利用者がResolutionを確認し必要なExecutableを承認したOperational配置で実行する。 ## 16.2.2 Operational E2E Test -- E2E-01: Release Bundle、local Ollama、実測approval、stdio MCP Clientでfixtureのread/edit/process/build/test、日本語応答、Audit追跡、cleanupまで完走する。 -- E2E-02: 同等の作業をloopback Streamable HTTPで完走し、Legacy SSE、GET SSE、non-loopback bindが存在しないことを確認する。 -- Cloud model試験は任意互換試験とし、Release Gateへ含めない。 +- E2E-01: Release Bundle、`deepseek-v4-flash:cloud`、Python 3.14の実測Approval、stdio MCP Clientで一時fixtureへ複雑なPython programとtestを作成し、Python subprocessを含むtest、日本語応答、Audit追跡、cleanupまで完走する。 +- E2E-02: 同等の作業をloopback Streamable HTTPのIPv4/IPv6で完走し、Legacy SSE、GET SSE、non-loopback bindが存在しないことを確認する。 ## 16.3 Requirement Traceability監査 最低限次のマトリクスを完成させる。 | Requirement | Implementation | Test | Result | Evidence | @@ -622,7 +618,7 @@ AT-01〜AT-20をすべて再実行する。 9. 仕様・設計逸脱0、または承認済み文書更新済み 10. Recovery後のclean state確認 11. publish済みwin-x64 self-contained Release Bundleが単独で起動可能 -12. E2E-01 / E2E-02がlocal OllamaでPASS +12. E2E-01 / E2E-02が配布既定の`deepseek-v4-flash:cloud`とPython 3.14でPASS 13. READMEだけでfresh directoryから初回セットアップ可能 --- # 17. フェーズ横断テスト分類 diff --git a/plans/RequirementTraceability.md b/plans/RequirementTraceability.md index 6025755..4f901e6 100644 --- a/plans/RequirementTraceability.md +++ b/plans/RequirementTraceability.md @@ -1,8 +1,8 @@ # Requirement Traceability -現行実装の検証対象はcommit `37386b8`(2026-08-11 JST)である。2026-08-09を含む過去の結果は`plans/progress/`に履歴として保持するが、現行PASS判定には使用しない。 +Sandbox Usability Revision後の現行実装を対象とする。commit `37386b8`(2026-08-11 JST)以前の結果は`plans/progress/`と本書末尾に履歴として保持するが、現行PASS判定には使用しない。 -状態は`PASS / PARTIAL / FAIL / NOT REVERIFIED`を使う。現在、Requirement mapping、Acceptance Tests、Security Testsに未説明の`PARTIAL / FAIL / NOT REVERIFIED`はない。 +状態は`PASS / PARTIAL / FAIL / NOT REVERIFIED`を使う。2026-08-12の現行working-tree管理者証跡は`artifacts/evidence/sandbox-usability-final-working-tree-2/`に保存する。 ## Requirement mapping @@ -15,11 +15,11 @@ | AGT-01〜06 | `AgentRuntime`, `InternalToolDispatcher`, MCP公開境界 | completion/replan/iteration/catalog tests、stdio/HTTP E2E | PASS | | PLN-01〜06 | `TaskGraph`, `PlannerService`, structural `task_update` | Planner 10件、structured-plan retry、Task固有Tool証跡test | PASS | | SES-01〜06 | `SessionStore`、会話/graph/result保持、structured要約 | summary成功/失敗、session/runtime tests | PASS | -| FS-01〜07 | `PathPolicy`, `TaskAclManager`, `WindowsToolBackend` | Elevated Windows Security/Recovery 42件、Operational E2E | PASS | -| PRC-01〜07 | `ExecutablePolicy`, `RestrictedProcessLauncher`, Job/child restriction | Elevated Windows suite、in-process build/self-test E2E | PASS | +| FS-01〜10 | `ResolvedPath`, `PathPolicy`, `TaskAclManager`, `SandboxEnvironmentResolver` | Resolution/fingerprint/nested root、read/execute分離、readonly/development LPAC実効果、管理者suite | PASS | +| PRC-01〜09 | `EntryExecutablePolicy`, `RestrictedProcessLauncher`, Approval Store、Job Process Tree | Alias-only Entry、hash、Runtime/Artifact分類、Entry/Descendant LPAC SID・Job、PowerShell/Python/.NET build/test、breakaway、process/memory/timeout | PASS | | REC-01〜07 | retry、timeout、cancel cleanup、`RecoveryManager` | Core lifecycle、elevated crash/recovery matrix、残留0 | PASS | -| SEC-INV-01〜05 | deny-by-default、Policy非変更、path/hash/descendant制約 | Core境界、elevated Windows suite、E2E | PASS | -| SEC-INV-06〜08 | WFP allow/deny、credential非継承 | elevated WFP/security tests、Bundle check、E2E | PASS | +| SEC-INV-01〜05 | deny-by-default、Policy非変更、Resolved Path/hash/Entry分類 | Core境界、管理者Windows suite、Operational E2E | PASS | +| SEC-INV-06〜08、13 | WFP none/local/restricted、credential非継承、Process Tree confinement | credential非継承、全Network mode、Process Tree実効果 | PASS | | SEC-INV-09 | Fixed Prompt precedence | prompt tests | PASS | | SEC-INV-10〜11 | cleanup、Recovery gate | Core cleanup、elevated recovery tests、全E2E後残留0 | PASS | | SEC-INV-12 | stdio stdout purity | MCP stdio integration、Operational stdio E2E | PASS | @@ -33,7 +33,7 @@ | ID | Current implementation / test | Status | |---|---|---| | AT-01〜06 | MCP stdio/HTTP、公開schema、Task lifecycle、Ollama、Vision拒否 | PASS | -| AT-07〜13 | Windows backend、reparse、process、WFP、crash recovery | PASS | +| AT-07〜13、21〜22 | Resolution、LPAC/ACL/Process Tree、WFP、crash recovery | PASS | | AT-14〜17 | cycle、immutable、PERT/CPM、全phase token集計 | PASS | | AT-18〜20 | en/ja、追加locale、malformed/unknown locale | PASS | @@ -41,8 +41,8 @@ | ID | Current implementation / test | Status | |---|---|---| -| ST-01〜09 | capability/path/reparse/hash/shell、ACL/AppContainer実効果 | PASS | -| ST-10〜14 | child restriction、Job timeout、WFP actual traffic | PASS | +| ST-01〜09 | Profile/Mount/Resolution/Approval、ACL/LPAC実効果 | PASS | +| ST-10〜14 | Descendant inheritance、Job timeout、WFP actual traffic | PASS | | ST-15〜16 | MCP非公開境界、Design Tool catalogの名前/schema/Policy境界 | PASS | | ST-17〜18 | ACL/WFP/loopback crash recovery | PASS | | ST-19〜20 | malformed locale fatal、non-loopback bind拒否 | PASS | @@ -53,14 +53,14 @@ Internal Tool testは件数を仕様としてassertしない。`Design.md`のToo | Verification | Date / privilege | Result | |---|---|---| -| Release build | 2026-08-11 / non-elevated / Release | warning 0、error 0 | -| Agent.Core.Tests | 2026-08-11 / administrator suite | 82 PASS、0 FAIL、0 skip | -| Agent.Planner.Tests | 2026-08-11 / administrator suite | 10 PASS、0 FAIL、0 skip | -| Agent.Windows.Tests | 2026-08-11 / administrator | 42 PASS、0 FAIL、0 skip。Security/Recovery/crash matrixを含む | -| win-x64 self-contained Bundle | 2026-08-11 / publish後administrator check | directory/ZIP/checksum生成、local override非同梱、runtime check PASS。ZIP SHA-256 `8ccc538557dcb96cc95ecae7b1858de3544997b16a5675aacb05a03f539829b1` | -| Operational E2E | 2026-08-11 / administrator / `deepseek-v4-flash:cloud` | stdio、HTTP IPv4、HTTP IPv6すべてPASS。read/edit/build/self-test、ACL/WFP/temp cleanup確認 | -| Task Resource repository state | 2026-08-11 | `state/journals`, `state/backups`, `state/mcp-tasks`はいずれも不存在 | +| Release build | 2026-08-12 / non-elevated | warning 0、error 0 | +| Agent.Core.Tests | 2026-08-12 / administrator | 103 PASS、0 FAIL、0 skip | +| Agent.Planner.Tests | 2026-08-12 / administrator | 10 PASS、0 FAIL、0 skip | +| Agent.Windows.Tests | 2026-08-12 / administrator | 53 PASS、0 FAIL、0 skip。LPAC/ACL/Job/WFP/Recovery、PowerShell/Python/.NET Process Treeを含む | +| win-x64 self-contained Bundle | 2026-08-12 / administrator / fresh展開 | check PASS。既定ZIPにlocal override・local prompt/Skill・固有pathなし。deployment local資産保持。ZIP SHA-256 `f220cbf66d6d2bfd110542cdeec48cd64c97aec549b4ffd999c4746f3324ecb0` | +| Operational E2E | 2026-08-12 / administrator / `deepseek-v4-flash:cloud`, `think:true`, Python 3.14.3 | stdio、HTTP IPv4、HTTP IPv6すべてPASS。Python実装、子Python unittest、独立検証、ACL/WFP/temp cleanup確認 | +| Task Resource residual state | 2026-08-12 / administrator | Journal 0、backup 0、published `agent-mcp` process 0 | | MCP 2026-07-28 conformance runner 0.2.0-alpha.11 | 2026-08-11 / loopback HTTP | application failure 0。適用可能checkはPASS。production fixture非公開とWindows runner終了不具合はrunner limitation 9件として非PASSのまま分離 | | MCP Tasks conformance runner | 2026-08-11 / loopback HTTP | application failure 0。runner専用fixture不足による未実施27件をrunner limitationとして非PASSのまま分離 | -証跡は`artifacts/evidence/final-37386b8/`に生成した。conformanceのrunner limitationはRequirement PASSの根拠に使用しておらず、対応する製品機能はCore/MCP integration、Windows suite、Operational E2Eで検証した。 +conformance runnerの行はcommit `37386b8`の履歴であり、旧証跡は`artifacts/evidence/final-37386b8/`に保持する。現行ReleaseのSecurity/Operational証跡は上記2026-08-12記録を正本とする。 diff --git a/plans/progress/Phase6.md b/plans/progress/Phase6.md index fdfd175..03a6120 100644 --- a/plans/progress/Phase6.md +++ b/plans/progress/Phase6.md @@ -1,6 +1,7 @@ # Phase 6 Progress > Historical evidence: この記録は作成当時のcommitに対する結果であり、現行commitの再監査結果ではない。 +> Sandbox Usability Revisionでchild-process restriction、path/hash設定、shell toggleはLPAC Process Tree、Executable Alias/Approval Store、semantic Execution設定へ置換された。本記録は新設計の受入証跡には使用しない。 - 実装: fixed AppContainer、canonical/reparse policy、能力別ACL、path+SHA-256、Job Object、`PROCESS_CREATION_CHILD_PROCESS_RESTRICTED`、Broker launch。 - 実測: approved PowerShellからの `Start-Process`、Process API、shell、rename/copyの4経路を試し、全unbrokered childをOS restrictionで拒否した。markerなし、Job process不変、非zero親結果、Audit `policy.deny`を確認した。 diff --git a/prompts/user-system.md b/prompts/user-system.md index 9766b67..482d757 100644 --- a/prompts/user-system.md +++ b/prompts/user-system.md @@ -36,9 +36,10 @@ # Running Programs -- 承認済みの実行fileだけを直接起動する。 +- `process_run.executable`には設定されたExecutable Alias、Runtime Component、またはdevelopment Workspace Artifactを指定する。 - executable、cwd、argsを分け、引数の順序と値を維持する。 -- 作成するprogramやtestからdescendant processを生成しない。 +- build、test、subprocessなど必要なDescendant ProcessはEntry Processと同じSandbox Process Tree内で実行できる。個別承認を要求しない。 +- Descendant ProcessをSandbox外へ逃がす方法、Job Objectからbreakawayする方法、権限を拡張する方法は試さない。 - 依頼なしにpackageをinstallしたりnetworkへ接続したりしない。 - stdout、stderr、exit codeを確認し、期待結果と照合する。 diff --git a/scripts/Launch-ElevatedVerification.ps1 b/scripts/Launch-ElevatedVerification.ps1 new file mode 100644 index 0000000..b4327bd --- /dev/null +++ b/scripts/Launch-ElevatedVerification.ps1 @@ -0,0 +1,10 @@ +param([string]$EvidenceRoot = 'artifacts\evidence\sandbox-usability-precommit') + +$ErrorActionPreference = 'Stop' +$verification = (Resolve-Path (Join-Path $PSScriptRoot 'Run-Final-ElevatedVerification.ps1')).Path +$repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$arguments = @( + '-NoProfile', '-ExecutionPolicy', 'Bypass', '-NoExit', '-File', ('"' + $verification + '"'), + '-EvidenceRoot', $EvidenceRoot +) +Start-Process -FilePath 'C:\Program Files\PowerShell\7\pwsh.exe' -Verb RunAs -ArgumentList $arguments -WorkingDirectory $repository -WindowStyle Normal diff --git a/scripts/Operational-E2E.ps1 b/scripts/Operational-E2E.ps1 index 4b964f1..b253198 100644 --- a/scripts/Operational-E2E.ps1 +++ b/scripts/Operational-E2E.ps1 @@ -3,122 +3,52 @@ param( [string]$Transport = 'stdio', [ValidateSet('127.0.0.1', '::1')] [string]$HttpHost = '127.0.0.1', - [string]$ReleaseZip = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64.zip' + [string]$ReleaseZip = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64.zip', + [string]$PythonPath ) $ErrorActionPreference = 'Stop' $OutputEncoding = [Text.UTF8Encoding]::new($false) [Console]::OutputEncoding = $OutputEncoding -$originalTemp = $env:TEMP -$originalTmp = $env:TMP -$originalDotnetCliHome = $env:DOTNET_CLI_HOME -$originalFirstTimeExperience = $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE -$originalTelemetry = $env:DOTNET_CLI_TELEMETRY_OPTOUT -$originalAddGlobalToolsToPath = $env:DOTNET_ADD_GLOBAL_TOOLS_TO_PATH -$originalUserPath = [Environment]::GetEnvironmentVariable('Path', 'User') $repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path $zip = (Resolve-Path (Join-Path $repository $ReleaseZip)).Path $root = Join-Path ([IO.Path]::GetTempPath()) ("agent-mcp-operational-e2e-" + [Guid]::NewGuid().ToString('N')) $bundleRoot = Join-Path $root 'bundle' $fixture = Join-Path $root 'fixture' -New-Item -ItemType Directory -Path $bundleRoot,$fixture | Out-Null +$alias = 'python-e2e-' + [Guid]::NewGuid().ToString('N').Substring(0, 12) $passed = $false +$approved = $false +$resolutionStorePath = $null +$server = $null +New-Item -ItemType Directory -Path $bundleRoot,$fixture | Out-Null $fixtureAclBefore = (Get-Acl -LiteralPath $fixture).Sddl try { Expand-Archive -LiteralPath $zip -DestinationPath $bundleRoot $bundle = (Get-ChildItem -LiteralPath $bundleRoot -Directory | Select-Object -First 1).FullName - $e2eSkills = Join-Path $bundle 'e2e-skills' - New-Item -ItemType Directory -Path $e2eSkills | Out-Null - $program = @' -namespace OperationalFixture; - -public static class Program -{ - private const string Value = "BUG"; - public static int Main(string[] args) - { - if (args.Length == 1 && args[0] == "--self-test") return Value == "FIXED" ? 0 : 1; - System.Console.WriteLine(Value); - return 0; + $agent = Join-Path $bundle 'agent-mcp.exe' + $configPath = Join-Path $bundle 'agent.yaml' + if (-not $PythonPath) { + $PythonPath = (& uv python find 3.14).Trim() + if ($LASTEXITCODE -ne 0 -or -not $PythonPath) { throw 'Python 3.14 could not be resolved with uv.' } } -} -'@ - Set-Content -LiteralPath (Join-Path $fixture 'Program.cs') -Value $program -Encoding utf8NoBOM - Set-Content -LiteralPath (Join-Path $fixture 'OperationalFixture.csproj') -Encoding utf8NoBOM -Value @' - - - Exe - net10.0 - false - false - - -'@ - $sandboxTemp = Join-Path $fixture '.sandbox-temp' - $sandboxCliHome = Join-Path $fixture '.dotnet-home' - New-Item -ItemType Directory -Path $sandboxTemp,$sandboxCliHome | Out-Null - $env:TEMP = $sandboxTemp - $env:TMP = $sandboxTemp - $env:DOTNET_CLI_HOME = $sandboxCliHome - $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' - $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' - $env:DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = 'false' + $pythonLogical = [IO.Path]::GetFullPath($PythonPath) + $python = (Get-Item -LiteralPath $pythonLogical).Target + if (-not $python) { $python = $pythonLogical } + elseif (-not [IO.Path]::IsPathFullyQualified($python)) { $python = [IO.Path]::GetFullPath((Join-Path (Split-Path $pythonLogical) $python)) } + $python = (Resolve-Path -LiteralPath $python).Path + $pythonVersion = (& $python --version 2>&1 | Out-String).Trim() + if ($pythonVersion -notmatch '^Python 3\.14\.') { throw "Operational E2E requires Python 3.14; resolved '$pythonVersion'." } + $runtimeRoot = Split-Path $python - $dotnet = (Get-Command dotnet).Source - $dotnetRoot = Split-Path $dotnet - $sdkVersion = (& $dotnet --version).Trim() - $csc = Join-Path $dotnetRoot "sdk\$sdkVersion\Roslyn\bincore\csc.dll" - $referenceDirectory = (Get-ChildItem (Join-Path $dotnetRoot 'packs\Microsoft.NETCore.App.Ref') -Directory | - Sort-Object { [Version]$_.Name } -Descending | Select-Object -First 1).FullName + '\ref\net10.0' - $outputDirectory = Join-Path $fixture 'build' - New-Item -ItemType Directory -Path $outputDirectory | Out-Null - $dll = Join-Path $outputDirectory 'OperationalFixture.dll' - Set-Content -LiteralPath (Join-Path $outputDirectory 'OperationalFixture.runtimeconfig.json') -Encoding utf8NoBOM -Value @' -{"runtimeOptions":{"tfm":"net10.0","framework":{"name":"Microsoft.NETCore.App","version":"10.0.0"},"rollForward":"LatestPatch"}} -'@ - $rsp = Join-Path $fixture 'build.rsp' - Set-Content -LiteralPath $rsp -Encoding utf8NoBOM -Value @" --nostdlib+ --target:exe --deterministic+ --debug- --out:"$dll" --r:"$(Join-Path $referenceDirectory 'System.Runtime.dll')" --r:"$(Join-Path $referenceDirectory 'System.Console.dll')" -"$(Join-Path $fixture 'Program.cs')" -"@ - & $dotnet exec $csc "@$rsp" - if ($LASTEXITCODE -ne 0) { throw 'Initial in-process compiler fixture build failed.' } + Set-Content -LiteralPath (Join-Path $fixture 'README.md') -Encoding utf8NoBOM -Value @' +# Graph metrics fixture - $precompute = Join-Path $root 'precompute' - New-Item -ItemType Directory -Path $precompute | Out-Null - $expectedSource = Join-Path $precompute 'Program.cs' - $expectedDll = Join-Path $precompute 'OperationalFixture.dll' - $expectedRsp = Join-Path $precompute 'expected.rsp' - Set-Content -LiteralPath $expectedSource -Value $program.Replace('"BUG"','"FIXED"') -Encoding utf8NoBOM - Set-Content -LiteralPath $expectedRsp -Encoding utf8NoBOM -Value @" --nostdlib+ --target:exe --deterministic+ --debug- --out:"$expectedDll" --r:"$(Join-Path $referenceDirectory 'System.Runtime.dll')" --r:"$(Join-Path $referenceDirectory 'System.Console.dll')" -"$expectedSource" -"@ - & $dotnet exec $csc "@$expectedRsp" - if ($LASTEXITCODE -ne 0) { throw 'Expected fixture hash build failed.' } - $expectedDllHash = (Get-FileHash -LiteralPath $expectedDll -Algorithm SHA256).Hash - $fixtureAclBaseline = @{} - @($fixture) + @(Get-ChildItem -LiteralPath $fixture -Recurse -Force | Select-Object -ExpandProperty FullName) | ForEach-Object { - $fixtureAclBaseline[$_] = (Get-Acl -LiteralPath $_).Sddl - } +Implement `graph_metrics.py` and `test_graph_metrics.py`. The implementation must validate dependencies, +produce a deterministic topological order, detect cycles, and calculate the weighted critical path. +'@ - $dotnetHash = (Get-FileHash -LiteralPath $dotnet -Algorithm SHA256).Hash - $cscHash = (Get-FileHash -LiteralPath $csc -Algorithm SHA256).Hash $fixtureYaml = $fixture.Replace('\','/') - $dotnetYaml = $dotnet.Replace('\','/') $config = @" schema_version: 1 application: @@ -139,7 +69,7 @@ agent: ollama: endpoint: http://127.0.0.1:11434 model: deepseek-v4-flash:cloud - think: false + think: true keep_alive: 5m options: num_ctx: 32768 @@ -148,27 +78,21 @@ ollama: planner: max_tasks: 32 sandbox: - filesystem: - read: [$fixtureYaml] - write: [$fixtureYaml] - execute: [$dotnetYaml, $($csc.Replace('\','/')), $($dll.Replace('\','/'))] - delete: [$fixtureYaml] - process: - allow_shell: false - approvals: - - path: $dotnetYaml - sha256: $dotnetHash - - path: $($csc.Replace('\','/')) - sha256: $cscHash - - path: $($dll.Replace('\','/')) - sha256: $expectedDllHash - max_processes: 8 + profile: development + workspaces: + - $fixtureYaml + mounts: [] + execution: + executables: + - $alias + shells: [] network: - mode: deny - allow: [] + access: none + local_endpoints: [] + restricted_endpoints: [] resources: - memory_mb: 2048 - process_timeout_seconds: 300 + profile: constrained + execution_timeout_seconds: 300 file_edit: memory_backup_limit_mb: 16 limits: @@ -180,95 +104,90 @@ localization: directory: config/locales fallback: en skills: - directory: e2e-skills + directory: skills logging: directory: logs max_file_bytes: 10485760 retained_files: 5 "@ - Set-Content -LiteralPath (Join-Path $bundle 'agent.yaml') -Value $config -Encoding utf8NoBOM - $programPath = (Join-Path $fixture 'Program.cs').Replace('\','/') - $cscPath = $csc.Replace('\','/') - $rspPath = $rsp.Replace('\','/') - $dllPath = $dll.Replace('\','/') - $buildRequest = @" -実作業を行ってください。最終応答は日本語にしてください。 -1. file_readで $programPath を読む。 -2. file_editのoverwriteで、他の内容を維持したまま文字列 \"BUG\" を \"FIXED\" に変更する。 -3. process_runで executable=$dotnetYaml, cwd=$fixtureYaml, args=[\"exec\",\"$cscPath\",\"@$rspPath\"] を実行し、descendantを生成しないin-process C# buildのexit code 0を確認する。 -すべてのtool結果を確認してから完了すること。 -"@ - $testRequest = @" -直前のbuild成果物を独立Taskで実テストしてください。最終応答は日本語にしてください。 -process_runで executable=$dotnetYaml, cwd=$fixtureYaml, args=[\"$dllPath\",\"--self-test\"] を1回実行し、exit code 0を確認してから完了すること。 -"@ + Set-Content -LiteralPath $configPath -Value $config -Encoding utf8NoBOM + + & $agent executable approve $alias --path $pythonLogical --runtime-root $runtimeRoot --yes --config $configPath --lang en + if ($LASTEXITCODE -ne 0) { throw 'Executable approval failed.' } + $approved = $true + & $agent sandbox confirm --yes --config $configPath --lang en + if ($LASTEXITCODE -ne 0) { throw 'Sandbox Environment Resolution confirmation failed.' } + $normalizedConfig = [IO.Path]::GetFullPath($configPath).ToUpperInvariant() + $hash = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($normalizedConfig))) + $resolutionStorePath = Join-Path $env:LOCALAPPDATA "AgentSandboxMcp\environment-resolutions\$hash.json" + & $agent sandbox show --format json --config $configPath --lang en | Out-Null + if ($LASTEXITCODE -ne 0) { throw 'Effective Sandbox Policy show failed.' } + $request = @" +実作業を行ってください。作業ディレクトリは $fixtureYaml です。 +README.md の仕様に従い、外部パッケージを使わず graph_metrics.py と test_graph_metrics.py を作成してください。 +テストには、正常なDAG、同順位時の決定的な順序、未知dependency、cycle、weighted critical pathを含めてください。 +さらに test_graph_metrics.py 自身が subprocess.run([sys.executable, ...]) で小さな子Pythonを起動して成功を検証するテストを含めてください。 +README.md の読取後、2ファイルの作成、最初のテスト実行、失敗時の修正と再実行は、分割せず単一の実装・検証Taskとして計画してください。 +最後に process_run で executable=$alias, cwd=$fixtureYaml, args=["-m","unittest","discover","-v"] を実行し、全テスト成功を確認してください。 +最終応答は日本語にし、作成ファイルとテスト結果を簡潔に報告してください。 +"@ $clientProject = Join-Path $repository 'scripts\E2EClient\E2EClient.csproj' if ($Transport -eq 'stdio') { - dotnet run --project $clientProject -c Release --no-build -- stdio (Join-Path $bundle 'agent-mcp.exe') (Join-Path $bundle 'agent.yaml') $buildRequest $testRequest - if ($LASTEXITCODE -ne 0) { throw 'E2E-01 stdio client failed.' } + dotnet run --project $clientProject -c Release --no-build -- stdio $agent $configPath $request + if ($LASTEXITCODE -ne 0) { throw 'E2E stdio client failed.' } } else { - $server = Start-Process -FilePath (Join-Path $bundle 'agent-mcp.exe') -ArgumentList @('serve','--config',(Join-Path $bundle 'agent.yaml'),'--lang','ja','--transport','http') -WorkingDirectory $bundle -WindowStyle Hidden -PassThru - try { - $httpAuthority = if ($HttpHost -eq '::1') { '[::1]' } else { $HttpHost } - $endpoint = "http://${httpAuthority}:18080/mcp" - $ready = $false - for ($attempt = 0; $attempt -lt 100; $attempt++) { - try { $response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck; if ($response.StatusCode -eq 405) { $ready=$true; break } } catch { } - Start-Sleep -Milliseconds 100 - } - if (-not $ready) { throw 'HTTP server did not become ready.' } - dotnet run --project $clientProject -c Release --no-build -- http $endpoint unused $buildRequest $testRequest - if ($LASTEXITCODE -ne 0) { throw 'E2E-02 HTTP client failed.' } + $server = Start-Process -FilePath $agent -ArgumentList @('serve','--config',$configPath,'--lang','ja','--transport','http') -WorkingDirectory $bundle -WindowStyle Hidden -PassThru + $httpAuthority = if ($HttpHost -eq '::1') { '[::1]' } else { $HttpHost } + $endpoint = "http://${httpAuthority}:18080/mcp" + $ready = $false + for ($attempt = 0; $attempt -lt 100; $attempt++) { + try { $response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck; if ($response.StatusCode -eq 405) { $ready=$true; break } } catch { } + Start-Sleep -Milliseconds 100 } - finally { if (-not $server.HasExited) { $server.Kill($true); $server.WaitForExit() } } + if (-not $ready) { throw 'HTTP server did not become ready.' } + dotnet run --project $clientProject -c Release --no-build -- http $endpoint unused $request + if ($LASTEXITCODE -ne 0) { throw 'E2E HTTP client failed.' } + if (-not $server.HasExited) { $server.Kill($true); $server.WaitForExit() } + $server = $null } - & $dotnet exec $csc "@$rsp" - if ($LASTEXITCODE -ne 0) { throw 'Post-task fixture build failed.' } - & $dotnet $dll --self-test - if ($LASTEXITCODE -ne 0) { throw 'Post-task fixture self-test failed.' } + & $python -m unittest discover -v -s $fixture + if ($LASTEXITCODE -ne 0) { throw 'Independent Python verification failed.' } + foreach ($required in 'graph_metrics.py','test_graph_metrics.py') { + if (-not (Test-Path -LiteralPath (Join-Path $fixture $required))) { throw "Expected artifact is missing: $required" } + } if ((Get-ChildItem -LiteralPath (Join-Path $bundle 'state\journals') -File -ErrorAction SilentlyContinue | Measure-Object).Count -ne 0) { throw 'Journal residue detected.' } if ((Get-ChildItem -LiteralPath (Join-Path $bundle 'state\backups') -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count -ne 0) { throw 'Temporary backup residue detected.' } if ((Get-Acl -LiteralPath $fixture).Sddl -ne $fixtureAclBefore) { throw 'Fixture ACL was not restored byte-for-byte.' } - $bundleExe = (Join-Path $bundle 'agent-mcp.exe') - $orphan = Get-Process -Name 'agent-mcp' -ErrorAction SilentlyContinue | Where-Object { try { $_.Path -eq $bundleExe } catch { $false } } - if ($orphan) { throw 'Orphan agent-mcp process detected.' } - foreach ($entry in $fixtureAclBaseline.GetEnumerator()) { - if ((Get-Acl -LiteralPath $entry.Key).Sddl -ne $entry.Value) { throw "Existing resource ACL was not restored byte-for-byte: $($entry.Key)" } - } - $audit = Get-Content -LiteralPath (Join-Path $bundle 'logs\audit.jsonl') - $auditEvents = $audit | ForEach-Object { $_ | ConvertFrom-Json } + $auditEvents = Get-Content -LiteralPath (Join-Path $bundle 'logs\audit.jsonl') | ForEach-Object { $_ | ConvertFrom-Json } $appContainerSid = ($auditEvents | Where-Object Event -eq 'acl.apply' | Select-Object -First 1).Data.sid - if (-not $appContainerSid) { throw 'Audit did not contain the AppContainer SID.' } - $processStarts = @($auditEvents | Where-Object Event -eq 'process.start') - if (-not ($processStarts | Where-Object { $_.Data.managed_entry_path -eq $csc })) { throw 'Audit is missing the approved in-process compiler execution.' } - if (-not ($processStarts | Where-Object { $_.Data.managed_entry_path -eq $dll })) { throw 'Audit is missing the approved fixture self-test execution.' } + if (-not $appContainerSid) { throw 'Audit is missing the AppContainer SID.' } foreach ($path in @($fixture) + @(Get-ChildItem -LiteralPath $fixture -Recurse -Force | Select-Object -ExpandProperty FullName)) { if ((Get-Acl -LiteralPath $path).Sddl.Contains($appContainerSid, [StringComparison]::OrdinalIgnoreCase)) { throw "AppContainer ACL residue detected: $path" } } - $auditText = $audit -join "`n" - if ($auditText -notmatch 'file.write' -or $auditText -notmatch 'process.start' -or $auditText -notmatch 'network.rule.remove' -or $auditText -notmatch 'acl.restore') { throw 'Audit trace is incomplete.' } + if (-not ($auditEvents | Where-Object Event -eq 'process.start')) { throw 'Audit is missing process.start.' } + if (-not ($auditEvents | Where-Object Event -eq 'process.exit')) { throw 'Audit is missing process.exit.' } + if (-not ($auditEvents | Where-Object { $_.Event -eq 'network.rule.remove' -and $_.Data.residual_filters -eq '0' })) { throw 'Audit is missing zero-residual network.rule.remove.' } + $orphan = Get-Process -Name agent-mcp -ErrorAction SilentlyContinue | Where-Object { try { $_.Path -eq $agent } catch { $false } } + if ($orphan) { throw 'Operational Bundle left an orphan agent-mcp process.' } $passed = $true - Write-Output "Operational E2E $Transport/$HttpHost PASS: $root" + Write-Output "Operational E2E $Transport/$HttpHost PASS with $pythonVersion" } catch { Write-Error "Operational E2E $Transport FAILED. Evidence retained at $root. $($_.Exception.Message)" throw } finally { - $env:TEMP = $originalTemp - $env:TMP = $originalTmp - $env:DOTNET_CLI_HOME = $originalDotnetCliHome - $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = $originalFirstTimeExperience - $env:DOTNET_CLI_TELEMETRY_OPTOUT = $originalTelemetry - $env:DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = $originalAddGlobalToolsToPath - $currentUserPath = [Environment]::GetEnvironmentVariable('Path', 'User') - if ($currentUserPath -cne $originalUserPath) { - Write-Error 'Operational E2E changed the persistent user PATH.' + if ($server -and -not $server.HasExited) { $server.Kill($true); $server.WaitForExit() } + if ($approved -and (Test-Path -LiteralPath $agent)) { + & $agent executable revoke $alias --yes --config $configPath --lang en | Out-Null + } + if ($resolutionStorePath -and (Test-Path -LiteralPath $resolutionStorePath)) { + Remove-Item -LiteralPath $resolutionStorePath -Force } - if ($passed) { Remove-Item -LiteralPath $root -Recurse -Force } + if ($passed -and (Test-Path -LiteralPath $root)) { Remove-Item -LiteralPath $root -Recurse -Force } } diff --git a/scripts/Publish-Release.ps1 b/scripts/Publish-Release.ps1 index a36c2b4..d794bb5 100644 --- a/scripts/Publish-Release.ps1 +++ b/scripts/Publish-Release.ps1 @@ -15,22 +15,18 @@ $bundleName = "agent-sandbox-mcp-$Version-win-x64" $bundle = Join-Path $outputRootPath $bundleName $zip = Join-Path $outputRootPath "$bundleName.zip" $checksum = "$zip.sha256" +$stagingRoot = Join-Path $outputRootPath (".staging-" + [Guid]::NewGuid().ToString('N')) +$stagingBundle = Join-Path $stagingRoot $bundleName New-Item -ItemType Directory -Force -Path $outputRootPath | Out-Null -if (Test-Path -LiteralPath $bundle) { - $resolvedBundle = (Resolve-Path -LiteralPath $bundle).Path - if (-not $resolvedBundle.StartsWith($outputRootPath + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { - throw 'Existing Release Bundle escaped the validated output root.' - } - Get-ChildItem -LiteralPath $resolvedBundle -Force | Remove-Item -Recurse -Force -} if (Test-Path -LiteralPath $zip) { Remove-Item -LiteralPath $zip -Force } if (Test-Path -LiteralPath $checksum) { Remove-Item -LiteralPath $checksum -Force } +New-Item -ItemType Directory -Path $stagingBundle | Out-Null $appProject = Join-Path $repository 'src\Agent.App\Agent.App.csproj' dotnet restore $appProject -r win-x64 --locked-mode if ($LASTEXITCODE -ne 0) { throw "win-x64 locked restore failed with exit code $LASTEXITCODE" } -dotnet publish $appProject -c Release -r win-x64 --self-contained true --no-restore --output $bundle -p:Version=$Version +dotnet publish $appProject -c Release -r win-x64 --self-contained true --no-restore --output $stagingBundle -p:Version=$Version if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" } $required = @( @@ -38,23 +34,23 @@ $required = @( 'prompts\user-system.md', 'skills', 'README.md' ) foreach ($relative in $required) { - if (-not (Test-Path -LiteralPath (Join-Path $bundle $relative))) { throw "Release Bundle is missing $relative" } + if (-not (Test-Path -LiteralPath (Join-Path $stagingBundle $relative))) { throw "Release Bundle is missing $relative" } } foreach ($relative in @('agent.local.yaml', 'config\agent.local.example.yaml')) { - if (Test-Path -LiteralPath (Join-Path $bundle $relative)) { throw "Release Bundle must not contain local override input: $relative" } + if (Test-Path -LiteralPath (Join-Path $stagingBundle $relative)) { throw "Release Bundle must not contain local override input: $relative" } } -$manifestPath = Join-Path $bundle 'publish-manifest.sha256' -Get-ChildItem -LiteralPath $bundle -File -Recurse | +$manifestPath = Join-Path $stagingBundle 'publish-manifest.sha256' +Get-ChildItem -LiteralPath $stagingBundle -File -Recurse | Where-Object FullName -ne $manifestPath | Sort-Object FullName | ForEach-Object { - $relative = [IO.Path]::GetRelativePath($bundle, $_.FullName).Replace('\', '/') + $relative = [IO.Path]::GetRelativePath($stagingBundle, $_.FullName).Replace('\', '/') $hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant() "$hash $relative" } | Set-Content -LiteralPath $manifestPath -Encoding utf8NoBOM -Compress-Archive -LiteralPath $bundle -DestinationPath $zip -CompressionLevel Optimal +Compress-Archive -LiteralPath $stagingBundle -DestinationPath $zip -CompressionLevel Optimal $zipHash = (Get-FileHash -LiteralPath $zip -Algorithm SHA256).Hash.ToLowerInvariant() "$zipHash $([IO.Path]::GetFileName($zip))" | Set-Content -LiteralPath $checksum -Encoding ascii @@ -70,8 +66,8 @@ try { if (-not $SkipRuntimeCheck) { Push-Location $freshBundle try { - & .\agent-mcp.exe check --config .\agent.yaml --lang en - if ($LASTEXITCODE -ne 0) { throw "Bundle check failed with exit code $LASTEXITCODE. Run this script in an elevated terminal." } + & .\agent-mcp.exe sandbox resolve --config .\agent.yaml --lang en + if ($LASTEXITCODE -ne 0) { throw "Bundle resolution check failed with exit code $LASTEXITCODE." } } finally { Pop-Location } } @@ -80,6 +76,25 @@ finally { if (Test-Path -LiteralPath $freshRoot) { Remove-Item -LiteralPath $freshRoot -Recurse -Force } } +# The ZIP is reproducible defaults only. The deployment directory keeps explicitly local, +# publish-excluded inputs while all versioned defaults and binaries are replaced. +try { + if (Test-Path -LiteralPath $bundle) { + $resolvedBundle = (Resolve-Path -LiteralPath $bundle).Path + if (-not $resolvedBundle.StartsWith($outputRootPath + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { + throw 'Existing deployment Bundle escaped the validated output root.' + } + Get-ChildItem -LiteralPath $resolvedBundle -Force | + Where-Object Name -notin @('agent.local.yaml', 'local', 'logs', 'state') | + Remove-Item -Recurse -Force + } + else { New-Item -ItemType Directory -Path $bundle | Out-Null } + Copy-Item -Path (Join-Path $stagingBundle '*') -Destination $bundle -Recurse -Force +} +finally { + if (Test-Path -LiteralPath $stagingRoot) { Remove-Item -LiteralPath $stagingRoot -Recurse -Force } +} + Write-Output "Bundle: $bundle" Write-Output "ZIP: $zip" Write-Output "SHA-256: $checksum" diff --git a/scripts/Restart-PublishedServer.ps1 b/scripts/Restart-PublishedServer.ps1 new file mode 100644 index 0000000..58dd928 --- /dev/null +++ b/scripts/Restart-PublishedServer.ps1 @@ -0,0 +1,30 @@ +param( + [string]$BundlePath = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64', + [switch]$StopOnly +) + +$ErrorActionPreference = 'Stop' +$repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$bundle = [IO.Path]::GetFullPath((Join-Path $repository $BundlePath)) +$publishRoot = [IO.Path]::GetFullPath((Join-Path $repository 'build\Release\publish')) +if (-not $bundle.StartsWith($publishRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { + throw 'BundlePath must remain inside build\Release\publish.' +} +$agent = Join-Path $bundle 'agent-mcp.exe' +$configuration = Join-Path $bundle 'agent.yaml' + +$matching = Get-Process -Name 'agent-mcp' -ErrorAction SilentlyContinue | Where-Object { + try { [IO.Path]::GetFullPath($_.Path).Equals($agent, [StringComparison]::OrdinalIgnoreCase) } + catch { $false } +} +foreach ($process in $matching) { + Stop-Process -Id $process.Id -Force + $process.WaitForExit() + Write-Output "Stopped published agent-mcp PID $($process.Id)." +} +if ($StopOnly) { exit 0 } +if (-not (Test-Path -LiteralPath $agent) -or -not (Test-Path -LiteralPath $configuration)) { + throw 'Published agent-mcp executable or configuration is missing.' +} +$started = Start-Process -FilePath $agent -ArgumentList @('serve', '--config', $configuration) -WorkingDirectory $bundle -WindowStyle Hidden -PassThru +Write-Output "Started published agent-mcp PID $($started.Id)." diff --git a/scripts/Run-Final-ElevatedVerification.ps1 b/scripts/Run-Final-ElevatedVerification.ps1 index 9039c77..627bd65 100644 --- a/scripts/Run-Final-ElevatedVerification.ps1 +++ b/scripts/Run-Final-ElevatedVerification.ps1 @@ -12,6 +12,18 @@ if (-not $evidence.StartsWith($repository + [IO.Path]::DirectorySeparatorChar, [ } New-Item -ItemType Directory -Force -Path $evidence | Out-Null $failed = $false +$metadata = Join-Path $evidence 'metadata.txt' +$administrator = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator) +@( + "timestamp=$(Get-Date -Format o)" + "commit=$(git -C $repository rev-parse HEAD)" + "administrator=$administrator" + "working_tree_clean=$(-not [bool](git -C $repository status --porcelain))" + "os=$([Environment]::OSVersion.VersionString)" + "dotnet=$(& dotnet --version)" +) | Set-Content -LiteralPath $metadata -Encoding utf8NoBOM +if (-not $administrator) { throw 'Final elevated verification must run as Windows Administrator.' } function Invoke-VerificationStep { param([string]$Name, [scriptblock]$Action) @@ -50,6 +62,14 @@ Invoke-VerificationStep 'e2e-02-http-ipv4' { Invoke-VerificationStep 'e2e-02-http-ipv6' { & (Join-Path $PSScriptRoot 'Operational-E2E.ps1') -Transport http -HttpHost ::1 } +Invoke-VerificationStep 'residual-state' { + $journalCount = @(Get-ChildItem -LiteralPath (Join-Path $bundle 'state\journals') -File -ErrorAction SilentlyContinue).Count + $backupCount = @(Get-ChildItem -LiteralPath (Join-Path $bundle 'state\backups') -File -Recurse -ErrorAction SilentlyContinue).Count + $bundleAgent = Join-Path $bundle 'agent-mcp.exe' + $processCount = @(Get-Process -Name agent-mcp -ErrorAction SilentlyContinue | Where-Object { try { $_.Path -eq $bundleAgent } catch { $false } }).Count + "journal_count=$journalCount backup_count=$backupCount published_process_count=$processCount" + if ($journalCount -ne 0 -or $backupCount -ne 0 -or $processCount -ne 0) { throw 'Task Resource residue was detected.' } +} if ($failed) { Write-Error "Final elevated verification FAILED. Evidence: $evidence" diff --git a/skills/python/SKILL.md b/skills/python/SKILL.md index 966a4fd..867d74c 100644 --- a/skills/python/SKILL.md +++ b/skills/python/SKILL.md @@ -13,7 +13,7 @@ description: Pythonコードを作成・実行・検証する。計算、デー - 小さな修正で`file_edit`の`patch` operationを使う場合、`patch` fieldにはJSON文字列`{"old_text":"置換前","new_text":"置換後"}`を渡す。unified diffや`@@`形式を渡さない。置換前文字列はfile内で正確に1回だけ一致させる。 4. 承認済みのPython executableを直接`process_run`し、cwdとargsを分ける。shell、launcher、環境探索Toolを経由しない。 5. 通常のscriptは`-I