Back to skill

Security audit

Wanikani

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says: it syncs WaniKani data to a local SQLite database and provides local analysis queries, with some normal privacy precautions needed.

Use this only with a WaniKani token you are comfortable using for local sync. Prefer WANIKANI_API_TOKEN over --token so the token is not stored in shell history, choose a private data directory, and protect or delete wanikani.db if it contains sensitive learning or account history.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sync.py:521
Finding

API Token Can Be Exposed Through Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: scripts/sync.py:521-534; insecure usage is also documented at SKILL.md:43-44
Vulnerability Type: Secret exposure through process arguments and shell history
Risk Level: Medium

Vulnerable Code:

python
parser.add_argument("--token", "-t", help="WaniKani API token (or set WANIKANI_API_TOKEN env var)")
parser.add_argument("--data-dir", "-d", help="Directory to store data (default: current directory)")
parser.add_argument("--full", "-f", action="store_true", help="Force full sync (ignore last sync time)")
parser.add_argument("--user-only", action="store_true", help="Sync only user data")
parser.add_argument("--assignments-only", action="store_true", help="Sync only assignments")
parser.add_argument("--reviews-only", action="store_true", help="Sync only reviews")
parser.add_argument("--subjects-only", action="store_true", help="Sync only subjects")
parser.add_argument("--with-subjects", action="store_true", help="Include subjects in full sync")
parser.add_argument("--subject-levels", help="Comma-separated levels to sync subjects (e.g., 1,2,3)")
parser.add_argument("--stats", "-s", action="store_true", help="Show stats after sync")

args = parser.parse_args()

token = args.token or os.environ.get("WANIKANI_API_TOKEN")

The documented invocation is:

bash
python3 scripts/sync.py --token "your-token-here"

Technical Analysis

Secrets supplied as command-line arguments may be recorded in interactive shell history, terminal logs, process-accounting systems, diagnostic reports, or automation logs. Depending on operating-system configuration, another local user may also be able to inspect process arguments while the command is running.

The token is legitimately required to authenticate to the WaniKani API, and the implementation sends it only to the fixed official HTTPS endpoint. However, accepting the token through a command-line option is not n ...[truncated 1092 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove the --token and -t command-line options.
  • Continue supporting WANIKANI_API_TOKEN for controlled execution environments.
  • For interactive use, obtain the token with getpass.getpass() so it is not echoed or stored in shell history.
  • Consider integration with an operating-system credential store or keyring for recurring use.
  • Update SKILL.md to remove the literal command-line token example.
  • Avoid printing, logging, serializing, or including the token in exception messages.
  • Recommend narrowly scoped, read-only WaniKani tokens where supported, and advise users to revoke tokens suspected of exposure.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sync.py:33
Finding

Sensitive Local Database Is Created Without Explicit Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/sync.py:33-37
Vulnerability Type: Insecure local storage permissions
Risk Level: Medium

Vulnerable Code:

python
def _init_db(self):
    """Initialize SQLite database with required tables."""
    self.data_dir.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(self.db_path)
    cursor = conn.cursor()

Technical Analysis

The Skill creates its data directory and SQLite database without explicitly setting restrictive permissions. Effective permissions therefore depend on the caller's umask and the permissions of an existing target directory.

The database contains privacy-sensitive account information, including the username, subscription information, review history, learning progress, reset history, accuracy statistics, and study timestamps. Local persistence of this information is necessary for the declared offline analysis feature, but making its confidentiality dependent entirely on ambient process settings is avoidable.

SQLite may also create auxiliary journal or write-ahead logging files under some configurations. The containing directory should therefore be protected in addition to the primary database file.

Attack Path

  1. The Skill is executed with a permissive umask or with a data directory accessible to other local users.
  2. wanikani.db is created without an explicit restrictive mode.
  3. Another local account enumerates or reads the database and any accessible SQLite auxiliary files.
  4. The attacker copies or queries the stored profile and learning-history records.

Exploitation depends on the resulting filesystem permissions; it is not remotely exploitable by itself.

Impact Assessment

A successful local attacker may obtain the complete synchronized WaniKani dataset, including personally identifying account information and detailed behavioral history. The issue does not expose the API token because the toke ...[truncated 92 chars]

Remediation
View remediation

Remediation Suggestions

  • Create dedicated data directories with mode 0700.
  • Enforce mode 0600 on wanikani.db immediately after creation.
  • Verify existing directory and database permissions before synchronization and fail safely or emit a prominent warning if they are overly permissive.
  • Do not automatically weaken permissions on an existing user-selected directory.
  • Protect SQLite journal and write-ahead logging files by securing the containing directory.
  • Document that users should choose a private local directory and avoid shared or web-served paths.
  • Where supported, create files atomically with restrictive permissions rather than relying solely on a later chmod.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/sync.py:174
Finding

Outbound API Requests Have No Timeout

Content
View full analysis

Vulnerability Details

File Location: scripts/sync.py:174
Vulnerability Type: Unbounded network operation
Risk Level: Low

Vulnerable Code:

python
response = requests.get(url, headers=self.headers, params=params or {})

Technical Analysis

The requests.get call does not define a connection or read timeout. The Requests library therefore permits the operation to wait indefinitely while establishing a connection or waiting for response data.

The destination is a fixed official WaniKani HTTPS endpoint, so this is not an arbitrary network-access or credential-exfiltration issue. It is an availability weakness: a stalled service, broken network path, or connection that stops producing data can prevent the Skill from completing.

Attack Path

  1. A user or automated agent starts synchronization.
  2. The network path or WaniKani endpoint accepts or partially establishes a connection but does not complete the response.
  3. The request remains blocked because no timeout is configured.
  4. The invoking agent, worker, or scheduled workflow remains occupied until externally terminated.

An attacker would need influence over the network path, service availability, or execution environment to trigger this deliberately.

Impact Assessment

Exploitation can cause loss of availability, blocked automation, and unnecessary worker or process consumption. It does not directly disclose the API token, execute code, or grant additional privileges.

Remediation
View remediation

Remediation Suggestions

  • Set bounded connection and read timeouts, for example:
python
response = requests.get(
    url,
    headers=self.headers,
    params=params or {},
    timeout=(5, 30),
)
  • Catch requests.Timeout and requests.ConnectionError and return a clear, non-sensitive error.
  • Add a limited retry policy with exponential backoff and jitter for transient failures.
  • Respect rate-limit responses and avoid retrying authentication or other permanent failures indefinitely.
  • Ensure error handling never logs the Authorization header or API token.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented behavior claims this skill performs authenticated API sync and local SQLite storage, but the analyzed implementation reportedly only provides reporting/query functionality and depends on a preexisting database. This kind of description-behavior mismatch is dangerous because it can mislead users and downstream agents into trusting the skill with sensitive tokens or assuming data sync, storage, and update semantics that are not actually implemented.

Content

No source excerpt is available for this finding.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · SKILL.md (reported line 91)May include surrounding context.

md
### Resets
`GET /resets`

Account reset history:
- `original_level`, `target_level`
- `confirmed_at`, `created_at`

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · references/api-structure.md (reported line 64)May include surrounding context.

md
### Resets
`GET /resets`

Account reset history:
- `original_level`, `target_level`
- `confirmed_at`, `created_at`

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/sync.py (reported line 377)May include surrounding context.

python
### Resets
`GET /resets`

Account reset history:
- `original_level`, `target_level`
- `confirmed_at`, `created_at`

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares access to an API token via environment variables and describes network-backed synchronization, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where an agent may invoke broader capabilities than a reviewer expects, increasing the risk of unintended network access or secret exposure during execution.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/api-structure.md (reported line 8)May include surrounding context.

md
import requests

API_BASE = "https://api.wanikani.com/v2"
REVISION = "20170710"
DB_FILENAME = "wanikani.db"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/sync.py (reported line 18)May include surrounding context.

python
import requests

API_BASE = "https://api.wanikani.com/v2"
REVISION = "20170710"
DB_FILENAME = "wanikani.db"

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This markdown file includes an authentication example with an API bearer token placeholder, but it does not warn readers that the token is sensitive and should not be exposed, logged, or committed. For markdown files, omitted warnings about privacy-sensitive behavior can qualify as missing user warnings.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code performs authenticated network requests to an external API using a user-provided token, and the module-level description says it fetches and stores user progress data locally. While network sync is part of the tool's purpose, there is no explicit user-facing warning or disclosure in the CLI help or runtime output about transmitting account-linked data to the external service and storing the returned data.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.