Back to skill

Security audit

CCP Local Proxy

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-built for a local CCP proxy, but it stores and forwards API credentials while running a persistent daemon with limited safeguards.

Install only if you are comfortable with a local background proxy that forwards prompts and Authorization headers to the CCP upstream and stores your CCP API key in ~/.workbuddy/models.json. Prefer using a limited-scope key, check file permissions on the config file, avoid putting real keys in shell history where possible, and verify the PID file before using stop if the service state looks stale.

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
scripts/ccp_proxy.py:215
Finding

API Key Exposure Through Command-Line Arguments and Plaintext Configuration

Content
View full analysis

Vulnerability Details

File Location: scripts/ccp_proxy.py:215-221 and scripts/ccp_proxy.py:245-263
Vulnerability Type: Sensitive credential exposure
Risk Level: Medium

The documented usage in SKILL.md:27-28 also instructs users to supply API keys directly as command-line arguments.

Vulnerable Code

python
def cmd_test():
    args = sys.argv[2:]
    key = None
    model = "qwen-3.5"
    for a in args:
        if a.startswith("sk-"):
            key = a
        elif a and not a.startswith("-"):
            model = a
python
def cmd_configure(key=None, model=None):
    args = sys.argv[2:]
    key = key or next((a for a in args if a.startswith("sk-")), "")
    model = model or next((a for a in args if not a.startswith("sk-") and not a.startswith("-")), "")
    if not key.startswith("sk-"):
        print("[FAIL] 请提供有效的 API Key:python3 ccp_proxy.py configure sk-xxx [model-id]")
        return
    if not model:
        model = "qwen-3.5"
    try:
        with open(MODELS_JSON) as f:
            models = json.load(f)
    except FileNotFoundError:
        models = []
    except json.JSONDecodeError:
        print("[FAIL] models.json 已存在但不是合法 JSON,请手工检查后再试")
        return
    models = [m for m in models if m.get("id") != model]
    entry = make_model_entry(model, key)
    models.append(entry)
    with open(MODELS_JSON, "w") as f:
        json.dump(models, f, ensure_ascii=False, indent=2)

Technical Analysis

API keys supplied through sys.argv can be retained in shell history and may be visible in process listings while the command executes. The configure operation then stores the key in ~/.workbuddy/models.json as plaintext.

When the configuration file is newly created, the code does not explicitly assign restrictive permissions. Its resulting mode depends on the user's current umask. If the umask permits group or world access, another ...[truncated 1302 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not accept API keys through command-line arguments by default.
  • Read credentials interactively with getpass.getpass(), through standard input, or from an operating-system credential store.
  • If backward compatibility requires an argument, clearly warn that it is insecure and provide a safer default interface.
  • Create new configuration files with mode 0600, and verify that existing files are owned by the current user and are not group- or world-readable.
  • Write updates atomically through a securely created temporary file in the same directory, apply restrictive permissions, and then replace the destination.
  • Where supported, store only a keychain reference in models.json instead of the credential itself.
  • Advise affected users to remove exposed commands from shell history and rotate any key previously supplied through argv.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ccp_proxy.py:174
Finding

Unverified PID File Can Cause Termination of an Unrelated Process

Content
View full analysis

Vulnerability Details

File Location: scripts/ccp_proxy.py:174-188
Vulnerability Type: Unsafe process management and local denial of service
Risk Level: Medium

Vulnerable Code

python
def cmd_stop():
    pid = read_pid()
    if pid and pid_alive(pid):
        os.kill(pid, 15)
        for _ in range(20):
            if not pid_alive(pid):
                break
            time.sleep(0.1)
        if pid_alive(pid):
            os.kill(pid, 9)
        print(f"[OK] 已停止 PID={pid}")
    else:
        print("[INFO] 服务未在运行")
    if os.path.exists(PID_FILE):
        os.remove(PID_FILE)

Technical Analysis

The stop operation treats the numeric value in .ccp_proxy.pid as sufficient proof that the process is the proxy. pid_alive() only calls os.kill(pid, 0) and therefore confirms existence or accessibility, not process identity.

If the proxy exits unexpectedly without deleting its PID file, the operating system may later reuse that PID for an unrelated process owned by the same user. A modified PID file produces the same condition. Running stop then sends SIGTERM to that unrelated process and escalates to SIGKILL if it remains alive after approximately two seconds.

The implementation does not validate the target executable, command line, process start time, service-specific token, or ownership of the listener on port 8257 before sending signals.

Attack Path

  1. The proxy exits unexpectedly and leaves a stale .ccp_proxy.pid file, or a local actor capable of modifying that file replaces its contents.
  2. The operating system assigns the recorded PID to another process running under the same user.
  3. The user invokes ccp_proxy.py stop.
  4. The script verifies only that the PID exists.
  5. It sends SIGTERM to the unrelated process.
  6. If the process remains alive after the wait loop, the script sends SIGKILL.
  7. The unrelated application terminates, potenti ...[truncated 451 chars]
Remediation
View remediation

Remediation Suggestions

  • Before signaling the recorded PID, verify that it belongs to the expected proxy process.
  • Record and validate additional identity data, such as process start time and a random per-instance token, rather than relying on a PID alone.
  • On platforms that expose process metadata, confirm the executable and expected _server.py command line.
  • Confirm that the target process owns the listener on 127.0.0.1:8257.
  • Treat identity mismatches as stale PID files: report the condition and remove the stale file without signaling the process.
  • Avoid escalating to SIGKILL unless process identity has been conclusively validated.
  • Prefer a standard user-level service supervisor that securely tracks process identity and lifecycle.
  • Restrict write permissions on the Skill directory and PID file to the owning user.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill declares no explicit tool scope even though it instructs use of shell, network access, and file writes. That creates an authorization gap: an agent may invoke the skill without clear least-privilege boundaries, increasing the chance of unintended command execution, network proxy setup, or modification of local configuration.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The activation text uses broad trigger phrases like references to 'ccp 代理' or local forwarding, which can cause the skill to activate in contexts where the user did not intend to manage a persistent proxy service. Because this skill can start a daemon and alter local model configuration, accidental invocation has more serious consequences than a read-only skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill omits an explicit warning that all proxied prompts and Authorization headers are forwarded to the external CCP platform. Users may assume requests stay local because they interact with 127.0.0.1, but the proxy actually transmits sensitive content and bearer credentials upstream, creating confidentiality and credential-handling risks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill instructs writing an API key into ~/.workbuddy/models.json without clearly warning the user that credentials will be stored locally and may persist beyond the session. Persistent plaintext or weakly protected credential storage increases the risk of key disclosure through local compromise, backups, logs, or other tools reading the config.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The code reads and later persists API keys in a user configuration file, which increases credential exposure on disk without a strong justification tied to basic reverse-proxy operation. Storing long-lived secrets in plaintext configuration broadens the attack surface if the local account, filesystem permissions, backups, or logs are compromised.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/ccp_proxy.py (reported line 175)May include surrounding context.

python
with open(SERVER_FILE, "w") as f:
        f.write(SERVER_CODE)
    with open(LOG_FILE, "a") as lf:
        proc = subprocess.Popen(
            [sys.executable, SERVER_FILE],
            stdout=lf, stderr=lf,
            start_new_session=True,  # 脱离会话,父进程退出后继续运行

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill description presents this as a proxy management utility, but the code also modifies the user's WorkBuddy model configuration and injects API credentials into it. This hidden side effect expands the skill's authority beyond the stated purpose and can cause unauthorized configuration changes or credential persistence the user did not clearly consent to.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The configure command writes directly to ~/.workbuddy/models.json without an interactive warning, consent flow, or safe merge protections beyond replacing entries with the same model id. This can unexpectedly alter client behavior and persist sensitive credentials, making accidental misconfiguration or secret exposure more likely.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The natural-language instructions and usage guidance are entirely in Chinese, with no indication that users may interact in another language or that the locale is mandatory for a region-specific reason. This can be a language/locale policy issue when a skill implicitly forces one language without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

文件顶部的说明、用法和运行提示均以中文固定呈现,未见提供用户语言选择或注明该工具仅面向特定中文场景。根据规则,强制单一语言而无用户选择可能构成自然语言政策问题。

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.