Back to skill

Security audit

Clup

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims by creating ClickUp tickets, but it gives an agent broad ticket-creation authority with weak activation and credential-handling safeguards.

Install only if you are comfortable letting the agent create real ClickUp tasks with your API token. Use a least-privileged ClickUp token if possible, prefer temporary or secret-manager based credential handling over shell rc files, avoid putting secrets or sensitive infrastructure details in ticket text, and require an explicit confirmation before the agent runs the command.

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

T09 · Insecure Skill Coding Practices

Warning
Location
clup.sh:139
Finding
Unescaped User Input Allows JSON Payload Injection<![CDATA[ ## Vulnerability Details **File Location**: `clup.sh`, lines 139-158 **Vulnerability Type**: Improper JSON construction and injection **Risk Level**: Medium ### Vulnerable Code ```bash # Build tags array for JSON # Convert comma-separated string to JSON array: "tag1,tag2" -> ["tag1","tag2"] if [[ -n "$TAGS" ]]; then IFS=',' read -ra TAG_ARRAY <<< "$TAGS" TAGS_JSON=$(printf ',"%s"' "${TAG_ARRAY[@]}") TAGS_JSON="[${TAGS_JSON:1}]" # Remove leading comma and wrap in brackets else TAGS_JSON="[]" fi # Build JSON payload JSON_PAYLOAD=$(cat <<EOF { "name": "$TITLE", "description": "$DESCRIPTION", "status": "$STATUS", "tags": $TAGS_JSON } EOF ) ``` ### Technical Analysis The script constructs JSON through direct shell-string interpolation. The user-controlled `TITLE`, `DESCRIPTION`, `STATUS`, and `TAGS` values are inserted without JSON escaping or serialization. Characters such as double quotes, backslashes, newlines, and control characters can therefore produce malformed JSON. A deliberately crafted value may also terminate its original JSON string and introduce additional properties. Tag values are similarly enclosed in quotes using `printf` without escaping their contents. This issue is JSON injection rather than shell command injection: shell syntax embedded inside these variables is not automatically evaluated as a new shell command. The affected security boundary is the authenticated ClickUp API request. ### Attack Path 1. An attacker supplies a crafted ticket title, description, status, or tag value containing JSON syntax. 2. An AI agent or user passes that value to `clup.sh`. 3. The script interpolates the value directly into `JSON_PAYLOAD`. 4. The crafted input terminates or modifies the intended JSON structure. 5. If the resulting payload is valid and the ClickUp API accepts the injected fields, the API processes unintended task attributes using the configured `CLICKUP_API_KEY`. 6. Otherwise, the malformed payload c ...[truncated 543 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Construct the request with a JSON-aware serializer instead of shell interpolation. For example, use `jq` with `--arg` and `--argjson` so all string values are escaped correctly: ```bash IFS=',' read -ra TAG_ARRAY <<< "$TAGS" TAGS_JSON=$(printf '%s\n' "${TAG_ARRAY[@]}" | jq -R . | jq -s .) JSON_PAYLOAD=$( jq -n \ --arg name "$TITLE" \ --arg description "$DESCRIPTION" \ --arg status "$STATUS" \ --argjson tags "$TAGS_JSON" \ '{ name: $name, description: $description, status: $status, tags: $tags }' ) if [[ -n "$PRIORITY" ]]; then JSON_PAYLOAD=$( jq --argjson priority "$PRIORITY" \ '. + {priority: $priority}' <<< "$JSON_PAYLOAD" ) fi ``` Additional hardening measures: - Validate the allowed length and character set of status and tag values. - Reject control characters where they are not required. - Validate the final payload with a JSON parser before sending it. - Avoid using `sed` to modify serialized JSON. - Add tests covering quotes, backslashes, newlines, Unicode, empty tags, and attempted property injection. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
clup.sh:161
Finding
ClickUp API Token Is Exposed Through Curl Process Arguments<![CDATA[ ## Vulnerability Details **File Location**: `clup.sh`, lines 161-166 **Vulnerability Type**: Sensitive credential exposure in process arguments **Risk Level**: Low ### Vulnerable Code ```bash # Make API request response=$(curl -s -w "\n%{http_code}" -X POST \ "https://api.clickup.com/api/v2/list/${CLICKUP_DEFAULT_LIST_ID}/task" \ -H "Authorization: ${CLICKUP_API_KEY}" \ -H "Content-Type: application/json" \ -d "$JSON_PAYLOAD") ``` ### Technical Analysis The script expands `CLICKUP_API_KEY` directly into a `curl` command-line argument. While `curl` is running, the authorization header may be visible through process-inspection mechanisms such as process listings or `/proc` interfaces, depending on the operating system, process ownership, and local security configuration. Environment variables are already sensitive storage, but copying the token into the command line creates an additional exposure surface. The same command line also includes the complete ticket payload through `-d`, which may expose sensitive ticket content to local process observers. ### Attack Path 1. A user runs `clup.sh` with a valid ClickUp API token. 2. The script starts `curl` with the token embedded in the `Authorization` header argument. 3. A concurrent local process or user with sufficient process-inspection access reads the `curl` command line while it is active. 4. The observer extracts the ClickUp API token. 5. The stolen token is used directly against the ClickUp API until it expires or is revoked. The feasibility of this path depends on local operating-system protections and whether an attacker can inspect another process's arguments. ### Impact Assessment A recovered token permits API actions available to the associated ClickUp account and token. Depending on the account's permissions, this may include reading or modifying ClickUp resources beyond the target list used by this script. The issue does not directly elevate local operating-system pr ...[truncated 208 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Avoid placing the authorization header and request body directly in command-line arguments. Use permission-restricted temporary files or another mechanism that does not expose the values in the process argument list. A hardened implementation should: 1. Create temporary files with `mktemp`. 2. Set a restrictive `umask`, such as `umask 077`, before creating them. 3. Store the curl configuration and JSON request body in those protected files. 4. Register a `trap` to delete the files on normal exit and interruption. 5. Pass only the temporary file paths to `curl`. 6. Rotate the ClickUp token if process-argument exposure is suspected. 7. Grant the token only the minimum ClickUp permissions required for task creation. For example: ```bash umask 077 curl_config=$(mktemp) payload_file=$(mktemp) trap 'rm -f "$curl_config" "$payload_file"' EXIT HUP INT TERM printf '%s' "$JSON_PAYLOAD" > "$payload_file" cat > "$curl_config" <<EOF silent request = "POST" header = "Authorization: ${CLICKUP_API_KEY}" header = "Content-Type: application/json" EOF response=$( curl --config "$curl_config" \ --write-out "\n%{http_code}" \ --data-binary "@${payload_file}" \ "https://api.clickup.com/api/v2/list/${CLICKUP_DEFAULT_LIST_ID}/task" ) ``` Ensure that debugging, tracing, and error handling never print the configuration file, token, or full authenticated request. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description claims constrained behavior and quality enforcement, but the actual behavior implies use of shell execution and ClickUp API credentials without declaring those permissions or validating the promised 2-3 sentence minimum. This mismatch can mislead reviewers and users about what the skill actually does, causing unexpected network actions and secret use under a seemingly simple ticket-formatting skill. The skill context increases risk because it can transform vague prompts into real external side effects against a production task system.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
./clup.sh --title "Test" --description "Testing the tool"

# Optional: Add to PATH for system-wide access
sudo ln -s "$(pwd)/clup.sh" /usr/local/bin/clup
# Then you can use: clup --title "..." --description "..."
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The examples explicitly include sensitive internal details such as hostnames, private IPs, ports, outage timing, and production system names, but the documentation does not warn that these descriptions are sent to a third-party service. This creates a realistic risk of oversharing confidential infrastructure and incident data through routine ticket creation.

Session Persistence

Medium
Category
Rogue Agent
Content
### Error:

```json
{"status":"error","http_code":400,"message":"Failed to create ticket"}
```

## Troubleshooting
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The README encourages very broad natural-language invocation such as 'Create ticket for...' and 'Just say "Create ticket for..."', which can overlap with ordinary conversation and make an agent invoke the skill without sufficiently explicit user intent. In an agent setting, this increases the chance of unintended external actions and accidental transmission of operational details into ClickUp.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises shell-based execution but does not declare any tool scope such as allowed-tools or permissions. That omission weakens containment and review because a task-creation skill can invoke local commands and potentially use sensitive environment variables without explicit authorization boundaries. In this context, the lack of scoping is more dangerous because the skill is intended to trigger from common natural-language requests and relies on API credentials.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases are broad enough to match generic productivity language such as reminders or task creation requests that may not be intended for ClickUp. This can cause unintended invocation and external ticket creation, especially when combined with shell execution and API-backed side effects. In a skill that creates real objects in a third-party system, accidental activation is more dangerous than in a read-only or advisory skill.

Session Persistence

Medium
Category
Rogue Agent
Content
echo -e "${YELLOW}Please set your ClickUp API key:${NC}" >&2
    echo -e "  ${GREEN}export CLICKUP_API_KEY=\"pk_your_api_key_here\"${NC}" >&2
    echo "" >&2
    echo "To make it permanent, add to ~/.zshrc or ~/.bashrc:" >&2
    echo -e "  ${GREEN}echo 'export CLICKUP_API_KEY=\"pk_xxx\"' >> ~/.zshrc${NC}" >&2
    echo "" >&2
    echo "Get your API key: ClickUp Settings → Apps → Generate API Token" >&2
Confidence
90% confidence
Finding
The script instructs users to persist the ClickUp API key in shell startup files like ~/.zshrc or ~/.bashrc. Storing long-lived secrets in plaintext config files increases exposure to local compromise, accidental disclosure via backups/dotfile sync, or inadvertent sharing.

Session Persistence

Medium
Category
Rogue Agent
Content
echo -e "${YELLOW}Please set your ClickUp List ID:${NC}" >&2
    echo -e "  ${GREEN}export CLICKUP_DEFAULT_LIST_ID=\"123456789\"${NC}" >&2
    echo "" >&2
    echo "To make it permanent, add to ~/.zshrc or ~/.bashrc:" >&2
    echo -e "  ${GREEN}echo 'export CLICKUP_DEFAULT_LIST_ID=\"xxx\"' >> ~/.zshrc${NC}" >&2
    echo "" >&2
    echo "Find your List ID in the ClickUp URL:" >&2
Confidence
90% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This shell script sends the provided title, description, tags, and status to the external ClickUp API using the user's API key. While the script's purpose implies ticket creation, the code does not include a direct user-facing notice at the point of transmission that task contents are sent to a third-party service.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

# Make API request
response=$(curl -s -w "\n%{http_code}" -X POST \
    "https://api.clickup.com/api/v2/list/${CLICKUP_DEFAULT_LIST_ID}/task" \
    -H "Authorization: ${CLICKUP_API_KEY}" \
    -H "Content-Type: application/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.

External Transmission

Medium
Category
Data Exfiltration
Content
# Make API request
response=$(curl -s -w "\n%{http_code}" -X POST \
    "https://api.clickup.com/api/v2/list/${CLICKUP_DEFAULT_LIST_ID}/task" \
    -H "Authorization: ${CLICKUP_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$JSON_PAYLOAD")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.