T09 · Insecure Skill Coding Practices
- Location
scripts/unifi.py:149- Finding
UniFi Network Inventory Cached Without Enforced Restrictive Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/unifi.py:28andscripts/unifi.py:149-163
Vulnerability Type: Plaintext storage of sensitive network metadata with permissions inherited from the process umask
Risk Level: MediumVulnerable Code
python CACHE_FILE = Path.home() / ".openclaw" / "unifi-skill.json"python library.append({ "label": label, "name": name, "hostId": host_id, "siteId": site_id, "hardware": hw.get("name", ""), "shortname": hw.get("shortname", ""), "firmwareVersion": hw.get("firmwareVersion", ""), "state": rs.get("state", ""), "apiIntegration": has_api, "hasNetwork": has_network, "timezone": rs.get("timezone", ""), "ip": rs.get("ip", ""), }) CACHE_FILE.write_text(json.dumps({"library": library, "updatedAt": time.time()}, indent=2))Technical Analysis
The Skill stores discovered UniFi infrastructure metadata in plaintext at
~/.openclaw/unifi-skill.json. The cached data includes host and site identifiers, names, IP addresses, hardware models, firmware versions, controller state, timezone, and API capability information.Path.write_text()creates or replaces the file using permissions determined by the process umask. The implementation does not explicitly ensure that the parent directory is owner-only or that the cache file has mode0600. On systems with permissive directory or file permissions, another local user may be able to read the cached inventory.The API key is not written to this cache. Credential transmission elsewhere in the script is limited to the fixed
https://api.ui.comorigin and is necessary for the declared UniFi API functionality.Attack Path
- A user with a configured `UN ...[truncated 1415 chars]
- Remediation
View remediation
Remediation Suggestions
- Create
~/.openclawwith owner-only permissions (0700) before writing the cache. - Create the cache file explicitly with mode
0600rather than relying on the process umask. - Write to a securely created temporary file in the same directory, flush and synchronize it, set its permissions, and atomically replace the destination.
- Reject symbolic-link destinations and verify that the cache and parent directory are owned by the current user.
- Correct permissions on existing cache files during startup.
- Minimize cached data by excluding fields that are not needed for site resolution, particularly IP addresses and firmware details.
- Consider disabling caching or providing an opt-out option for shared or otherwise untrusted hosts.
Example hardening approach:
python CACHE_FILE.parent.mkdir(mode=0o700, parents=True, exist_ok=True) os.chmod(CACHE_FILE.parent, 0o700) flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(CACHE_FILE, flags, 0o600) try: with os.fdopen(fd, "w", encoding="utf-8") as cache: json.dump( {"library": library, "updatedAt": time.time()}, cache, indent=2, ) os.chmod(CACHE_FILE, 0o600) except Exception: try: os.close(fd) except OSError: pass raise- Create
