PRIVACY DATA EXFILTRATION PARTIALLY REMEDIATED STATIC ANALYSIS

OneDragon-Anything / ZenlessZoneZero-OneDragon

Privacy Policy Violation & Undisclosed Data Exfiltration

DATE
2026-03-18
SEVERITY
CRITICAL — privacy violation
REPORTED TO
GitHub Issues (public)
STATUS
PARTIALLY REMEDIATED
RESEARCHER
Ahlyx (@AhIyxx)
DISCLOSURE TYPE
Responsible disclosure — 30-day window
// REPORT SUMMARY
FIELDVALUE
RepositoryOneDragon-Anything/ZenlessZoneZero-OneDragon
Repository URLgithub.com/OneDragon-Anything/ZenlessZoneZero-OneDragon
Discovery MethodAutomated secret scanner (Grafana token) → manual code review
Initial FindingHardcoded Grafana Cloud auth token in config.py
Escalated FindingTelemetry system with privacy policy violations and data exfiltration
Original SeverityCRITICAL
Reported ToGitHub Issues (public issue filed)
StatusPartially remediated — three of four findings patched, one remains
Files Involvedtelemetry_manager.py, event_collector.py, config.py, game_account_config.py
// OVERVIEW

ZenlessZoneZero-OneDragon is an open-source game automation tool for the miHoYo title Zenless Zone Zero. It automates daily tasks, manages multiple game accounts, and handles auto-login using stored credentials. The software is distributed on GitHub and has an active user base primarily in China.

This report was initiated when an automated secret scanner flagged a hardcoded Grafana Cloud authentication token in the repository. Manual review of the surrounding codebase revealed a significantly more serious issue: a purpose-built telemetry system that contradicted the project's published privacy policy in multiple ways, including the collection and transmission of game login usernames to an Alibaba Cloud server without user disclosure or consent.

// HOW IT WAS FOUND
STEP 1 Automated Secret Scanner

The initial discovery came from running an automated secret scanner against recently updated public GitHub repositories. The scanner flagged a Grafana Cloud authentication token hardcoded in src/zzz_od/telemetry/config.py. This token authenticated against a Loki log aggregation instance and was also being written to users' local config/telemetry.yml files on first run.

STEP 2 Manual Code Review

After finding the Grafana token, the full telemetry subdirectory was retrieved and analysed across 15 source files. Review revealed that the Grafana/Loki backend was fully commented out and inactive — the real active backend was Alibaba Cloud SLS (Simple Log Service). This shifted the investigation from a credential leak to a data collection audit.

STEP 3 Privacy Policy Comparison

The project's PRIVACY.md was retrieved and compared against the code behaviour. The policy explicitly states that collected data is anonymous and that running configuration information is stored only on the user's local device. Both claims were found to be directly contradicted by the code.

// TECHNICAL FINDINGS
FINDING 1 Anonymization Forcibly Disabled CRITICAL
FILE
telemetry_manager.py
POLICY CONTRADICTION
Policy claims data is anonymous
PATCH STATUS
PATCHED — commit b99f5ff1

The PrivacySettings data model defines anonymize_user_data: bool = True as the default. However, TelemetryManager.initialize() explicitly overrode this value after loading settings from disk, making it impossible for any user configuration to take effect. All three data collection flags were also force-enabled in the same function regardless of saved user preferences.

self.privacy_settings.anonymize_user_data = False self.privacy_controller.settings.anonymize_user_data = False # both lines in TelemetryManager.initialize() — overrides any saved user preference

With the flag set to False, the PrivacyController fell back to a weaker filter-only mode that still passed many values through unmodified. Removed in commit b99f5ff1 on 2026-04-16.

FINDING 2 Game Login Usernames Sent to Alibaba Cloud CRITICAL
FILE
telemetry_manager.py
ENDPOINT
zzz-od-1.cn-hangzhou.log.aliyuncs.com
PATCH STATUS
PATCHED — commit b99f5ff1

The method _report_multi_account_usage() fired during initialization for any user with more than three game accounts configured. It read the account field from GameAccountConfig for each instance — the same credential used by the auto-login feature to authenticate with game servers. The payload included a list of raw login usernames, account count, and a persistent device identifier.

Critically, the two methods responsible for applying privacy filtering and sanitization to all other telemetry events explicitly skipped this event:

def _should_apply_privacy(self, event_name: str) -> bool: return event_name != 'multi_account_usage' def _should_apply_sanitizer(self, event_name: str) -> bool: return event_name != 'multi_account_usage' # payload bypassed every privacy and sanitization layer before transmission
FINDING 3 Raw Hostname Sent on Every Launch — Unresolved HIGH
FILE
telemetry_manager.py
ENDPOINT
zzz-od-1.cn-hangzhou.log.aliyuncs.com
PATCH STATUS
PARTIALLY PATCHED — persists

At time of disclosure, track_app_launch() in event_collector.py constructed a machine_id string using socket.gethostname() and platform.machine(), then sent it as a plaintext property on every application launch. The hostname was not in PrivacyController.SENSITIVE_KEYS, so even the partial filtering that ran on other events did not redact this value.

Status: Partially patched. event_collector.py was deleted in commit b99f5ff1, but the current telemetry_manager.py continues to send platform.node() (the raw computer hostname) on every launch:

self._send_event("app_launched", { "machine_id": f"{platform.node()}-{platform.machine()}", ... }) # _generate_user_id() derives a persistent UUID from the same hostname string: @staticmethod def _generate_user_id() -> str: machine_id = f"{platform.node()}-{platform.machine()}" return str(uuid.uuid5(uuid.NAMESPACE_DNS, machine_id))

Users are still trackable across sessions via a stable identifier derived from their computer name. There is no opt-out mechanism visible in the current codebase. The Alibaba Cloud SLS endpoint remains active and unchanged.

FINDING 4 Leaked Grafana Credential MEDIUM
FILE
config.py
PATCH STATUS
PATCHED — commit 7228bc70

The Grafana Cloud authentication token was hardcoded as a fallback value in config.py and written to users' local config/telemetry.yml on first run. The Loki backend was inactive at time of discovery, so this did not affect users directly, but the token was publicly exposed in the repository. _load_from_env() was removed in commit 7228bc70 on 2026-03-22. The token should be rotated.

// DATA FLOW SUMMARY (AT TIME OF DISCLOSURE)

For a user with more than three game accounts configured, on every application launch:

Step 1 App starts and TelemetryManager.initialize() is called silently
Step 2 anonymize_user_data forcibly set to False, overriding any saved user preference
Step 3 AliyunWebTrackingClient initialized pointing to zzz-od-1.cn-hangzhou.log.aliyuncs.com
Step 4 Persistent device UUID generated from socket.gethostname() + platform.machine()
Step 5 app_launched event fires, sending the raw hostname string to Alibaba Cloud
Step 6 _report_multi_account_usage() reads the account field (login username) for each configured instance
Step 7 Privacy filter SKIPPED — hardcoded bypass for this event
Step 8 Data sanitizer SKIPPED — hardcoded bypass for this event
Step 9 Payload with raw login usernames, account count, and machine UUID sent to Alibaba Cloud SLS
// PRIVACY POLICY CONTRADICTIONS
POLICY CLAIMSCODE BEHAVIOUR (AT DISCLOSURE)CURRENT STATUS
Data collected is anonymous anonymize_user_data force-set to False at runtime Patched
Configuration info stored only on local device Login usernames sent to Alibaba Cloud SLS Patched
Only anonymous error logs and performance metrics collected Hostname, login usernames, and machine UUID collected Partially patched — hostname persists
// WHAT WAS NOT FOUND
// NOT IN SCOPE
  • Password exfiltration — the .password field exists in GameAccountConfig alongside .account, but no telemetry code was found reading the password field
  • Active Grafana/Loki collection — the token was leaked but the backend was inactive
  • Payment data collection — miHoYo handles payments in-browser; the app does not have access to that flow
// NOTABLE OBSERVATIONS
  • The TelemetryUISupport class had a full privacy settings backend built, suggesting anonymization was intended to be user-controllable — it was never surfaced in the UI and has since been deleted
  • Neither remediation commit references the disclosed issue; critical fixes were folded into unrelated commit messages
// MAINTAINER RESPONSE

A maintainer (m1boy) responded to the GitHub issue with the following (reproduced for accuracy):

"The key retained for this project has been deprecated. The related services have been discontinued and will be removed in future updates. [...] This data is stored separately on secure servers and has not been utilized to date. We also hope that it never will be used in the future. [...] If you cannot accept our approach and decide to stop running this project, no further reports will be sent."

The acknowledgment that data was stored on external servers confirms collection occurred. The response does not address the privacy policy contradictions directly, does not commit to updating the policy, and does not mention the persistent hostname collection that remains in the current codebase.

// REMEDIATION TIMELINE
2026-03-18 Automated scanner flags Grafana token in config.py
2026-03-18 Manual review of telemetry module begins
2026-03-18 Privacy policy violations confirmed across four findings
2026-03-18 GitHub issue filed with full technical documentation
2026-03-22 Maintainers remove Grafana token loading in commit 7228bc70 ("chore: remove dead code") — 4 days after disclosure
2026-04-16 Remaining 10 telemetry files deleted in commit b99f5ff1, buried inside a character gameplay feature commit ("feat: 希希芙识别") — 29 days after disclosure, one day before the 30-day window
2026-04-17 Maintainer response received via GitHub issue
2026-04-17 Public disclosure — 30-day window elapsed
// CURRENT STATUS (2026-04-17)

Findings 1, 2, and 4 have been patched. Finding 3 (hostname collection) persists in the refactored telemetry_manager.py. Users are still tracked by a deterministic UUID derived from their computer name on every launch, transmitted to Alibaba Cloud SLS with no opt-out mechanism and no disclosure in the current privacy policy.

Users who ran this software with more than three game accounts configured between the initial telemetry implementation and the April 16 patch should assume their game login usernames were transmitted to Alibaba Cloud SLS.