Back to skill

Security audit

WorkBuddy 项目续接

Security checks for vulnerabilities and agentic risk

Overview

The skill appears intended for encrypted workspace migration, but it handles sensitive project secrets and imported agent instructions with too little user control.

Install only if you are comfortable migrating an entire project workspace, including .env secrets and project-local agent instructions. Use strong passphrases, transfer .wbpack files like confidential credentials, and restore only packages from sources you trust after reviewing imported rules before continuing agent work.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Warning
Location
references/agent-workflow.md:50
Finding

Restored Packages Can Inject Instructions into the Agent Workflow

Content
View full analysis

Vulnerability Details

File Location: references/agent-workflow.md:50
Vulnerability Type: Untrusted instruction adoption
Risk Level: Medium

Relevant Code / Instructions:

text
6. After restoration succeeds, first read the restored directory's
   .workbuddy-relay/HANDOFF.md, then read the project rules and key files.
   Explain the project goal, current phase, blockers, and next step, and
   continue the current task; do not automatically execute restored scripts,
   install dependencies, or switch the current WorkBuddy workspace.

Supporting implementation in scripts/relay.py:595-599:

python
def _prepare_restore_root(extracted: Path, destination: Path) -> None:
    payload = extracted / "payload"
    workspace = payload / "workspace"
    shutil.copytree(workspace, destination, symlinks=False)
    metadata = destination / METADATA_DIR
    metadata.mkdir(parents=True, exist_ok=True)
    for name in ("HANDOFF.md", "manifest.json", "runtime.json"):
        _copy_file(payload / name, metadata / name)

Technical Analysis

A package author controls the restored HANDOFF.md, project rules, Agent configuration, and project-local Skills. The restoration checks establish archive safety and internal integrity, but they do not authenticate the package author or determine whether textual content contains adversarial Agent instructions.

The workflow directs the Agent to read the package-controlled handoff and project rules and then continue the task. It does not explicitly classify these files as untrusted data, prevent them from overriding the current task, or require user approval before acting on newly imported instructions.

Cryptographic age encryption does not eliminate this trust-boundary issue. An attacker can create a structurally valid package, choose its password, and provide both to a victim. SHA-256 checks only prove that restored files match the package manifest create ...[truncated 1521 chars]

Remediation
View remediation

Remediation Suggestions

  1. Explicitly classify all restored handoff files, project rules, Agent configurations, and Skills as untrusted content.
  2. Require the Agent to summarize imported instructions for the user and obtain approval before adopting new goals or invoking tools.
  3. State that restored content cannot override system or developer instructions, expand workspace authorization, request secrets, or authorize access outside the restored project.
  4. Separate descriptive handoff data from executable Agent instructions by using a constrained, schema-validated metadata format.
  5. Disable restored project-local Skills and persistent Agent rules by default until the user explicitly enables them.
  6. Consider adding package signatures tied to trusted identities. Encryption with a shared passphrase provides confidentiality and integrity but does not establish the package author's identity.
  7. Continue enforcing the existing prohibition against automatically running restored scripts or installing dependencies.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/relay.py:477
Finding

Unbounded Archive Processing Allows Resource-Exhaustion Attacks

Content
View full analysis

Vulnerability Details

File Location: scripts/relay.py:477-500
Vulnerability Type: Unbounded archive extraction
Risk Level: Medium

Complete Vulnerable Code Segment:

python
def _extract_archive(tar_path: Path, destination: Path) -> None:
    seen: set[str] = set()
    with tarfile.open(tar_path, mode="r:gz") as archive:
        members = archive.getmembers()
        if not members:
            raise RelayError("migration package archive is empty")
        for member in members:
            safe_name = _validate_archive_name(member.name)
            if member.name in seen:
                raise RelayError("migration package contains duplicate paths")
            seen.add(member.name)
            if not (member.isdir() or member.isreg()):
                raise RelayError("migration package contains a link or special file")
            target = destination.joinpath(*safe_name.parts)
            if not _is_within(target, destination):
                raise RelayError("migration package escapes the extraction directory")
            if member.isdir():
                target.mkdir(parents=True, exist_ok=True)
                continue
            target.parent.mkdir(parents=True, exist_ok=True)
            source = archive.extractfile(member)
            if source is None:
                raise RelayError("migration package contains an unreadable file")
            with source, target.open("wb") as output:
                shutil.copyfileobj(source, output, length=CHUNK_SIZE)
            try:
                os.chmod(target, stat.S_IMODE(member.mode))
            except OSError:
                pass

Validation occurs only after extraction in scripts/relay.py:741-745:

python
extracted = staging_parent / "extracted"
extracted.mkdir()
_extract_archive(decrypted, extracted)
manifest, _, project_name = _validate_manifest(extracted)
target = _unique_restore_t
...[truncated 2119 chars]
Remediation
View remediation

Remediation Suggestions

  1. Define conservative configurable limits for encrypted size, decrypted archive size, archive member count, per-file size, cumulative extracted bytes, path length, and directory depth.
  2. Reject oversized input packages before invoking age.
  3. Process tar members incrementally rather than calling getmembers() for the entire archive.
  4. Track the actual number of bytes copied from every member and abort immediately when per-file or cumulative limits are exceeded.
  5. Validate each member's declared size before opening its output file, while also enforcing limits against actual streamed bytes.
  6. Check available temporary-disk capacity and reserve a safety margin before extraction.
  7. Add an extraction deadline or watchdog in addition to the existing age-operation timeout.
  8. Apply strict size limits to manifest.json, runtime.json, and HANDOFF.md before parsing or reading them fully.
  9. Clean up the temporary directory on every limit violation, retaining the existing fail-before-publication behavior.
  10. Add tests for gzip bombs, oversized sparse-looking members, excessive member counts, deeply nested paths, and disk-capacity failures.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill describes behavior that relies on file read, file write, and shell-capable operations, but it does not declare an explicit tool scope or permissions boundary. This can lead to overbroad execution in hosts that default to permissive tool access, increasing the risk of unintended filesystem access or command execution during packaging and restore flows.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

Natural-language policy violations include forcing a specific language or locale without user opt-in. The skill metadata, overview, and sample requests are entirely in Chinese, and the file does not indicate that the skill supports other languages or that Chinese is a justified region-specific requirement.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill states that project .env files are included by default in the encrypted package, which can silently capture API keys, database passwords, and other secrets. Even if encrypted, this increases secret exposure risk through weak passphrases, accidental transfer to the wrong device, insecure storage, or later decryption on a compromised host.

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/relay.py (reported line 169)May include surrounding context.

python
def _git_branch(workspace: Path) -> str | None:
    try:
        result = subprocess.run(
            ["git", "-C", os.fspath(workspace), "branch", "--show-current"],
            check=False,
            capture_output=True,

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/relay.py (reported line 320)May include surrounding context.

python
import pty

    master, slave = pty.openpty()
    process = subprocess.Popen(
        command,
        stdin=slave,
        stdout=slave,

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/relay.py (reported line 377)May include surrounding context.

python
del password
    creation_flags = getattr(subprocess, "CREATE_NEW_CONSOLE", 0)
    try:
        result = subprocess.run(
            command,
            check=False,
            creationflags=creation_flags,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

In restore mode, if the destination root is empty, the code moves restored children directly into that directory with os.replace, which will overwrite any conflicting paths that appear between the emptiness check and the final move. Because this skill is specifically designed to import a packaged workspace onto another machine, unintended overwrites can destroy local files or project state in a realistic workflow.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The Tkinter prompt hard-codes Chinese text such as '设置迁移密码', '输入迁移密码', '密码', '再次输入', and '继续'. This imposes a specific language on users without opt-in or locale selection, which matches the policy category for language or locale constraints.

Content

No source excerpt is available for this finding.