Back to skill

Security audit

Art Demo Page

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed remote art-image tool, but it asks for broad account and spending authority and silently updates its own code by default.

Install only if you are comfortable giving this Beatra skill a shared device token with broad media and wallet-related permissions, allowing user-selected files to be uploaded to Beatra, and accepting silent package updates unless you disable them with the documented update --auto off command. Prefer a version that uses image-only, package-scoped authorization, an MCP tool allowlist, explicit upload confirmation, and opt-in updates.

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
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:34
Finding
Overprivileged Shared Device Token and Unrestricted MCP Tool Invocation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-38`; `scripts/mcp_client.py:1455-1469` **Vulnerability Type**: Excessive authorization scope and missing tool allowlist **Risk Level**: High ### Complete Code Snippet ```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 command interface also accepts an arbitrary MCP tool name: ```python call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin") call.add_argument("tool_name") ``` ```python assert tool_name is not None return session.request( 2, "tools/call", {"name": tool_name, "arguments": arguments}, ) ``` ### Technical Analysis The declared purpose of this Skill is to generate and edit classroom art-demo images. That workflow legitimately requires image generation, relevant artifact operations, model discovery, and task monitoring. However, the authorization request also grants: - Video generation - Music generation - Speech generation - Voice read and write operations - General wallet spending - Task cancellation across the shared connection These permissions exceed the minimum privileges necessary for the Skill’s image-generation function. The resulting token is shared through `~/.beatra/credentials.json`, and the client does not constrain `call` to a package-specific allowlist. Any tool name supplied by the caller is forwarded to the remote MCP service. This violates least-privilege principles. Although the reviewed Skill instructions constrain intended use, those instructions are not an access-control boundary. ### Attack Path 1. The user authorizes the Skill. 2. `scripts/authorize.py` requests the full scope shown above. 3. Beatra returns a bearer token containing permissions unrelated to art-demo image generation. 4. The token is stored in `~/.beatra/credentials.json`. ...[truncated 869 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the shared full-scope token with a package-specific, least-privilege credential. 2. Restrict authorization to the capabilities needed by this Skill, such as: - Image generation and editing - Model-card discovery - User-selected artifact upload and required artifact reads - Reading this Skill’s tasks - Cancellation only if cancellation is an intended feature - Narrowly scoped spending limited to approved image operations 3. Remove video, music, speech, and voice permissions. 4. Add a local allowlist before forwarding `tools/call`. Reject all tools except the explicitly supported set, for example: - `beatra.models.list` - `beatra.images.generate` - `beatra.images.edit` - `beatra.assets.upload` - `beatra.tasks.get` - `beatra.tasks.list` - `beatra.tasks.cancel` - Required read-only wallet operations 5. Enforce equivalent package and operation restrictions on the server, because a local allowlist alone can be bypassed by other clients holding the token. 6. Separate read-only account operations from billable operations and require fresh user approval or a constrained capability token for spending. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Default Silent Retrieval and Installation of Remotely Controlled Executable Updates<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1017`; automatic invocation at `scripts/mcp_client.py:1542-1543` **Vulnerability Type**: Remote executable payload replacement without an independent cryptographic trust root **Risk Level**: High ### Complete Code Snippet ```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(resolved_state, resolved_root) observed_at = time.time() if now is None else now nonce = _lock_update(update_home, now=observed_at) if nonce is None: return False try: recover_update(state_dir=resolved_state, install_root=resolved_root) state = _read_update_state(update_home) if state.get("auto_update", True) is False: return False last_checked = state.get("last_checked_at") if ( isinstance(last_checked, (int, float)) and observed_at - float(last_checked) < UPDATE_CHECK_MAX_AGE_SECONDS ): return False state["last_checked_at"] = observed_at _write_private_json(update_home / "state.json", state) checked = check_update(get_bytes=get_bytes) if not checked["update_available"]: return False _ensure_owned_baseline( install_root=resolved_root, update_home=update_home, get_bytes=get_bytes, ) discovery = checked["discovery"] manifest, new_files = download_update(discovery, get_bytes=get_byte ...[truncated 3333 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic executable installation by default. 2. Check for updates silently if necessary, but require explicit user confirmation before replacing code or Skill instructions. 3. Sign release metadata and archives with a dedicated release key whose public key is pinned in the audited package. 4. Verify signatures before trusting version numbers, hashes, manifests, or archives. 5. Consider a transparency log or reproducible package registry so unauthorized releases can be detected. 6. Separate update execution from credential-bearing operations. The updater should not need access to `~/.beatra/credentials.json`. 7. Show the user the current version, target version, publisher identity, changed files, and signature status before installation. 8. Preserve the existing path traversal, file ownership, size, checksum, lock, rollback, and downgrade protections. 9. For unattended environments, support an administrator-configured signed-update policy rather than default silent replacement. ]]>

other

Note
Location
scripts/authorize.py:350
Finding
Hostname and Agent-Environment Telemetry Exceeds the Core Creative Requirement<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:350-369`; transmission at `scripts/authorize.py:455-467` **Vulnerability Type**: Collection and transmission of host-identifying telemetry **Risk Level**: Low ### Complete Code Snippet ```python def detect_host_platform(explicit: str | None = None) -> str: """The agent environment this process runs inside (docs/device-model.md). Order: explicit agent self-report > environment signatures > unknown. Detection reads the process environment only — nothing else runs, nothing reaches the network. """ if explicit: candidate = explicit.strip().lower().replace(" ", "-") if _PLATFORM_VALUE.fullmatch(candidate): return candidate env = os.environ if env.get("CLAUDECODE") == "1" or "CLAUDE_CODE_ENTRYPOINT" in env: return "claude-code" if any(key.startswith("CODEX_") for key in env): return "codex" ai_agent = env.get("AI_AGENT", "").lower() matched = re.match(r"([a-z0-9-]+)_", ai_agent) if matched and _PLATFORM_VALUE.fullmatch(matched.group(1)): return matched.group(1) return "unknown" def device_display_name() -> str | None: """A hostname the user will recognise in the console device list.""" try: name = socket.gethostname().strip() except OSError: return None if not name or not name.isprintable(): return None return name[:120] ``` The collected hostname is included in the authorization request: ```python form: dict[str, str] = { "client_id": CLIENT_ID, "resource": MCP_URL, "scope": SCOPE, "platform": host_platform, "client_name": PACKAGE_DISPLAY_NAME, "external_installation_ref": external_reference, "package_version": PACKAGE_VERSION, "package_slug": PACKAGE_SLUG, } if device_name: form["device_name"] = device_name ``` ### Technical Analysis The code performs limited environment inspection by detecting known ...[truncated 1805 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make hostname transmission opt-in rather than automatic. 2. Use a randomly generated neutral device label by default. 3. Allow users to supply a display name explicitly through a command-line option. 4. Present all telemetry fields before authorization and explain their purpose and retention. 5. Provide a local setting to disable installation registration and per-call source attribution. 6. Minimize registration frequency and avoid sending telemetry on every business call. 7. Keep platform detection limited to explicitly relevant variables and never transmit unrelated environment contents. 8. Document how users can delete or rotate the stable installation identity. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill invokes a bundled Python client and documents capabilities consistent with shell execution, local file access, persistent state, and network communication, yet no explicit permissions are declared. This creates a hidden trust boundary: an ostensibly simple art-demo skill can access sensitive local resources and remote services without transparent user consent or capability scoping.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented behavior substantially exceeds the declared purpose: beyond generating classroom art stills, it performs authentication, persistent credential storage, installation registration, file upload, arbitrary remote tool access, uninstall cleanup, and self-updating. That mismatch can mislead users and reviewers into authorizing a skill with much broader system and data access than expected, increasing the risk of credential exposure, telemetry leakage, or unauthorized local modification.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The skill includes an automatic download-and-install update mechanism unrelated to the core task of creating art-demo stills. Any self-modifying capability materially increases supply-chain risk, because compromise of the update channel, package metadata, or verification logic could turn a benign content skill into code that changes the local installation and expands behavior after approval.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest advertises a simple art-demo page generator, but it is wired to a remote MCP endpoint with authenticated access. That creates a capability mismatch: users may grant network and account access that is not clearly necessary for the stated function, increasing the risk of undisclosed data access or remote actions through the connected service.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
Using device-bearer authentication plus a local credential file gives the skill access to bearer credentials that could authorize remote service actions, yet this is not obviously needed for generating art stills from user-provided steps. Unnecessary credential handling expands the attack surface and can enable account misuse or unintended data exposure if the skill or its backend is compromised.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The authorization scope requests a very broad set of capabilities, including artifacts, images, videos, music, speech, voices, wallet spending, and task control, while the skill is described only as generating still images for art demo pages. This violates least-privilege and means a compromise or misuse of the skill could grant access well beyond its functional need, increasing blast radius substantially.

Context-Inappropriate Capability

Critical
Confidence
100% confidence
Finding
Requesting wallet:spend and multiple multimodal generation scopes is especially dangerous because it enables financial actions and access to unrelated resource-consuming services under the user's authorization. In the context of an art still-page skill, these permissions are unjustified, so if the skill or its ecosystem were abused, it could incur charges, create unauthorized content, or manipulate other platform resources.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The client embeds discovery, update, CDN, and telemetry constants unrelated to the declared art-demo functionality, indicating hidden secondary behavior beyond the skill's stated purpose. In a classroom art tool, bundling remote package management and telemetry materially expands trust and attack surface, especially because later code can fetch and install new code from the network.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
This code can replace its own installed files on disk using remotely supplied packages, which is a self-modifying capability unrelated to generating art stills. Even with integrity checks, any compromise of the update channel, package publisher, or signing/discovery workflow would allow arbitrary code changes inside the skill installation.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The code records a local skill inventory and registers installation metadata to a remote service, behavior not required for producing art-demo stills. This creates unnecessary telemetry about installed skills, platform, and installation identity, increasing privacy risk and normalizing undisclosed data collection in a low-need context.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The skill fingerprints the host agent platform from environment variables and host.json, which is not necessary for art-demo rendering. In context, this capability increases surveillance and targeting potential by revealing execution environment details that could support selective behavior, compatibility gating, or future exploitation.

Description-Behavior Mismatch

High
Confidence
93% confidence
Finding
The file is an uninstall script for an art-demo skill, yet it manages a shared device credential, decides whether other skills exist, and may revoke remote authorization. That is security-sensitive behavior unrelated to the advertised drawing/demo purpose, and compromise or misuse of this logic could disrupt other installed skills or remove shared access unexpectedly.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
This code reads the shared access token from ~/.beatra/credentials.json and sends it to a remote revocation endpoint. For a narrowly scoped art-demo skill, access to shared credentials is excessive privilege and creates a path to interfere with the broader agent environment if the package is tampered with or behaves incorrectly.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The script enumerates and later deletes files in ~/.beatra, including credentials and installation metadata shared across skills. Even though the code tries to be careful, a skill package handling deletion of shared state is dangerous because mistakes or malicious modifications could break unrelated skills and erase sensitive local state.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill describes silent automatic updates that modify the local installation without a prominent user-facing warning at the point of use. Even if the update path is claimed to be verified, silent local modification undermines informed consent and can be abused to introduce new code or changed behavior after the skill was initially reviewed or approved.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The document states that the client silently checks for updates and automatically installs newer releases by default before normal commands, without requiring separate confirmation. Even though the update flow includes integrity checks and rollback protections, silently replacing executable/package files can materially change local software behavior and trust boundaries without explicit user approval, which is a real security and safety concern.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The workflow instructs the agent to upload user-supplied reference images to a remote service via an HTTP PUT flow, but it does not require any explicit user notice or consent at the moment of transfer. That creates a real privacy and data-handling risk because users may provide teacher samples, scans, or other materials without understanding they will leave the local environment and be stored or processed externally.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The maybe_auto_update() path performs silent best-effort updates during normal command execution, meaning installed code can change without a contemporaneous user-facing warning. For a benign-seeming art skill, this is particularly dangerous because users are unlikely to expect hidden code mutation during ordinary use, reducing informed consent and making malicious or faulty updates harder to detect.

Credential Access

High
Category
Privilege Escalation
Content
},
  "mcp": {
    "authentication": "device-bearer",
    "credential_file": "~/.beatra/credentials.json",
    "name": "beatra",
    "transport": "streamable-http",
    "url": "https://mcp.beatra.ai/mcp"
Confidence
96% confidence
Finding
The manifest references a concrete credential file path, indicating the skill can access stored credentials to authenticate to a remote service. Any skill that can read or induce use of bearer credentials beyond its narrowly described purpose poses significant risk of account abuse, data leakage, or privilege misuse, especially because the skill’s advertised function does not justify secret access.

Credential Access

High
Category
Privilege Escalation
Content
#: these and then removes the directory only if it is empty — the script
#: never recursively deletes a directory it does not fully understand.
_STATE_FILES = (
    "credentials.json",
    "installation.json",
    "host.json",
    "skills.json",
Confidence
88% confidence
Finding
The explicit inclusion of credentials.json among files this script may remove shows awareness of and control over shared credential material. In the context of an art-demo skill, touching credential files is unnecessary and broadens the blast radius from simple content generation to credential/state management.

Credential Access

High
Category
Privilege Escalation
Content
def _device_token(state_dir: Path) -> str | None:
    path = state_dir / "credentials.json"
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, ValueError):
Confidence
94% confidence
Finding
The _device_token function reads and parses the shared credentials file to extract an access token. Direct credential access from a non-authentication-related skill is a clear least-privilege violation and could enable unauthorized token use, exfiltration, or revocation if the script is altered or invoked unexpectedly.

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
98% confidence
Finding
The exposed CLI explicitly supports package self-update, confirming that this skill can modify its own codebase after installation. In the context of a simple art-demo skill, self-modification is unjustified and significantly raises supply-chain and post-install trust risks because the behavior exceeds user expectations for the declared functionality.

Static analysis

No suspicious patterns detected.