diff --git a/apps/ai-game-creator-shell/src-tauri/src/commands.rs b/apps/ai-game-creator-shell/src-tauri/src/commands.rs index c81dd2663..b3cc58f28 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/commands.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/commands.rs @@ -517,11 +517,7 @@ pub(crate) fn create_automatic_local_game_project_at( match fs::create_dir(&project_root) { Ok(()) => { let result = (|| { - prepare_game_creator_private_path_for_read( - &project_root, - true, - "自动项目目录", - )?; + harden_new_game_creator_private_path(&project_root, true, "自动项目目录")?; enforce_project_permission_policy(&project_root, "project.create")?; let _lock = acquire_project_write_lock(&project_root, "project.create")?; init_local_game_project_at( diff --git a/apps/ai-game-creator-shell/src-tauri/src/config.rs b/apps/ai-game-creator-shell/src-tauri/src/config.rs index 3240832c2..25c94d17d 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/config.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/config.rs @@ -1299,15 +1299,12 @@ pub(crate) fn ensure_game_creator_private_directory_tree( #[cfg(all(windows, test))] initialize_windows_game_creator_directory_owner_for_current_user(&directory)?; #[cfg(windows)] - if game_creator_private_path_allows_auto_elevation(&directory) { - secure_windows_game_creator_path_for_current_user_with_auto_elevation( - &directory, true, true, - )?; - } else { - secure_windows_game_creator_path_for_current_user_with_owner_policy( - &directory, true, true, true, - )?; - } + // This invocation created the directory, so initialize its + // owner/DACL in-process. Marker-based managed-path detection + // must not route a newly-created descendant into UAC. + secure_windows_game_creator_path_for_current_user_with_owner_policy( + &directory, true, true, true, + )?; #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; @@ -1346,15 +1343,11 @@ pub(crate) fn ensure_game_creator_private_directory_tree( fs::create_dir(&directory).map_err(|retry_error| { format!("创建 {label} 失败:{}: {retry_error}", directory.display()) })?; - if game_creator_private_path_allows_auto_elevation(&directory) { - secure_windows_game_creator_path_for_current_user_with_auto_elevation( - &directory, true, true, - )?; - } else { - secure_windows_game_creator_path_for_current_user_with_owner_policy( - &directory, true, true, true, - )?; - } + // The retry also created this directory in the current + // process; keep it on the local hardening path. + secure_windows_game_creator_path_for_current_user_with_owner_policy( + &directory, true, true, true, + )?; } Err(error) => { return Err(format!( diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 60d4694c4..74e1c6273 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -156,6 +156,7 @@ - 背景:#211 要求 sidecar 满足当前用户独占、禁止继承的 DACL。新建文件会先继承父目录 ACE,生产路径把这种短暂不合格送进 UAC;`project.lock` 还在独占句柄上 harden。含空格项目路径上提权 ArgumentList 被拆开,修复以 exit 1 失败。GDD 审批改意见因此弹权限,V1 锁创建不会。 - 决策:`harden_new_game_creator_private_path` 只在本进程收紧 owner/DACL,失败则删除刚创建的对象,不 UAC 接管。项目锁先写再释放句柄再 harden,并用内容回读防换绑;UAC 仍只用于允许范围内的已有外人本对象。提权 helper 的 ArgumentList 改为一条按 Windows 规则加引号的字符串。 +- 补充:逐级创建 `.agent`、`runtime`、`locks` 等目录时,即使祖先已有 `manifest.json`,刚由本进程创建的目录也必须直接走 owner/DACL 初始化,不能因 managed-path 判定进入 UAC;自动项目根目录同样在创建成功后立即本地加固。 - 影响范围:`config.rs` 的新建 harden 与提权命令行、`project/write_lock.rs` 的项目锁创建;不改变锁竞争、失效回收、Drop 删除,也不放宽 symlink / reparse / 外人本 fail-closed。 - 验证方式:Windows 定向测试覆盖 `Genarrative GameAgent\gameagent-*` 取锁与私有 DACL,以及带空格路径的 quoted ArgumentList。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/pitfalls.md`。