Back to skill

Security audit

Bluesky

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Bluesky CLI skill; it can post and change account state, but that matches its stated purpose and the main risks are normal credential and dependency-installation cautions.

Install only if you are comfortable giving this CLI access to operate your Bluesky account. Use a Bluesky app password, keep it out of chat and shell history, review posts with --dry-run, confirm targets before follows, blocks, reposts, deletes, or mutes, and consider pinning or reviewing dependencies if you need stronger supply-chain control.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T08 · Insecure Dependencies

Warning
Location
scripts/bsky:12
Finding
Automatic Installation of Unhashed and Loosely Pinned Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bsky:12-15`; `requirements.txt:1` **Vulnerability Type**: Supply-chain exposure through automatic dependency installation **Risk Level**: Medium ### Vulnerable Code `scripts/bsky:12-15`: ```bash # Create venv if needed if [ ! -d "$VENV_DIR" ]; then echo "Setting up Bluesky CLI..." >&2 python3 -m venv "$VENV_DIR" "$VENV_DIR/bin/pip" install -q -r "$SCRIPT_DIR/../requirements.txt" fi ``` `requirements.txt:1`: ```text atproto>=0.0.65,<0.1.0 ``` ### Technical Analysis On its first invocation, the launcher automatically creates a virtual environment and retrieves packages from the Python package index configured for `pip`. The dependency is specified as a version range rather than an exact version, and no cryptographic hashes or lockfile are supplied. Consequently, the code executed by the Skill is not limited to the source reviewed in this project. Any future `atproto` release matching the range, as well as its resolved transitive dependencies, may be downloaded and executed without repository-level review. The effective package source can also be influenced by the user's or environment's `pip` configuration. The audit found no evidence that the current dependency is malicious. The vulnerability is the absence of controls that ensure the installed artifact is the exact dependency set reviewed and approved by the Skill publisher. ### Attack Path 1. An attacker compromises an allowed release of the direct dependency, one of its transitive dependencies, or a package index trusted by the local `pip` configuration. 2. The user invokes `scripts/bsky` on a system where `scripts/venv` does not yet exist. 3. The wrapper silently creates the virtual environment and executes `pip install` using the broad dependency constraint. 4. `pip` resolves and installs the attacker-controlled or compromised package without checking repository-supplied hashes. 5. The package executes with the privileges ...[truncated 916 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the direct dependency to an exact reviewed version instead of permitting a broad range. 2. Generate a lockfile containing exact versions for all direct and transitive dependencies. 3. Record and enforce cryptographic hashes for every downloaded artifact, for example by using: ```bash pip install --require-hashes -r requirements.lock ``` 4. Configure an explicit trusted package index rather than inheriting an arbitrary environment-specific index configuration. 5. Perform dependency installation as a separate, visible setup step instead of automatically downloading code during an ordinary CLI invocation. 6. Run dependency vulnerability and provenance checks in CI before publishing each release. 7. Consider distributing a reproducibly built package or verified environment whose dependency artifacts are fixed at release time. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (36)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
return

    if getattr(args, "yes", False):
        return

    prompt = f"{action} {target}? Type 'yes' to continue: "
    if not sys.stdin.isatty():
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill invokes shell commands, reads local config state, and depends on environment/runtime behavior, but it does not declare any explicit tool scope such as allowed-tools or permissions. This creates an authorization ambiguity where an agent may execute commands with broader capabilities than users or policy expect, increasing the risk of unintended command execution or access to local state.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Safety rules:

- Never ask the user to paste a Bluesky app password into chat, notes, logs, or command arguments.
- For login, ask the user to run `bsky login --handle THEIR_HANDLE.bsky.social` locally so the app password goes into the hidden prompt.
- For public posts/replies/quotes/threads, use `--dry-run` first unless the user already gave final text.
- For block, unblock, mute, unmute, follow, unfollow, delete, repost, and unrepost, verify the exact account or post target before running. Pass `--yes` only after the user has clearly confirmed the action or when opt-in mutation confirmations are enabled.
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The wrapper automatically creates a virtual environment and installs packages from a requirements file on first run without explicit user confirmation. This creates a supply-chain and unexpected code-execution risk because package installation runs network-dependent dependency resolution and executes package installation logic, which users may not anticipate from invoking a CLI wrapper.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
prompt = f"{action} {target}? Type 'yes' to continue: "
    if not sys.stdin.isatty():
        print(
            f"Refusing to {action.lower()} {target} without confirmation.",
            file=sys.stderr,
        )
        print(
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
prompt = f"{action} {target}? Type 'yes' to continue: "
    if not sys.stdin.isatty():
        print(
            f"Refusing to {action.lower()} {target} without confirmation.",
            file=sys.stderr,
        )
        print(
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# delete
    del_p = subparsers.add_parser("delete", aliases=["del", "rm"], help="Delete a post")
    del_p.add_argument("post_id", help="Post ID or URL")
    del_p.add_argument("--yes", action="store_true", help="Skip confirmation prompt")

    # profile
    profile_p = subparsers.add_parser("profile", help="Show profile")
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_version_flag(self):
        """bsky --version should return version string."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "--version"], capture_output=True, text=True
        )
        assert result.returncode == 0
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_version_flag(self):
        """bsky --version should return version string."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "--version"], capture_output=True, text=True
        )
        assert result.returncode == 0
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_post_dry_run_no_auth_needed(self):
        """bsky post --dry-run should work without authentication."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "post", "Test post", "--dry-run"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_post_empty_text_rejected(self):
        """Empty post text should be rejected."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "post", "", "--dry-run"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_post_too_long_rejected(self):
        """Post over 300 chars should be rejected."""
        long_text = "x" * 350
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "post", long_text, "--dry-run"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_help_flag(self):
        """bsky --help should show usage."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "--help"], capture_output=True, text=True
        )
        assert result.returncode == 0
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_post_help(self):
        """bsky post --help should show post options."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "post", "--help"], capture_output=True, text=True
        )
        assert result.returncode == 0
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_login_help_does_not_accept_password_argument(self):
        """Login should use the hidden prompt, not a command-line secret."""
        result = subprocess.run(
            [VENV_PYTHON, SCRIPT_PATH, "login", "--help"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_high_impact_commands_have_confirmation_flag(self):
        """Account-changing commands should expose an explicit confirmation flag."""
        for command in ["delete", "repost", "follow", "block", "mute"]:
            result = subprocess.run(
                [VENV_PYTHON, SCRIPT_PATH, command, "--help"],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""Test create-thread --dry-run (no auth needed)."""

    def test_dry_run_basic(self):
        result = subprocess.run(
            [
                VENV_PYTHON,
                SCRIPT_PATH,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.