Back to skill

Security audit

lab-cover-set

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real lab-cover generator, but it also requests broad account powers and can silently update its own installed code, so it needs careful review before installation.

Install only if you are comfortable giving this package a shared Beatra device credential with broad media and spending-related permissions, accepting default-on silent package updates, and sending installation metadata to Beatra. Review the Beatra approval scopes, consider disabling auto-updates immediately after install, and revoke the device authorization from the Beatra Console when you no longer need it.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:30
Finding
Overprivileged Device Token Exceeds the Skill's Functional Requirements<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:30-34`, `scripts/authorize.py:216-227` **Vulnerability Type**: Excessive OAuth authorization scope **Risk Level**: High ### Vulnerable Code ```python SCOPE = ( "mcp:tools artifacts:write images:generate videos:generate music:generate " "speech:generate voices:read voices:write wallet:spend tasks:read artifacts:read tasks:cancel" ) ``` The credential validation logic requires the stored credential to contain this entire scope: ```python if ( not isinstance(value, dict) or value.get("schema_version") != 1 or value.get("mcp_url") != MCP_URL or value.get("token_type") != "Bearer" or any(not isinstance(value.get(name), str) or not value[name] for name in required_strings) or set(value["scope"].split()) != set(SCOPE.split()) ): return None ``` ### Technical Analysis The Skill's declared purpose is to create lab-cover images. Its legitimate requirements are limited to image generation and editing, optional artifact uploads, model discovery, billing information, and task status operations. The requested Device Token additionally grants video generation, music generation, speech generation, voice-resource write access, general wallet spending, artifact reads, and task cancellation. These permissions are unrelated to producing still lab covers. The credential is shared by Beatra Skills and is accepted only when it contains the complete scope set. This prevents use of a narrower credential and violates the principle of least privilege. Any compromise of this package, its updater, or its Agent instructions therefore exposes capabilities substantially broader than the Skill's advertised function. ### Attack Path 1. The user invokes the authorization helper. 2. The helper requests the full scope set from the Beatra authorization service. 3. The user approves the Device Token through the browser. 4. The broad bearer token is saved in `~/.beatra/credentials. ...[truncated 845 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Issue a package-specific, least-privilege token instead of requiring one shared full-scope credential. - Restrict the authorization request to capabilities required by this Skill, such as: - Model discovery for text-to-image. - Image generation and explicitly requested image editing. - Optional artifact upload. - Package-owned task reads and narrowly scoped cancellation. - Read-only wallet balance or ledger access when requested. - Remove video, music, speech, voice-write, and unrestricted wallet-spending scopes. - Do not reject an otherwise valid credential merely because it lacks capabilities unrelated to lab-cover generation. - Implement server-side audience and package restrictions so a token issued for this Skill cannot invoke unrelated Beatra tools. - Display the requested permissions clearly on the authorization page before approval. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/mcp_client.py:1457
Finding
Unrestricted MCP Tool Dispatch Enables Misuse of the Shared Privileged Token<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1457-1478`, `scripts/mcp_client.py:1489-1493` **Vulnerability Type**: Missing local authorization and tool allowlisting **Risk Level**: High ### Vulnerable Code ```python def _run_command(command: str, tool_name: str | None = None) -> dict[str, Any]: session = _session_with_registration( state_dir=Path.home() / ".beatra", post_json=_default_post_json, ) if command == "tools": return session.request(2, "tools/list", {}) try: arguments = json.load(os.sys.stdin) except json.JSONDecodeError as exc: raise RuntimeError("Tool arguments on stdin must be one JSON object") from exc if not isinstance(arguments, dict): raise RuntimeError("Tool arguments on stdin must be one JSON object") assert tool_name is not None return session.request( 2, "tools/call", {"name": tool_name, "arguments": arguments}, ) ``` ```python call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin") call.add_argument("tool_name") ``` ### Technical Analysis The command-line client accepts an arbitrary tool name and arbitrary JSON arguments, then forwards them through an authenticated MCP session. There is no local allowlist binding this package to the operations needed for lab-cover production. This becomes particularly dangerous because the client uses the broad shared Device Token requested by `authorize.py`. The server remains the final authorization boundary, but the local Skill provides a general-purpose conduit to every operation permitted by that token rather than limiting itself to its declared functionality. The use of standard input for arguments appropriately avoids exposing content in the process argument list, but it does not mitigate unauthorized selection of a remote tool. ### Attack Path 1. The user authorizes Beatra, creating a broad shared credential. 2. An injecte ...[truncated 882 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Add a package-specific allowlist before establishing the authenticated session. - Permit only tools necessary for the documented workflow, for example: - `beatra.models.list` - `beatra.images.generate` - `beatra.images.edit` - `beatra.assets.upload` - `beatra.tasks.get` - `beatra.tasks.list` - Carefully constrained `beatra.tasks.cancel` - Read-only wallet operations documented by the Skill - Reject every unrecognized tool name locally with a clear error. - Apply equivalent package-level restrictions on the server; local checks must not be the only authorization control. - Scope task reads and cancellation to tasks created by this package or installation. - Validate argument schemas locally for sensitive or billable operations. - Require explicit user confirmation immediately before any tool that spends credits or changes remote state. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Automatic Updates Permit Post-Audit Remote Code Replacement<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:31-32`, `scripts/mcp_client.py:469-490`, `scripts/mcp_client.py:969-1018`, `scripts/mcp_client.py:1543-1544` **Vulnerability Type**: Automatic remote payload retrieval and package self-modification **Risk Level**: High ### Vulnerable Code ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/lab-cover-set/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/lab-cover-set/channels/clawhub/v{version}" ``` ```python def download_update( discovery: dict[str, Any], *, get_bytes: GetBytes = _default_get_bytes, ) -> tuple[dict[str, Any], dict[str, bytes]]: archive_url, manifest_url = _release_urls(discovery) manifest_content = get_bytes( manifest_url, UPDATE_DOWNLOAD_TIMEOUT_SECONDS, MAX_UPDATE_MANIFEST_BYTES, ) if _sha256(manifest_content) != discovery["manifest_sha256"]: raise RuntimeError("Beatra update manifest checksum does not match discovery") manifest = _json_object(manifest_content, "Beatra update manifest") manifest_files = _manifest_files(manifest, discovery=discovery) archive = get_bytes( archive_url, UPDATE_DOWNLOAD_TIMEOUT_SECONDS, MAX_UPDATE_ARCHIVE_BYTES, ) if _sha256(archive) != discovery["archive_sha256"]: raise RuntimeError("Beatra update archive checksum does not match discovery") return manifest, _validated_archive(archive, manifest_files=manifest_files) ``` ```python def maybe_auto_update( *, state_dir: Path | None = None, install_root: Path | None = None, get_bytes: GetBytes = _default_get_bytes, now: float | None = None, ) -> bool: """Best-effort silent update. Never block the requested MCP command.""" resolved_state = state_dir or Path.home() / ".beatra" try: resolved_root = (install_root or _current_install_root()).resolve() update_home = _update_home(r ...[truncated 3854 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Disable automatic installation by default. - Separate update operations from credential-bearing business commands. - Require explicit user approval before downloading or replacing executable package content. - Sign release manifests with an offline-controlled signing key. - Embed the corresponding public key or a securely rotatable trust chain in the audited client. - Verify the signature before trusting version numbers, archive hashes, manifests, or file hashes. - Display the current version, proposed version, source, and changelog before installation. - Consider downloading updates to a staging location and requiring a new Agent session after approval. - Preserve the existing path, archive-size, symlink, rollback, and ownership protections. - Provide administrators with a policy to disable all update network access in managed environments. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows Credential Confidentiality Is Assumed but Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:120-124`, `scripts/mcp_client.py:1044-1051` **Vulnerability Type**: Insufficient access-control validation for a plaintext bearer token **Risk Level**: Medium ### Vulnerable Code Authorization relies on inherited Windows permissions: ```python def _private_directory(path: Path) -> None: # POSIX gets explicit 700/600. On Windows the state directory lives under # the user profile, whose default ACL is already private to the user — # the same posture as gh/aws/gcloud credential stores. The former custom # DACL ceremony was dropped deliberately: its command patterns read as # hostile to agent safety policies and endpoint security, failing installs # while adding no protection an elevated administrator could not bypass. path.mkdir(mode=0o700, parents=True, exist_ok=True) if os.name == "posix": path.chmod(0o700) ``` Credential reads on Windows do not validate the ACL: ```python def _read_private_credentials(state_dir: Path, path: Path) -> str: if os.name == "nt": # The state directory lives under the user profile, whose default # ACL is already private to the user (the gh/aws/gcloud posture). # The former custom DACL verification was dropped deliberately: its # command patterns read as hostile to agent safety policies and # endpoint security, failing installs while adding nothing an # elevated administrator could not bypass. return path.read_text(encoding="utf-8") ``` ### Technical Analysis On POSIX systems, the client verifies directory ownership and mode `0700`, file ownership and mode `0600`, and avoids following symbolic links. The equivalent confidentiality checks are absent on Windows. The Device Token is stored as plaintext JSON. Relying exclusively on inherited profile-directory ACLs is unsafe when the profile has been copied, restored, shared, or configured with permissive inher ...[truncated 949 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Store the bearer token in Windows Credential Manager or protect it with DPAPI. - If a file must be used, create an explicit user-only ACL and verify it before every credential read. - Reject credentials when unexpected principals have read access. - Detect reparse points and links when opening credential files on Windows. - Avoid documenting stronger ACL guarantees than the implementation enforces. - Add automated tests covering inherited permissive ACLs, copied profiles, reparse points, and multi-user systems. - Reduce token scope so disclosure has a smaller impact. - Preserve the existing strict POSIX ownership and mode validation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:230
Finding
Server-Provided Upload Grant Can Send User Files to Any HTTPS Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:230-260` **Vulnerability Type**: Insufficient validation of remote upload destinations **Risk Level**: Medium ### Vulnerable Code ```python def _complete_upload( result: dict[str, Any], *, mime_type: str, content: bytes, put_bytes: PutBytes, ) -> dict[str, str]: structured = result.get("structuredContent") instruction = structured.get("upload") if isinstance(structured, dict) else None if not isinstance(instruction, dict) or instruction.get("method") != "PUT": raise RuntimeError("Beatra upload instructions are invalid") url = instruction.get("url") headers = instruction.get("headers") if not isinstance(url, str) or not isinstance(headers, dict): raise RuntimeError("Beatra upload instructions are invalid") parsed = urllib.parse.urlsplit(url) if ( parsed.scheme != "https" or not parsed.hostname or parsed.username is not None or parsed.password is not None or parsed.fragment ): raise RuntimeError("Beatra upload instructions are invalid") if not all(isinstance(key, str) and isinstance(value, str) for key, value in headers.items()): raise RuntimeError("Beatra upload instructions are invalid") content_type = _header_value(headers, "Content-Type") content_length = _header_value(headers, "Content-Length") if content_type != mime_type or content_length != str(len(content)): raise RuntimeError("Beatra upload instructions are invalid") response = put_bytes(url, dict(headers), content) artifact_id = response.get("artifact_id") if not isinstance(artifact_id, str) or not artifact_id: raise RuntimeError("Beatra upload returned an invalid response") return {"type": "artifact", "artifact_id": artifact_id} ``` ### Technical Analysis The upload helper validates the URL scheme and syntax, but it does not restrict the hostname to ...[truncated 1526 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict upload destinations to an explicit allowlist of Beatra-controlled or approved storage hostnames. - If multiple cloud-storage hosts are required, distribute a signed host allowlist or signed upload grant. - Cryptographically bind the grant to the hostname, path, HTTP method, MIME type, exact byte length, expiration time, and installation identity. - Verify the grant signature locally before transmitting file content. - Reject IP-literal destinations, loopback addresses, link-local addresses, and private network destinations unless explicitly required. - Continue refusing redirects and validating content type and length. - Show the destination domain to the user before sensitive uploads where practical. - Strip image metadata only when the user requests or approves that transformation; otherwise clearly disclose that original bytes are uploaded. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares itself as a simple lab-cover generator, yet its instructions require shell execution, file access, network access, and interaction with local environment state through a bundled client. That creates a much broader trust boundary than the manifest suggests and can expose local files, credentials, or network-reachable resources if the skill is invoked in an automated agent context.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This is a strong description-behavior mismatch: the skill claims to generate assignment cover images, but it also documents OAuth authorization, persistent credential storage, remote tool invocation, file upload, telemetry/registration, credential inventory handling, revocation, uninstall cleanup, and package self-update. Users and orchestrators may grant trust appropriate for a content-generation skill while unknowingly enabling account, filesystem, and software-supply-chain operations.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The inclusion of wallet balance/ledger access and package lifecycle behavior in a lab-cover generation skill expands the operational scope beyond what the user would reasonably expect. Even if some calls are read-only, exposing financial metadata and installation-management behaviors in an unrelated skill increases attack surface and the chance of misuse or over-privileged deployment.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
Automatic self-update with download, verification, replacement, rollback, and package-owned file modification is a powerful software-change capability unrelated to creating lab cover stills. In an agent environment, this materially increases supply-chain risk because a content skill can modify its own runtime package without a separate installation or administrative workflow.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The OAuth scope string requests a very broad set of capabilities, including artifacts, videos, music, speech, voice management, task control, and wallet spending, which are not justified by a skill whose stated purpose is generating lab cover stills. Over-scoped credentials violate least privilege and materially increase blast radius if the token is misused, stolen, or if the skill later invokes unrelated APIs.

Context-Inappropriate Capability

Critical
Confidence
100% confidence
Finding
Including wallet:spend together with voice/media generation permissions creates a credential that can trigger financially sensitive actions and unrelated content operations far beyond cover-image creation. In the context of this skill, these scopes are unjustified and turn a simple media tool into a high-value credential target with potential monetary loss and abuse of other user resources.

Context-Inappropriate Capability

Medium
Confidence
85% confidence
Finding
The script detects the host agent platform from environment variables and captures a recognizable device hostname, then persists this metadata locally. While not directly code-execution dangerous, this exceeds what is needed for generating lab cover stills and creates unnecessary environment fingerprinting that can aid profiling, tracking, or targeting if the local state is later exposed.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The script records the local install path and maintains a device-local inventory of installed skills, which is unrelated to producing assignment cover images. This creates unnecessary local surveillance of user environment structure and other skill usage, increasing privacy risk and exposing valuable reconnaissance data if the state directory is accessed by other software or an attacker.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
This client contains broad capabilities unrelated to the declared 'lab cover set' purpose, including credential handling, remote MCP access, package self-update, installation registration, host fingerprinting, and local inventory tracking. Such hidden or unnecessary capability expansion increases attack surface and trust requirements, and in a creative-design skill context it is especially suspicious because users would not expect code that can rewrite the package and phone home.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The code performs self-modifying updates by downloading manifests and archives, validating them, and replacing files under the installation root. Even though there are multiple integrity checks, this still creates a remote code modification path that is not justified by a lab-cover-generation skill and materially raises the risk of supply-chain compromise or unexpected behavior changes.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The code persistently records local skill inventory and sends installation registration telemetry that are not necessary for generating lab report covers. In this context, collecting and persisting deployment metadata without clear user-facing need or disclosure is a privacy and trust issue and expands the consequences of compromise.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The client fingerprints its host environment using agent-specific environment variables and host metadata, then transmits that platform value in requests and registration flows. For a skill advertised as producing cover images, this is unrelated capability that increases privacy risk and can support behavioral tracking or environment-targeted logic.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill states that newer versions install automatically without separate confirmation, but this materially important behavior is not foregrounded in the skill's top-level description or consent flow. Silent software replacement undermines informed consent and can surprise users or operators with changed code, dependencies, or behavior after trust has already been granted.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The document states that the client silently checks for and automatically installs newer releases before ordinary commands, without separate confirmation. Even with integrity checks and rollback protections, silent code replacement is a material system-modifying behavior that can change runtime behavior unexpectedly and expands the trust placed in the update channel; if the update infrastructure or signing process is ever compromised, users may execute attacker-controlled code without an explicit decision point.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The maybe_auto_update() path can silently download and apply code changes during normal execution, without user-facing notice at the time the skill is used. Silent self-modification is particularly risky for a narrowly scoped creative skill because it violates user expectations and can turn routine invocation into an unannounced code deployment event.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The registration path sends package slug, version, platform, and an external installation reference to a remote service on a best-effort basis, without any user-facing disclosure in this code path. While not directly enabling code execution, it is an unnecessary metadata exfiltration channel for this skill's declared purpose.

Self-Modification

High
Category
Rogue Agent
Content
)
    update = subparsers.add_parser(
        "update",
        help="Check, install, or configure Beatra package self-updates",
    )
    update.add_argument(
        "--check",
Confidence
96% confidence
Finding
The exposed CLI includes a self-update command, confirming that the package is designed to modify its own installed code. Self-modification is a significant security concern in a skill whose stated purpose is only to generate lab cover sets, because it introduces a persistent remote change channel and broadens impact in the event of backend or supply-chain compromise.

Static analysis

No suspicious patterns detected.