Back to skill

Security audit

Vikunja Usage

Security checks for vulnerabilities and agentic risk

Overview

This Vikunja helper mostly matches its task-management purpose, but it under-discloses destructive actions and uses long-lived local tokens in a way users should review carefully.

Install only if you are comfortable giving the agent broad Vikunja account authority, including possible deletion and assignment changes. Prefer an HTTPS or otherwise trusted local endpoint, use a least-privileged token, avoid printing tokens, and require explicit confirmation before any delete operation.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:19
Finding
Vikunja credentials and long-lived bearer tokens transmitted over plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:19-23`, `SKILL.md:48-134`, and `SKILL.md:142-160` **Vulnerability Type**: Plaintext transmission of sensitive authentication data **Risk Level**: Medium ### Vulnerable Code The login example transmits a username and password to an unencrypted HTTP endpoint: ```bash RESP=$(curl -s -X POST http://localhost:3456/api/v1/login \ -H 'Content-Type: application/json' \ -d '{"username":"your-user","password":"your-pass"}') TOKEN=$(echo "$RESP" | grep -o '"token":"[^"]*"' | cut -d'"' -f4) echo "$TOKEN" ``` The Python template similarly uses plaintext HTTP while attaching the bearer token to requests: ```python import requests, os BASE = "http://localhost:3456/api/v1" TOKEN_FILE = os.path.join(os.getenv("AGENT_WORKSPACE", "."), "config", ".vikunja-token") def get_token(): if os.getenv("VIKUNJA_TOKEN"): return os.getenv("VIKUNJA_TOKEN") with open(TOKEN_FILE) as f: return f.read().strip() def headers(): return {"Authorization": f"Bearer {get_token()}", "Content-Type": "application/json"} def create_task(project_id, title, **kw): return requests.put(f"{BASE}/projects/{project_id}/tasks", json={"title": title, **kw}, headers=headers()).json() def update_task(task_id, **kw): return requests.post(f"{BASE}/tasks/{task_id}", json=kw, headers=headers()).json() def search_tasks(q): return requests.get(f"{BASE}/tasks", params={"s": q}, headers=headers()).json() ``` The other documented project, task, comment, label, assignee, and information requests also send the same bearer token through `http://localhost:3456`. ### Technical Analysis HTTP provides no transport encryption or server authentication. The login request therefore exposes the Vikunja username and password in plaintext at the transport layer. All authenticated API examples subsequently expose the bearer token in the same manner. The endpoint is restricted to loopback in the s ...[truncated 2194 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Configure Vikunja to use HTTPS and replace the embedded HTTP base URL with a configurable HTTPS endpoint. 2. Require certificate verification. Do not disable TLS validation in either `curl` or Python `requests`. 3. For a strictly local deployment, terminate TLS directly in Vikunja or through a trusted local reverse proxy with a certificate trusted by the agent environment. 4. Validate the configured endpoint and reject plaintext HTTP unless the user explicitly enables a documented development-only exception after understanding the risk. 5. Prefer short-lived, least-privileged, per-agent API tokens over reusable account passwords or indefinite tokens. 6. Document and implement explicit revocation of a leaked token. Logging in to obtain a replacement must not be presented as sufficient unless the old token is invalidated. 7. Avoid printing tokens to standard output. Replace `echo "$TOKEN"` with a direct, permission-restricted storage procedure where possible. 8. Retain the existing mode-`600` token-file requirement and metadata-only logging rule, as these are appropriate protections for credentials at rest and in logs. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (12)

External Transmission

Medium
Category
Data Exfiltration
Content
**获取 Token**:
```bash
RESP=$(curl -s -X POST http://localhost:3456/api/v1/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"your-user","password":"your-pass"}')
TOKEN=$(echo "$RESP" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
mkdir -p "$AGENT_WORKSPACE/config"
echo "$TOKEN" > "$AGENT_WORKSPACE/config/.vikunja-token"
chmod 600 "$AGENT_WORKSPACE/config/.vikunja-token"
```

**读取 Token**:
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# 列出项目
curl -s http://localhost:3456/api/v1/projects -H "Authorization: Bearer $TOKEN"

# 创建项目(PUT /projects)
curl -s -X PUT http://localhost:3456/api/v1/projects \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The skill documents project deletion but the manifest does not disclose destructive deletion capability. Hidden destructive actions are dangerous because users, agents, or reviewers may grant this skill broader trust than warranted, leading to unexpected permanent data loss.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
Documenting project deletion without any warning, confirmation guidance, or note about irreversibility increases the risk of accidental destructive use. In an agent context, terse examples may be copied directly into automation, causing unintended deletion of entire projects.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The task deletion example is presented as a routine operation without a warning about irreversible removal. This is risky in an automation skill because agents may execute the example directly and remove data based on ambiguous prompts or misunderstandings.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest/description understates the skill's capabilities by omitting assignee management, while the body documents endpoints that can change task ownership. This creates a security-relevant mismatch because operators or policy systems may approve the skill for limited task management while it can also modify assignment state affecting other users' workflows.

External Transmission

Medium
Category
Data Exfiltration
Content
return {"Authorization": f"Bearer {get_token()}", "Content-Type": "application/json"}

def create_task(project_id, title, **kw):
    return requests.put(f"{BASE}/projects/{project_id}/tasks",
                        json={"title": title, **kw}, headers=headers()).json()

def update_task(task_id, **kw):
Confidence
70% 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
json={"title": title, **kw}, headers=headers()).json()

def update_task(task_id, **kw):
    return requests.post(f"{BASE}/tasks/{task_id}", json=kw, headers=headers()).json()

def search_tasks(q):
    return requests.get(f"{BASE}/tasks", params={"s": q}, headers=headers()).json()
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
"persistence": null,
  "credentialSetup": {
    "type": "env_or_file",
    "description": "Login once per agent to get token. Store via $VIKUNJA_TOKEN env or write to $AGENT_WORKSPACE/config/.vikunja-token with chmod 600."
  },
  "requires": {
    "anyBinaries": ["curl"],
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The description forces Chinese-language instructions in a skill whose title and structure are otherwise English-facing, with no indication that the user can choose language or that the tool is intended only for a Chinese-speaking locale. This can violate language or locale policy when a specific language is imposed without opt-in.

Description-Behavior Mismatch

Low
Confidence
80% confidence
Finding
The manifest says the skill supports completion-status toggling, which implies switching task state in either direction. The documented examples only demonstrate updating a task with done=true and do not provide any dedicated toggle behavior or example of setting done=false.

Static analysis

No suspicious patterns detected.