Back to skill

Security audit

Ticktick Cli

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent TickTick command-line skill whose OAuth and task-write behavior is disclosed, though users should treat its stored credentials as sensitive.

Install only if you are comfortable granting the CLI TickTick tasks:read and tasks:write access. Avoid entering real client secrets in shared terminals or logs, protect or exclude ~/.clawdbot/credentials/ticktick-cli/config.json from backups/sync, and review task IDs carefully before using batch-abandon because it changes multiple tasks without a separate confirmation step.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ticktick.ts:29
Finding
OAuth Client Secret Exposed Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ticktick.ts:29-33` **Additional Location**: `SKILL.md:20-23`, `SKILL.md:33-36` **Vulnerability Type**: Sensitive credential exposure through process arguments and shell history **Risk Level**: Medium ### Vulnerable Code ```ts authCmd .option("--client-id <id>", "TickTick OAuth client ID") .option("--client-secret <secret>", "TickTick OAuth client secret") .option("--manual", "Manual auth flow for headless servers (paste redirect URL)") .option("--logout", "Clear authentication tokens") .option("--status", "Check authentication status") ``` The documented invocation explicitly places the secret on the command line: ```bash bun run scripts/ticktick.ts auth --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET ``` ### Technical Analysis The CLI accepts the OAuth client secret as a command-line option. Command-line arguments are not an appropriate secret-input channel because they may be retained in shell history, terminal session logs, automation logs, audit telemetry, or process-monitoring systems. Depending on operating-system controls, process arguments may also be observable by other local processes while authentication is running. This exposure is not required for the Skill's declared TickTick functionality. The secret can instead be collected through protected interactive input or a system credential manager. ### Attack Path 1. A user follows the documented authentication command and supplies the real client secret through `--client-secret`. 2. The shell records the complete command in its history or an automation platform records it in execution logs. 3. A local attacker, support process, backup collector, or user with access to those records retrieves the secret. 4. The attacker uses the exposed OAuth application credentials in attacks against the application's OAuth flow. The practical impact depends on TickTick's OAuth controls and whether the attacker can also obt ...[truncated 529 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--client-secret` as the recommended secret-input mechanism. 2. Prompt interactively using a hidden-input implementation that disables terminal echo. 3. Prefer storage and retrieval through the operating system's credential manager or keychain. 4. If environment-variable support is necessary for automation, document that CI systems must use masked secret variables and must not echo commands. 5. Keep the client ID as a normal option because it is not generally confidential, but handle the client secret separately. 6. Update `SKILL.md` so examples never place real secrets directly in shell commands. 7. Warn users to remove any previously entered commands from shell history and rotate secrets that may have entered shared logs. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/auth.ts:46
Finding
OAuth Credentials and Reusable Tokens Stored in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `scripts/auth.ts:8-10`, `scripts/auth.ts:46-50` **Additional Location**: `scripts/auth.ts:246-253`, `SKILL.md:193-207` **Vulnerability Type**: Plaintext storage and incomplete removal of sensitive authentication material **Risk Level**: Low ### Vulnerable Code ```ts const CONFIG_DIR = join(homedir(), ".clawdbot", "credentials", "ticktick-cli"); const CONFIG_FILE = join(CONFIG_DIR, "config.json"); ``` ```ts export async function saveConfig(config: TickTickConfig): Promise<void> { await ensureConfigDir(); await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 }); await chmod(CONFIG_DIR, 0o700).catch(() => {}); await chmod(CONFIG_FILE, 0o600).catch(() => {}); } ``` The persisted configuration contains all of the following sensitive values: ```ts export interface TickTickConfig { clientId: string; clientSecret: string; accessToken?: string; refreshToken?: string; tokenExpiry?: number; redirectUri?: string; } ``` Logout removes tokens but intentionally preserves the OAuth client credentials: ```ts export async function logout(): Promise<void> { const config = await loadConfig(); if (config) { delete config.accessToken; delete config.refreshToken; delete config.tokenExpiry; await saveConfig(config); console.log("Logged out successfully. Credentials preserved."); } else { console.log("No configuration found."); } } ``` ### Technical Analysis The application serializes the client secret, access token, and refresh token directly into a JSON file. The use of directory mode `0700` and file mode `0600` is a meaningful mitigation against access by other local users. However, these permissions do not protect the credentials from malware, compromised processes, backups, debugging tools, or attackers operating under the same user account. A refresh token is particularly sensitive because it may permit the acquisition of new access tokens aft ...[truncated 1576 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store the client secret, access token, and refresh token in the operating system's credential manager rather than in plaintext JSON. 2. Keep only non-sensitive metadata, such as token expiry and redirect URI, in the configuration file. 3. Add a full logout or `--purge` operation that securely removes tokens and client credentials. 4. Clearly distinguish between token logout and complete credential removal in command output and documentation. 5. Continue enforcing `0700` on the directory and `0600` on any fallback file. 6. Validate ownership and permissions before reading an existing credential file, and reject symlinks or unexpectedly permissive files. 7. Where a credential manager is unavailable, encrypt sensitive values using a key protected by platform facilities rather than storing the key beside the ciphertext. 8. Advise users to exclude the credential directory from backups, synchronization tools, diagnostics, and support bundles. ]]>

T08 · Insecure Dependencies

Note
Location
package.json:11
Finding
Non-Reproducible Dependency Resolution Due to Version Ranges and Missing Lockfile<![CDATA[ ## Vulnerability Details **File Location**: `package.json:11-17` **Vulnerability Type**: Unpinned dependency and supply-chain exposure **Risk Level**: Low ### Vulnerable Code ```json "dependencies": { "commander": "^12.0.0", "open": "^10.0.0" }, "devDependencies": { "@types/bun": "^1.0.0" } ``` No Bun lockfile is present in the audited project structure. ### Technical Analysis Caret version ranges allow package installations to resolve dependency releases that differ from those originally reviewed. Without a committed lockfile, direct and transitive dependency resolution is not reproducible. No evidence was found that the currently declared packages are malicious, typosquatted, or loaded from unsafe registries. The finding concerns future or environment-dependent resolution: a compromised package release, compromised transitive dependency, or unexpected compatible update could be installed without a corresponding source review. This risk is significant in context because imported dependencies execute in the same user context as the CLI. The process can access the plaintext TickTick credential file and receives OAuth authorization URLs. ### Attack Path 1. A user or deployment environment installs the project without a lockfile. 2. The package manager resolves a newer release permitted by a caret range, or resolves changed transitive dependencies. 3. A newly resolved package version contains compromised installation or runtime code. 4. The compromised dependency executes during installation or when the CLI imports it. 5. That code reads local TickTick credentials, modifies CLI behavior, or sends task and token data to an attacker-controlled destination. This is a conditional supply-chain path; the audit found no evidence that the currently named dependencies are themselves malicious. ### Impact Assessment A compromised runtime dependency would execute with the privileges of the user running the CLI. It could potentially read the credent ...[truncated 313 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate and commit the Bun lockfile produced by the supported Bun version. 2. Require immutable or frozen-lockfile installation in CI and deployment workflows. 3. Review lockfile changes as security-sensitive code changes. 4. Use automated dependency monitoring and vulnerability scanning. 5. Pin direct dependencies more narrowly where operationally practical. 6. Verify package provenance and registry configuration, and avoid installation from untrusted registries. 7. Periodically update dependencies through controlled, reviewed changes rather than allowing unreviewed resolution at installation time. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documentation claims a bounded TickTick-management purpose, but it also introduces sensitive OAuth handling, localhost callback behavior, and plaintext credential storage that are not fully surfaced in the top-level description. This mismatch can mislead users or agents about the true security-sensitive behavior of the skill, causing them to invoke it without understanding token capture and local secret persistence risks.

Credential Access

High
Category
Privilege Escalation
Content
if (!config.accessToken) {
    throw new Error(
      "No access token found. Run 'ticktick auth' to authenticate."
    );
  }
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares shell and network-capable behavior but does not define any explicit tool scope such as allowed-tools or permissions. In an agent setting, this ambiguity can let the runtime grant broader capabilities than users expect, increasing the risk of unintended command execution or outbound network access during task handling.

Session Persistence

Medium
Category
Rogue Agent
Content
### 1. Register a TickTick Developer App

1. Go to [TickTick Developer Center](https://developer.ticktick.com/manage)
2. Create a new application
3. Set the redirect URI to `http://localhost:8080`
4. Note your `Client ID` and `Client Secret`
Confidence
93% confidence
Finding
The skill instructs users to persist OAuth client credentials, access tokens, and refresh tokens in a local config file, explicitly noting plaintext storage. If the host is compromised, backups are exposed, or file permissions are misapplied, an attacker can reuse these tokens to access or manipulate the user's TickTick data without re-authentication.

External Transmission

Medium
Category
Data Exfiltration
Content
import { getValidToken } from "./auth";

const API_BASE = "https://api.ticktick.com/open/v1";

export interface Project {
  id: string;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import { getValidToken } from "./auth";

const API_BASE = "https://api.ticktick.com/open/v1";

export interface Project {
  id: string;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import { getValidToken } from "./auth";

const API_BASE = "https://api.ticktick.com/open/v1";

export interface Project {
  id: string;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code performs a destructive state-changing operation by marking tasks as abandoned via a batch API call. Although results are logged afterward, there is no confirmation prompt or explicit user-facing warning before the irreversible action executes.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"ticktick": "./scripts/ticktick.ts"
  },
  "dependencies": {
    "commander": "^12.0.0",
    "open": "^10.0.0"
  },
  "devDependencies": {
Confidence
94% confidence
Finding
The dependency on commander uses a caret range, which allows newer minor/patch releases to be installed without explicit review. This creates supply-chain risk and can lead to non-reproducible builds if an upstream release becomes compromised or introduces unsafe behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "commander": "^12.0.0",
    "open": "^10.0.0"
  },
  "devDependencies": {
    "@types/bun": "^1.0.0"
Confidence
98% confidence
Finding
The open dependency is not pinned and is fetched using a caret range, so different installs may resolve to different upstream releases. This is more dangerous here because open interacts with the local system to launch URLs/apps, and the package also has a known command-injection advisory in some versions, making version ambiguity a meaningful security risk.

Unverifiable Dependency: open has 1 known advisory(ies) (GHSA-28xh-wpgr-7fm8 (Command Injection in open)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding
The manifest includes open, which has a known command-injection advisory in some versions, but the package.json does not pin the version, so it is impossible to verify whether the installed dependency is vulnerable. In a CLI skill that is likely to open OAuth URLs or user-influenced links on the local machine, unresolved exposure to a command-injection flaw is materially risky.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"open": "^10.0.0"
  },
  "devDependencies": {
    "@types/bun": "^1.0.0"
  }
}
Confidence
83% confidence
Finding
The devDependency @types/bun is also unpinned, allowing unreviewed upstream changes into the development environment. While this is less severe than a runtime dependency, it still weakens build reproducibility and can expose developers or CI pipelines to supply-chain issues.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The code explicitly formats dates with "en-US", which imposes a specific locale regardless of the user's settings or preferences. This matches the policy category for language/locale constraints because there is no opt-in, fallback to system locale, or documented region-specific reason.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/auth.ts:103