Back to skill

Security audit

weclone-init-twin

Security checks for vulnerabilities and agentic risk

Overview

This skill creates local persona-template files for a digital twin and does not show hidden execution, exfiltration, or persistence beyond those files.

Install only if you want local digital-twin persona files. Keep the generated ai_twin files private, leave sensitive fields blank unless truly needed, avoid --force unless you mean to replace existing files, and do not run the initializer against directories writable by untrusted users.

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
scripts/init_twin_profile.py:86
Finding

Symbolic-Link Following Allows Out-of-Directory File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/init_twin_profile.py:86-90, 100-128
Vulnerability Type: Symbolic-link following and time-of-check/time-of-use file-write weakness
Risk Level: Medium

Vulnerable Code

python
def validate_targets(output_dir: Path, force: bool) -> None:
    conflicts = []
    for name in TEMPLATE_NAMES:
        target = output_dir / name
        if target.exists() and not force:
            conflicts.append(str(target))
    if conflicts:
        joined = "\n".join(f"- {item}" for item in conflicts)
        raise FileExistsError(
            "Refusing to overwrite existing files without --force:\n"
            f"{joined}"
        )


def main() -> int:
    args = parse_args()
    out_dir = Path(args.output_dir).expanduser().resolve()
    source_dir = template_dir()

    # ...

    out_dir.mkdir(parents=True, exist_ok=True)

    try:
        validate_targets(out_dir, args.force)
    except FileExistsError as exc:
        print(f"[ERROR] {exc}", file=sys.stderr)
        return 1

    for name in TEMPLATE_NAMES:
        source = template_path(source_dir, name, language)
        if not source.is_file():
            print(f"[ERROR] Missing template: {source}", file=sys.stderr)
            return 1
        target = out_dir / name
        target.write_text(render_template(source, args.user_name), encoding="utf-8")
        print(f"[OK] Wrote {target}")

Technical Analysis

The initializer checks target paths with Path.exists() and later writes them using Path.write_text(). It does not reject symbolic links or open destination files with no-follow semantics.

Path.write_text() follows a destination symbolic link. When --force is enabled, validation permits any existing destination, including a symbolic link. A dangling symbolic link can also bypass the non-force check because Path.exists() normally returns false when the link's destination does not exist.

The validation and write are separate filesystem ...[truncated 1488 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject every destination that is a symbolic link, including dangling links. Use lstat() or equivalent link-aware checks rather than relying only on exists().

  2. Open destination files atomically with no-follow semantics. On supported platforms, use os.open() with O_NOFOLLOW and appropriate creation flags, then write through the returned file descriptor.

  3. When creating new files, use exclusive creation such as O_CREAT | O_EXCL to prevent an existing entry from being followed or replaced silently.

  4. For approved overwrites, securely open the existing destination without following links and verify through fstat() that it is a regular file.

  5. Keep validation and opening atomic. Do not depend on a path check followed by a separate path-based write, because an attacker can alter the directory entry between those operations.

  6. Consider restricting output directories to an approved repository subtree unless arbitrary output locations are required. If arbitrary destinations remain supported, clearly document that the directory must not be writable by untrusted users.

  7. Add automated tests covering existing symlinks, dangling symlinks, non-regular files, --force behavior, and destination replacement during initialization.

Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

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

md
2. Scaffold the persona pack.
   Run `python3 skills/weclone-init-twin/scripts/init_twin_profile.py --user-name <name> --language <en|zh> [output_dir]`.
3. Regenerate only with explicit overwrite approval.
   Add `--force` only when the user clearly wants to replace existing files.
4. Hand off to reply generation.
   Tell the user to fill the generated markdown templates before using `$weclone-twin-reply`.

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · scripts/init_twin_profile.py (reported line 95)May include surrounding context.

python
if conflicts:
        joined = "\n".join(f"- {item}" for item in conflicts)
        raise FileExistsError(
            "Refusing to overwrite existing files without --force:\n"
            f"{joined}"
        )

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill performs filesystem-affecting actions by scaffolding and potentially overwriting persona files, but it does not declare any explicit tool scope or permissions boundary. That omission weakens least-privilege controls and makes it harder for a runtime or reviewer to constrain the skill to only the intended directory and operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The template explicitly solicits personal and potentially sensitive attributes such as gender, age, city/time zone, relationship status context, and personality profiling without any accompanying privacy notice, minimization guidance, or consent checkpoint. In a digital-twin/persona skill, this creates a concrete risk of unnecessary collection and persistence of personal data that could enable profiling, re-identification, or misuse if the persona pack is shared, synced, or later consumed by other tools.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

This markdown file presents all instructions and examples only in Chinese, which can amount to a language/locale policy violation when no user opt-in or justification is provided. The file does not indicate that the skill is region-specific or that users may choose another language.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.