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

Content
View full analysis
&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
View remediation
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
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/bsky.py (reported line 52)May include surrounding context.

python
return

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

    prompt = f"{action} {target}? Type 'yes' to continue: "
    if not sys.stdin.isatty():

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

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

md
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.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

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

md
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(

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 57)May include surrounding context.

python
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(

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1266)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1303)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1311)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1317)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1323)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1329)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1335)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1341)May include surrounding context.

python
# 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")

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · scripts/bsky.py (reported line 1346)May include surrounding context.

python
# 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")

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 · tests/test_cli.py (reported line 22)May include surrounding context.

python
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

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 · tests/test_thread.py (reported line 160)May include surrounding context.

python
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

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 · tests/test_cli.py (reported line 34)May include surrounding context.

python
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,

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 · tests/test_cli.py (reported line 48)May include surrounding context.

python
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,

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 · tests/test_cli.py (reported line 63)May include surrounding context.

python
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,

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 · tests/test_cli.py (reported line 81)May include surrounding context.

python
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

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 · tests/test_cli.py (reported line 89)May include surrounding context.

python
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

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 · tests/test_cli.py (reported line 97)May include surrounding context.

python
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,

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 · tests/test_cli.py (reported line 108)May include surrounding context.

python
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,

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 · tests/test_thread.py (reported line 19)May include surrounding context.

python
"""Test create-thread --dry-run (no auth needed)."""

    def test_dry_run_basic(self):
        result = subprocess.run(
            [
                VENV_PYTHON,
                SCRIPT_PATH,

Static analysis

No suspicious patterns detected.