Back to skill

Security audit

photo-singing-video

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches a Beatra photo-singing workflow, but it bundles broad authenticated account access, silent self-updates, and administrative credential handling that deserve careful review.

Review before installing if you are not comfortable giving this package a shared Beatra device token with broad generation and spending-related permissions, uploading portrait/audio content to Beatra, and accepting silent package updates unless disabled. Paid calls depend on the agent following the written confirmation workflow, not on an enforced local approval gate.

Vulnerability Patterns
  • 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
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/mcp_client.py:2299
Finding

Arbitrary authenticated MCP tool calls bypass the documented approval gate

Content
View full analysis

Vulnerability Details

File Location: scripts/mcp_client.py:2299-2316, 2383, 2475; authorization requirement documented at SKILL.md:37
Vulnerability Type: Missing authorization enforcement for authenticated and potentially billable operations
Risk Level: High

Complete Code Snippet

python
def _run_command(
    command: str,
    tool_name: str | None = None,
    arguments: dict[str, Any] | 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", {})
    if arguments is None:
        arguments = _read_tool_arguments()
    assert tool_name is not None
    return session.request(
        2,
        "tools/call",
        {"name": tool_name, "arguments": arguments},
    )

The command parser accepts an unrestricted tool name:

python
call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin")
call.add_argument("tool_name")

It is then invoked directly:

python
elif args.command in {"tools", "call"}:
    arguments = _read_tool_arguments() if args.command == "call" else None
    result = _with_waiting_approval(
        lambda: _run_command(args.command, getattr(args, "tool_name", None), arguments),
        state_dir=state_dir,
    )

By contrast, SKILL.md:37 requires an admission card and explicit confirmation before submitting the paid video operation:

text
Do not submit until the user confirms they have topped up or already have
enough credits for this estimate.

Technical Analysis

The CLI treats both the MCP tool name and its JSON arguments as caller-controlled values. _run_command establishes an authenticated session using the shared Device Token and forwards those values to tools/call without:

  • restrictin ...[truncated 2242 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace unrestricted tool dispatch with a package-specific allowlist containing only tools required by this Skill.
  2. Separate read-only operations from billable and state-changing operations.
  3. Require a short-lived confirmation artifact for every billable or destructive call. Bind it cryptographically or structurally to:
    • the exact tool name;
    • canonicalized arguments;
    • the estimated charge;
    • the stable client_request_id;
    • the user or installation identity;
    • a narrow expiration time.
  4. Reject any change to the approved payload and require fresh approval.
  5. Enforce one-time use for approval artifacts where appropriate.
  6. Prefer narrower OAuth scopes for this package rather than relying on one broad shared token.
  7. Add tests proving that direct CLI invocation cannot submit a paid generation, cancel a task, or invoke unrelated tools without valid approval.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/uninstall.py:168
Finding

Uninstall defaults to credential revocation and local-state deletion without an executable confirmation gate

Content
View full analysis

Vulnerability Details

File Location: scripts/uninstall.py:168-268
Vulnerability Type: Destructive operation without enforced confirmation
Risk Level: Medium

Complete Code Snippet

python
def uninstall(
    *,
    state_dir: Path | None = None,
    skill_root: Path | None = None,
    post_revoke: PostRevoke = _default_post_revoke,
    dry_run: bool = False,
) -> dict[str, Any]:
    """Apply the last-skill rule and return a machine-readable decision."""

    state_dir = (state_dir or Path.home() / ".beatra").expanduser()
    root = skill_root or _own_skill_root()
    resolved_root = str(Path(root).expanduser().resolve()) if root is not None else None

    result: dict[str, Any] = {
        "package": PACKAGE_SLUG,
        "dry_run": dry_run,
        "delete_next": resolved_root,
    }

    # Inventory and survivor checks omitted here only for readability.

    if dry_run:
        result.update({"decision": "disconnected", "revoked": False, "reason": "dry_run"})
        return result
    token = _device_token(state_dir)
    revoked = False
    revoke_state = "no_credential"
    if token is not None:
        try:
            status = post_revoke(token, _revoke_url(state_dir))
            if status == 200:
                revoked = True
                revoke_state = "revoked"
            elif status == 401:
                revoke_state = "not_recognized"
            else:
                result.update(
                    {
                        "decision": "revoke_retry",
                        "revoked": False,
                        "reason": f"http_{status}",
                    }
                )
                return result
        except RuntimeError:
            revoke_state = "unreachable"
    token = None
    _remove_local_state(state_dir)
    result.update({"decision": "disconnected", "revoked": revoked, "reason": revoke_state
...[truncated 2598 chars]
Remediation
View remediation

Remediation Suggestions

  1. Make dry-run behavior the default.
  2. Require an explicit destructive flag such as --confirm-uninstall, separate from merely omitting --dry-run.
  3. For Agent-driven execution, require a short-lived confirmation artifact bound to:
    • the package slug;
    • the resolved package path;
    • whether shared credentials will be revoked;
    • the exact local state directory;
    • an expiration time.
  4. Display the computed uninstall decision before revocation and require a second invocation carrying the decision identifier.
  5. Keep the existing conservative inventory checks and exact state-file allowlist.
  6. Consider separating package removal from shared credential revocation so that revocation requires an additional explicit confirmation.
  7. Add tests confirming that an unadorned invocation cannot revoke credentials or delete state.
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (39)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill's declared function is media animation, but its references and operational scope include uninstalling packages, reading/modifying ~/.beatra state, revoking shared credentials, and deleting local config files. These local administrative actions are materially different from the advertised purpose and could affect unrelated user setup or shared account access.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill's declared function is media animation, but its references and operational scope include uninstalling packages, reading/modifying ~/.beatra state, revoking shared credentials, and deleting local config files. These local administrative actions are materially different from the advertised purpose and could affect unrelated user setup or shared account access.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill's declared function is media animation, but its references and operational scope include uninstalling packages, reading/modifying ~/.beatra state, revoking shared credentials, and deleting local config files. These local administrative actions are materially different from the advertised purpose and could affect unrelated user setup or shared account access.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

The manifest explicitly references a local credential file for device-bearer authentication, which means the skill depends on sensitive tokens stored on disk. Any misuse of that credential path, poor file permissions, or overbroad agent access could expose authentication material and enable unauthorized use of the remote service.

Content

Scanner excerpt · manifest.json (reported line 67)May include surrounding context.

json
},
  "mcp": {
    "authentication": "device-bearer",
    "credential_file": "~/.beatra/credentials.json",
    "name": "beatra",
    "transport": "streamable-http",
    "url": "https://mcp.beatra.ai/mcp"

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/installation-and-auth.md (reported line 14)May include surrounding context.

md
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/installation-and-auth.md (reported line 21)May include surrounding context.

md
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/installation-and-auth.md (reported line 53)May include surrounding context.

md
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/mcp-connection.md (reported line 12)May include surrounding context.

md
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/authorize.py (reported line 430)May include surrounding context.

python
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/authorize.py (reported line 1009)May include surrounding context.

python
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/mcp_client.py (reported line 1612)May include surrounding context.

python
The host follows this computer's network route, described below.

They share the one full-scope Device Token stored in
`~/.beatra/credentials.json`. Installing or switching packages must not trigger
another authorization when that credential is already valid. Do not add,
enable, trust, or configure a host Beatra Connector. Never print or move the
token into command arguments, environment variables, logs, chat, or another

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The authorization scope requests a very broad set of privileges, including images, videos, music, speech, voices, artifacts, tasks, and wallet spending, far beyond the stated purpose of animating a portrait with a singing excerpt. If the issued token is compromised or the skill misuses it, the account could be used for unrelated generation actions and billable operations under excessive privilege.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The requested voice read/write plus separate music and speech generation permissions exceed what is described as a single-portrait singing animation workflow. Those extra capabilities broaden the blast radius of the token, enabling access to voice assets or unrelated content generation that users may not have intended to authorize.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
Confidence
98% confidence
Finding

Exposing a self-update command in the skill client means the package can intentionally replace its own code and related files. In a photo-singing skill, self-modification is unnecessary to the core task and substantially increases supply-chain risk, persistence potential, and the blast radius of any upstream compromise.

Content

Scanner excerpt · scripts/mcp_client.py (reported line 2396)May include surrounding context.

python
)
    update = subparsers.add_parser(
        "update",
        help="Check, install, or configure Beatra package self-updates",
    )
    update.add_argument(
        "--check",

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The script declares credentials.json as a managed file within shared state, signaling that this skill package is designed to handle authentication material directly. Direct access to credential storage by a non-authentication skill increases exposure of bearer tokens and makes credential deletion or misuse possible if the script or package is altered.

Content

Scanner excerpt · scripts/uninstall.py (reported line 39)May include surrounding context.

python
#: 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",

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The _device_token function reads an access token from credentials.json and later uses it as a Bearer token for revocation. Reading raw access tokens inside a media skill's uninstall script is dangerous because any compromise of the skill package yields direct access to reusable authentication material for the shared Beatra connection.

Content

Scanner excerpt · scripts/uninstall.py (reported line 121)May include surrounding context.

python
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):

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares no explicit tool restrictions while instructing use of a bundled Python client that can perform network, file, shell, and update operations. That creates an unnecessarily broad execution surface, making it easier for the skill or its helper to perform actions beyond the user-visible photo-to-video task, including credential handling, filesystem changes, and remote communications.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The description uses broad, everyday phrasing and many adjacent use cases, which can cause the skill to be invoked in situations beyond the tightly intended scenario. Over-broad invocation increases the chance that users unintentionally trigger uploads, remote processing, or paid actions when they only meant to discuss ideas or related media tasks.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The routing language is ambiguous and encourages adjacent workflow handling without sharply defined boundaries, which can lead to accidental invocation or overreach into other tasks. In this skill, that matters because activation can lead to file inspection, upload preparation, credit-sensitive remote calls, and interaction with a bundled client that has broader capabilities.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill states that ordinary use of the bundled client may silently check for and install updates without separate confirmation. Even with integrity checks, silent code replacement during routine execution materially changes the trusted codebase and introduces supply-chain and surprise-execution risk that is not prominently disclosed up front.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The changelog indicates region- or language-specific behavior, including use of a mainland network and an alternate international approval page, without stating any explicit user opt-in mechanism. Region-based routing can affect privacy, legal exposure, and user expectations if triggered silently.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · manifest.json (reported line 11)May include surrounding context.

json
"en": {
      "description": "Make a photo sing by animating one clear portrait with a short singing audio excerpt. This photo singing video and AI singing portrait workflow turns a single face into an expressive singing clip from one portrait and a chosen song excerpt, and reviews identity, mouth and facial movement, performance energy, audio presence, and synchronization. Use it for old photo singing, birthday greetings, character art, playful posts, song promos, and memorable messages, with one portrait and one singing audio excerpt per run and honest post-result review.",
      "display_name": "Photo Singing Video",
      "summary": "Animate one clear portrait with a short singing audio excerpt to create an expressive singing-photo clip.",
      "tags": [
        "make photo sing",
        "singing photo generator",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest declares an external MCP server, device-bearer authentication, and a local credential file, but the skill description does not warn users that it will connect to a remote service using stored credentials. This can mislead users about data handling and trust boundaries, especially for portrait and audio content that may be sensitive or copyrighted.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file documents an automatic self-update mechanism that silently checks for and installs newer releases before normal commands. For a skill whose stated purpose is generating a singing-photo video, bundling privileged code replacement functionality is unnecessary and materially expands the attack surface: if the update trust chain, endpoints, or client are compromised, arbitrary code could be delivered under the guise of an update.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The client performs silent network-based update checks and automatic installation without separate confirmation, which is unjustified by the declared media-generation purpose of the skill. Even though the document describes checksum and path validation, silent remote update channels create a powerful execution path that can bypass user expectations and turn any compromise of the update ecosystem into code execution on user systems.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.