Back to skill

Security audit

Homestay Welcome Talking Clips

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Beatra media-generation skill, but it needs Review because it grants broad shared account access and silently self-updates code beyond the narrow welcome-clip workflow.

Install only if you are comfortable giving this Beatra package a shared account credential with broad generation, task, artifact, and spending-related privileges, and with default silent package updates. Review the Beatra approval page carefully, disable automatic updates with `python3 scripts/mcp_client.py update --auto off` if you need change control, and protect or revoke `~/.beatra/credentials.json` if you stop using Beatra skills.

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 (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:34
Finding
Authorization requests permissions unrelated to the declared video workflow<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py`, lines 34–37 **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 resulting token is persisted and subsequently accepted only if it contains this complete scope set: ```python or set(value["scope"].split()) != set(SCOPE.split()) ``` The bundled client also exposes a generic tool-call interface: ```python call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin") call.add_argument("tool_name") ``` ### Technical Analysis The declared Skill workflow requires still-image upload, text-to-speech generation, image-to-video generation, model and voice discovery, task management, artifact reads, and billing operations. It does not declare a need to: - Generate standalone images through `images:generate`. - Generate music through `music:generate`. - Create or modify voices through `voices:write`. Nevertheless, authorization requires these permissions and rejects an otherwise valid credential if its scope is narrower. This violates least privilege. The risk is amplified because `~/.beatra/credentials.json` contains a shared Device Token and `mcp_client.py call` accepts an arbitrary MCP tool name and arbitrary JSON arguments from standard input. The local client does not restrict calls to the tools used by this particular Skill. ### Attack Path 1. The user runs `scripts/authorize.py` to enable the homestay talking-clip workflow. 2. The authorization request asks for image generation, music generation, voice writing, wallet spending, and other account-wide capabilities. 3. The broad bearer token is stored in `~/.beatra/credentials.json`. 4. A compromised Skill update, malicious agent instruction, or another ...[truncated 1072 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a package-specific minimum scope set containing only capabilities required by this workflow. 2. Remove at least `images:generate`, `music:generate`, and `voices:write` unless a documented feature demonstrably needs them. 3. Do not reject an existing credential merely because it lacks unrelated permissions. Validate that it contains the minimum required subset. 4. Replace the unrestricted `call <tool_name>` interface with an allowlist appropriate to this package, such as: - `beatra.assets.upload` - `beatra.models.list` - `beatra.voices.list` - `beatra.speech.synthesize` - `beatra.videos.animate` - Required task and wallet read operations 5. Require an explicit, separately confirmed elevation flow if a future feature needs additional scopes. 6. Prefer per-package or capability-bound tokens instead of sharing one full-scope bearer token across all Beatra Skills. 7. Display the requested capabilities clearly on the authorization page and explain why each one is needed. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:989
Finding
Silent default-on updater retrieves and installs mutable remote Python code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py`, lines 989–1015 **Vulnerability Type**: Automatic remote payload retrieval and replacement **Risk Level**: High ### Vulnerable Code Automatic updates default to enabled when update state is absent or invalid: ```python def _read_update_state(update_home: Path) -> dict[str, Any]: path = update_home / "state.json" try: value = json.loads(path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return {"schema_version": 1, "auto_update": True} if not isinstance(value, dict) or value.get("schema_version") != 1: return {"schema_version": 1, "auto_update": True} return value ``` Ordinary commands silently download and apply a newer remote package: ```python 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_bytes) _apply_update( install_root=resolved_root, update_home=update_home, discovery=discovery, manifest=manifest, new_files=new_files, ) return True ``` The update mechanism validates hashes, but those hashes originate from the same mutable remote release infrastructure. No offline publisher signature or locally pinned signing key is verified. ### Technical Analysis The updater has several good controls: HTTPS-only fixed hosts, redirect rejection, version checks, archive limits, ...[truncated 2380 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Permit background update checks, but require explicit user confirmation before replacing executable files. 2. Sign release manifests with an offline publisher key and embed only the corresponding public verification key in the audited package. 3. Verify a signature over the package identity, channel, locale, version, archive digest, manifest digest, and file list. 4. Use key rotation with threshold signatures or a signed root-metadata mechanism such as TUF rather than trusting hashes supplied by the download service itself. 5. Pin the expected release key independently of HTTPS and fail closed when signature verification fails. 6. Show the target version and changelog before installation, particularly when Python scripts or `SKILL.md` will change. 7. Consider allowing automatic updates only for non-executable data. Require approval for modifications to scripts or agent instructions. 8. Record and expose a verifiable update audit log containing old and new versions, signed manifest identity, and changed files. ]]>

other

Note
Location
scripts/authorize.py:360
Finding
Device hostname is collected and transmitted without clear necessity or explicit disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py`, lines 360–368 and 448–467 **Vulnerability Type**: Unnecessary environment metadata collection **Risk Level**: Low ### Vulnerable Code ```python 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 added to the remote 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 status, created = post_form(DEVICE_AUTHORIZATION_URL, form) ``` ### Technical Analysis The code performs limited host reconnaissance by reading the machine hostname. It does not enumerate IP addresses, scan the network, obtain an FQDN, or execute discovery commands. The stated purpose is to provide a recognizable device name in the Beatra console. However, the hostname is not required to generate homestay videos or authenticate a device. Hostnames frequently contain personal names, employer names, organizational domains, asset identifiers, geographic codes, or role descriptions. Sending this value to a remote service expands data collection beyond the minimum metadata required for the declared workflow. The registration documentation discloses package slug, version, platform, and a stable installation reference, but the reviewed documentation does not clearly disclose that the local hostname is collected and transmitted during authorization. ### Attack Path 1. The user runs `scripts/authorize.py`. 2. The helper calls `so ...[truncated 1062 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make hostname collection opt-in and explain where the value will be sent and displayed. 2. Use a generic default such as `Beatra device` or allow the user to enter a non-sensitive display name. 3. Do not persist the hostname in `host.json` unless needed for an explicitly selected feature. 4. Document the hostname field, retention period, purpose, and deletion mechanism in the authentication and privacy documentation. 5. Apply a stricter allowlist or redaction policy if automatic naming is retained. 6. Keep platform detection separate from hostname collection because platform attribution does not require the machine name. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows credential confidentiality relies on inherited default ACLs without verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py`, lines 1044–1083 **Vulnerability Type**: Inadequate credential-file access-control enforcement **Risk Level**: Medium ### Vulnerable Code ```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") if os.name != "posix": raise RuntimeError("Beatra credential permissions are unsupported on this platform") try: directory_stat = os.lstat(state_dir) if ( not stat.S_ISDIR(directory_stat.st_mode) or stat.S_IMODE(directory_stat.st_mode) != 0o700 or directory_stat.st_uid != os.getuid() ): raise RuntimeError("Beatra credential permissions are unsafe; authorize again") flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) descriptor = os.open(path, flags) try: file_stat = os.fstat(descriptor) if ( not stat.S_ISREG(file_stat.st_mode) or stat.S_IMODE(file_stat.st_mode) != 0o600 or file_stat.st_uid != os.getuid() ): raise RuntimeError("Beatra credential permissions are unsafe; authorize again") with os.fdopen(descriptor, encoding="utf-8") as handle: descriptor = -1 return handle.read() finally: if descriptor >= 0: os.close(descriptor) ``` The authorization helper similarly relies on inherited Windows permissions: ` ...[truncated 2242 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `~/.beatra` and `credentials.json` with an explicit Windows DACL granting access only to the owning user, SYSTEM, and optionally local administrators. 2. Verify the effective ACL before reading or using the token. Refuse operation when unexpected principals have read or write access. 3. Avoid invoking shell utilities to configure permissions. Use a reviewed Windows security API binding or a small platform-specific helper with fixed arguments. 4. Detect and reject reparse points, symbolic links, and non-regular credential files on Windows, mirroring the POSIX protections. 5. Apply equivalent ACL protection to temporary credential files before atomic replacement. 6. Update documentation to match actual enforcement. Do not claim current-user-only access when the implementation merely assumes inherited defaults. 7. Provide a repair command that safely reapplies the expected ACL without exposing the token. ]]>
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 (25)

Lp3

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding
The skill declares no permissions while its documented behavior clearly includes sensitive capabilities: local file access, shell execution via a bundled Python client, network communication, environment access, and file writes. This is dangerous because users and platform policy engines cannot meaningfully consent to or constrain the skill’s real authority, creating a trust gap that can enable credential theft, unwanted data exfiltration, or arbitrary system changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The published description frames the skill as a narrow media-generation workflow, but the document also authorizes OAuth login flows, persistent credential storage in ~/.beatra, remote tool invocation, local file upload, telemetry/registration, update installation, and uninstall-time credential revocation. That mismatch is dangerous because users may approve a seemingly simple video skill without realizing it establishes long-lived access, modifies the local environment, and communicates with remote services beyond the core advertised function.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The skill permits silent automatic software updates that are unrelated to the immediate user task of generating welcome-avatar clips. Even with integrity checks, silent self-update expands the trusted computing base and allows the skill’s behavior to change after installation without contemporaneous user review, which can introduce new capabilities or defects into an environment that already has file, shell, and network access.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The documented automatic update check/install behavior exceeds the manifest’s described media-generation purpose, meaning the skill performs software lifecycle management as part of ordinary runtime. This broadens attack surface and weakens informed consent because execution of a content-generation skill implicitly triggers software update logic and network access not clearly tied to the requested output.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The manifest changelog references unrelated 'balance' and 'ledger' capabilities and prior 'top-up' behavior, which do not fit a skill whose stated purpose is generating homestay welcome talking clips. This kind of scope mismatch is dangerous because it can conceal hidden financial or account-related operations behind an innocuous media-generation description, increasing the risk of deceptive capability exposure or unauthorized sensitive actions through the linked MCP service.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The documentation states that one approval grants access across image, video, music, speech, upload, model, and task tools, which is materially broader than the skill’s advertised Airbnb welcome-avatar function. This creates unnecessary privilege scope and increases blast radius if the skill, host, or token is abused, because a credential obtained for a narrow workflow can be reused for unrelated remote capabilities.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The bundled MCP client is documented as a generic remote tool interface supporting tool discovery and arbitrary tools/call execution, rather than a fixed-purpose welcome-video workflow. In the context of a narrowly branded skill, this effectively exposes a general remote execution surface over the vendor platform, enabling capability expansion beyond user expectations if invoked by the agent or repurposed by an attacker.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The OAuth scope string requests far more capability than the skill description justifies, including artifact/task controls and multiple generation domains unrelated to a simple homestay welcome clip workflow. Overbroad scopes violate least privilege and increase blast radius if the token is misused, leaked, or the skill later performs unexpected actions.

Context-Inappropriate Capability

Critical
Confidence
95% confidence
Finding
The skill requests music:generate despite the metadata describing welcome/facility talking clips from stills, scripts, and animation rather than soundtrack composition. Unused creative-generation scopes widen the attack surface and permit unexpected resource consumption or abuse.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
The skill requests music:generate despite the metadata describing welcome/facility talking clips from stills, scripts, and animation rather than soundtrack composition. Unused creative-generation scopes widen the attack surface and permit unexpected resource consumption or abuse.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill requests music:generate despite the metadata describing welcome/facility talking clips from stills, scripts, and animation rather than soundtrack composition. Unused creative-generation scopes widen the attack surface and permit unexpected resource consumption or abuse.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill requests music:generate despite the metadata describing welcome/facility talking clips from stills, scripts, and animation rather than soundtrack composition. Unused creative-generation scopes widen the attack surface and permit unexpected resource consumption or abuse.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The client includes a full self-update system that downloads manifests and archives, validates them, and replaces local installation files. Even with integrity checks, this exceeds the stated skill purpose of generating welcome clips and materially increases the attack surface: compromise of the update channel, signing/distribution pipeline, or server-side release process would let remote content modify local code.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The skill records installation telemetry and a local inventory of installed skills, including install path, platform, timestamps, and external installation reference data. This behavior is unrelated to producing welcome clips and creates privacy and tracking risk by collecting and persisting host metadata that could be used for profiling or operational surveillance.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The code fingerprints the host agent environment by inspecting environment variables and host metadata files to derive the platform identity, then attaches that information to outbound requests and registration data. For a media-generation skill, this is unnecessary host profiling and increases privacy exposure while enabling environment-specific targeting or telemetry correlation.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The uninstall script explicitly manages a shared Beatra device connection and can revoke shared authorization, which is unrelated to the declared purpose of generating Airbnb welcome talking clips. Even though the code tries to do this cautiously, the capability itself is over-privileged for this skill and creates an unnecessary trust boundary around shared credentials and global device state.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
This code performs a POST to the central revoke endpoint using a bearer token from shared local state, allowing this skill’s uninstall path to invalidate authorization used by other skills on the device. Although it attempts to check inventory before revoking, a content-generation skill should not possess logic to revoke platform-wide credentials at all; compromise, misuse, or logic errors here could disrupt all installed skills.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
Automatic updates install without separate confirmation and are disclosed only late in the document, reducing the chance that users understand they are authorizing self-modifying behavior. Poor disclosure is dangerous here because the skill has meaningful local and network capabilities, so hidden or de-emphasized auto-update behavior can materially change system risk after trust has already been granted.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The document explicitly states that the client silently checks for and automatically installs newer releases before ordinary commands, without separate confirmation. Even with checksum and origin verification, silently modifying installed software is a security-relevant behavior because it removes user awareness and consent, increases supply-chain blast radius if the update channel is ever compromised, and may violate change-control expectations on user systems.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation states that the bundled client automatically performs an installation registration call and writes a local cache file on first use, but it does not present this behavior as something users should explicitly consent to or be clearly warned about. Even though the data described is limited and non-secret, this creates a privacy/transparency issue because metadata about the package, version, platform, and installation reference is transmitted and persisted without an explicit user-facing notice.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The client can silently check for and apply updates during normal execution without a user-facing prompt at the time files are changed. Silent code replacement is risky because users may not realize local behavior has changed, and a compromised update source would be able to alter the package during ordinary use.

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
88% confidence
Finding
The manifest explicitly points to a local credential file used for bearer authentication to a remote MCP endpoint. Even if this is normal plumbing, exposing or depending on a fixed credential path increases the chance of credential misuse, overbroad access, or unintended secret handling by the skill runtime, especially when the skill’s public-facing purpose does not mention remote authenticated account 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
90% confidence
Finding
The script is designed to operate on ~/.beatra state files including credentials.json, showing that this skill package is aware of and permitted to manipulate shared credential-bearing files outside its functional domain. For a homestay welcome-video skill, touching shared credential storage is unnecessary and increases the blast radius if the package is modified, abused, or incorrectly installed.

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
96% confidence
Finding
The _device_token function reads an access token directly from credentials.json so the skill can perform authenticated revocation requests. Direct raw-token access by a media-generation skill is especially dangerous because any code execution in this package could repurpose that token for unauthorized API actions, and the skill context provides no legitimate need to handle bearer tokens directly.

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
94% confidence
Finding
The presence of a self-update command and associated code paths means the skill can modify its own local package files. Self-modifying/update-capable code is especially dangerous in a skill whose stated role is media generation, because it grants a remote distribution path the ability to change executable behavior after installation.

Static analysis

No suspicious patterns detected.