T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/task_registry.py:134- Finding
Cross-Account Registry Disclosure and Session-Boundary Contamination
- Content
View full analysis
Vulnerability Details
File Location:
scripts/task_registry.py:134-163, 178-188
Vulnerability Type: Cross-account access-control violation
Risk Level: HighVulnerable Code
python def find_cross_account_candidates(path: Path) -> list[Path]: try: account_dir = path.parent root = account_dir.parent peer_name = path.name except Exception: return [] if not root.exists(): return [] candidates = [] peer_aliases = {p.name for p in alias_registry_paths(path)} for other_account_dir in root.iterdir(): if not other_account_dir.is_dir() or other_account_dir == account_dir: continue for peer_alias in peer_aliases: candidate = other_account_dir / peer_alias if candidate.exists(): candidates.append(candidate) return sorted(set(candidates)) def restore_cross_account_registry(path: Path, data: dict) -> tuple[dict, list[str]]: restored_from = [] merged = data for candidate in find_cross_account_candidates(path): other = load_json_file(candidate) if not other or not other.get("tasks"): continue if not merged.get("tasks") or len(other.get("tasks", [])) > len(merged.get("tasks", [])): merged = merge_registries(other, merged) restored_from.append(str(candidate)) return merged, restored_fromThe cross-account restoration is invoked automatically when a registry does not exist or contains no tasks:
python def load_registry(path: Path) -> dict: resolved = choose_registry_path(path) if not resolved.exists(): data = default_registry() data, restored_from = restore_cross_account_registry(path, data) save_registry(path, data) if restored_from: data["_restoredFrom"] = restored_from return data data = load_json_file(resolved) or default_registry() restored_from = [] if not data. ...[truncated 3056 chars]- Remediation
View remediation
Remediation Suggestions
-
Remove automatic cross-account discovery and restoration:
- Delete or disable
find_cross_account_candidates(). - Delete or disable
restore_cross_account_registry(). - Ensure
load_registry()only reads the exact registry path for the current account and peer.
- Delete or disable
-
Preserve strict namespace isolation:
- Resolve and normalize the registry path.
- Verify that the resolved path remains under the expected
tasks/weixin/<account>directory. - Reject traversal components, symbolic-link escapes, and paths outside the assigned account namespace.
-
Treat migration as a privileged operation:
- If cross-account migration is a legitimate requirement, implement it as an explicit administrative command.
- Require authorization for both the source and destination accounts.
- Display the source, destination, and records to be migrated before confirmation.
- Maintain an audit log rather than silently importing data.
-
Validate session ownership before use:
- Bind each stored session key to its creating account and peer.
- Before calling
sessions_historyorsessions_send, verify that the session belongs to the active account/peer. - Do not rely solely on possession of a session key as authorization.
- Prefer restricted session visibility over
sessions.visibility=all.
-
Avoid copying foreign identifiers:
- Never merge session keys across account namespaces.
- If task metadata must be migrated, create new destination sessions and exclude source session identifiers.
-
Add regression tests that verify:
- A missing registry does not inspect sibling account directories.
- An empty registry remains empty rather than importing another account's tasks.
- Identical peer names under different accounts remain isolated.
- A session key created for one account is rejected when used from another.
- Registry aliases are confined to the current account directory.
-
