T09 · Insecure Skill Coding Practices
- Location
utils/ticktick_api.py:11- Finding
Configurable TickTick API Base Can Exfiltrate Bearer Tokens and Task Data
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This appears to be a real task-sync skill, but it can automatically delete real Google Tasks and TickTick data with insufficient safeguards.
Review carefully before installing. Use only dedicated test accounts or empty test lists until safeguards are added, do not enable cron on important task data, restrict token file permissions, pin the TickTick API endpoint to the official host, and add dry-run or confirmation controls before any delete propagation.
utils/ticktick_api.py:11Configurable TickTick API Base Can Exfiltrate Bearer Tokens and Task Data
sync.py:274Ambiguous API Errors Are Interpreted as Remote Task Deletions
sync.py:132TickTick Project Enumeration Failure Can Delete All Mapped Google Lists
sync.py:408Smart-List Cleanup Deletes Unrelated Tasks from Existing Google Lists
scripts/setup_google_tasks.py:87OAuth Tokens and Client Secrets Are Stored Without Enforced Restrictive Permissions
README.md:78Unpinned Third-Party Dependencies Create a Mutable Supply-Chain Risk
The declared description covers a broad synchronization skill between TickTick and Google Tasks, with sync logic, mapping, exports, and troubleshooting. The supplied code chunk does only one narrow setup function: TickTick OAuth authorization and token storage. While OAuth setup is mentioned in the description as one possible use case, the code does not implement the primary declared behavior of synchronization or any Google-side functionality. This is therefore a material description-behavior mismatch rather than a supporting partial implementation of the full declared skill.
This test suite performs live creation, completion, and deletion-like lifecycle changes against real Google Tasks and TickTick data, with no guardrails to ensure it is operating on a dedicated test account or sandbox. In the context of an agent skill, that is dangerous because accidental execution can modify or destroy a user's real task data at scale.
The script deletes a Google task list programmatically without confirmation or robust ownership checks. If the wrong list ID is used or the test runs against a real account, this can permanently remove user-organized data and cause broader data loss than individual task completion.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
SCOPES = ["https://www.googleapis.com/auth/tasks"]
BASE_DIR = Path(__file__).resolve().parent.parent
DEFAULT_CONFIG_FILE = BASE_DIR / "config.json"
DEFAULT_CREDENTIALS_FILE = BASE_DIR / "config" / "google_credentials.json"
DEFAULT_TOKEN_FILE = BASE_DIR / "data" / "google_token.json"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
SCOPES = ["https://www.googleapis.com/auth/tasks"]
BASE_DIR = Path(__file__).resolve().parent.parent
DEFAULT_CONFIG_FILE = BASE_DIR / "config.json"
DEFAULT_CREDENTIALS_FILE = BASE_DIR / "config" / "google_credentials.json"
DEFAULT_TOKEN_FILE = BASE_DIR / "data" / "google_token.json"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"scope": "tasks:read tasks:write",
}
print("\nRequesting access token...")
response = requests.post(token_url, data=data, timeout=30)
if response.status_code == 200:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
if response.status_code == 200:
token_data = response.json()
print("Access token received.")
return token_data
print(f"Failed to get token ({response.status_code}): {response.text}")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"ticktick_token": "/path/to/ticktick/token.json", "sync_db": "/path/to/sync_db.json", "sync_log": "/path/to/sync_log.json", "ticktick_api_base": "https://api.ticktick.com/open/v1" }
The README instructs users to run an end-to-end test suite against live Google Tasks and TickTick APIs but does not explicitly warn that the tests can create, modify, complete, or delete real user task data. In a bidirectional sync skill, this omission is risky because users may run tests against production accounts and unintentionally alter or lose task state across both services.
The skill invokes Python scripts, performs OAuth setup, and references cron, logs, config files, and external APIs, yet it declares no explicit tool scope or permissions boundaries. In an agent environment, this can cause over-broad access to shell, network, environment variables, and file writes without clear user-visible constraints, increasing the chance of unsafe execution or secret exposure.
The setup instructions direct users to perform OAuth flows, configure token paths, and run synchronization with third-party services, but they do not explicitly warn that access tokens will be stored locally and that task titles, notes, dates, and completion data will be transmitted externally. This weakens informed consent and can lead to accidental exposure of sensitive task content or insecure handling of credential files.
This Python file reads Google and TickTick tokens from configuration and immediately initializes API clients that will act on remote task data. While the module docstring says it is an E2E sync test, there is no explicit warning in the file that credentials are being used and that task data will be sent to third-party services.
The helper runs sync.py via subprocess, and that sync operation can modify tasks across Google Tasks and TickTick. Although there is a console message saying "Running sync...", it does not clearly warn that the subprocess may change remote user data as part of the test.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def run_sync():
"""Run sync.py and return output."""
result = subprocess.run(
[PYTHON_BIN, SYNC_SCRIPT],
capture_output=True, text=True, cwd=BASE_DIR, timeout=120,
)
The executable used by subprocess.run is taken from the PYTHON_BIN environment variable without validation. An attacker who can influence the environment could cause the test to execute an arbitrary binary instead of Python, leading to unintended code execution with access to the same local files and API tokens used by the test.
def run_sync():
"""Run sync.py and return output."""
result = subprocess.run(
[PYTHON_BIN, SYNC_SCRIPT],
capture_output=True, text=True, cwd=BASE_DIR, timeout=120,
)
This code removes an existing token file before proceeding and later writes a replacement token file, which is a safety-relevant file modification. Although there are print statements describing the paths, the script does not ask the user to confirm the deletion or overwrite before performing it.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
}
print("\nRequesting access token...")
response = requests.post(token_url, data=data, timeout=30)
if response.status_code == 200:
token_data = response.json()
The sync logic automatically deletes Google lists, Google tasks, and TickTick tasks when it infers the counterpart was deleted or is missing, with no dry-run, confirmation gate, conflict review, or safety threshold. In a bidirectional sync skill that manages user task data, mistaken mappings, transient API errors, stale caches, or inconsistent remote state can cause irreversible data loss across both services.
No suspicious patterns detected.