修复 Windows verbatim AppData 路径权限识别
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 2m2s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 2m15s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 2m13s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 2m13s
Project CI / AI game creator shell Rust smoke (push) Failing after 1m44s
Project CI / AI game creator shell Rust crates (push) Successful in 2m58s
Project CI / Native shell tests (push) Failing after 4m10s
Project CI / Frontend tests (push) Successful in 4m10s
Project CI / Repository checks (push) Successful in 3m4s
Project CI / AI game creator shell web tests (push) Successful in 2m32s
Project CI / Backend tests (push) Successful in 6m55s

归一化 \\?\ 与 \\?\UNC\ 路径后再判断受管 ACL scope

补充 verbatim AppData 回归测试
This commit is contained in:
2026-09-14 21:04:01 +08:00
parent 2bdee990a2
commit 1877aea33c
@@ -27,6 +27,7 @@ fn user_selected_path_grants() -> &'static Mutex<HashMap<String, UserSelectedPat
#[cfg(windows)]
fn normalize_user_selected_path_key(path: &Path) -> Option<String> {
let path = normalize_windows_policy_path(path);
if !path.is_absolute()
|| path
.components()
@@ -42,6 +43,15 @@ fn normalize_user_selected_path_key(path: &Path) -> Option<String> {
)
}
#[cfg(windows)]
fn normalize_windows_policy_path(path: &Path) -> PathBuf {
let value = path.to_string_lossy();
if let Some(rest) = value.strip_prefix(r"\\?\UNC\") {
return PathBuf::from(format!(r"\\{rest}"));
}
PathBuf::from(value.strip_prefix(r"\\?\").unwrap_or(&value))
}
#[cfg(windows)]
pub(crate) fn register_game_creator_user_selected_path(path: &Path, is_directory: bool) {
let Some(key) = normalize_user_selected_path_key(path) else {
@@ -838,6 +848,7 @@ pub(crate) fn validate_game_creator_private_path_ancestors(
/// separate, explicit user-selected scope below covers native picker/project
/// root results, including projects stored outside the current profile.
fn game_creator_private_path_allows_auto_elevation(path: &Path) -> bool {
let path = normalize_windows_policy_path(path);
if !path.is_absolute()
|| path
.components()
@@ -846,7 +857,10 @@ fn game_creator_private_path_allows_auto_elevation(path: &Path) -> bool {
return false;
}
let starts_with_path = |root: &Path| path == root || path.starts_with(root);
let starts_with_path = |root: &Path| {
let root = normalize_windows_policy_path(root);
path == root || path.starts_with(root)
};
if game_creator_runtime_config_dir()
.as_deref()
.is_some_and(starts_with_path)
@@ -1056,6 +1070,7 @@ pub(crate) fn parse_windows_acl_repair_scope(value: &str) -> Result<WindowsAclRe
#[cfg(windows)]
fn game_creator_runtime_config_repair_scope(path: &Path) -> WindowsAclRepairScope {
let path = normalize_windows_policy_path(path);
let is_builtin_root = |root: PathBuf| path == root || path.starts_with(root);
if let Some(home) = std::env::var_os("USERPROFILE")
.or_else(|| std::env::var_os("HOME"))
@@ -4506,6 +4521,23 @@ mod private_path_elevation_policy_tests {
);
}
#[cfg(windows)]
#[test]
fn verbatim_packaged_appdata_path_keeps_managed_repair_scope() {
let root = std::env::var_os("LOCALAPPDATA")
.or_else(|| std::env::var_os("APPDATA"))
.map(PathBuf::from)
.expect("local appdata");
let packaged = root.join("world.genarrative.ai-game-creator");
let verbatim = PathBuf::from(format!(r"\\?\{}", packaged.display()));
assert!(game_creator_private_path_allows_auto_elevation(&verbatim));
assert_eq!(
game_creator_runtime_config_repair_scope(&verbatim),
WindowsAclRepairScope::Managed
);
}
#[cfg(windows)]
#[test]
fn picker_grant_is_required_and_directory_grant_covers_descendants() {