Back to skill

Security audit

webnovel-serial-audio

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real webnovel audio workflow, but it asks for and uses broader Beatra account authority than that narrow purpose requires.

Review this before installing in any account with paid Beatra credits or sensitive artifacts. Installing authorizes a shared Beatra device token with broad media, artifact, task, voice, and wallet capabilities, stores it under ~/.beatra, sends package/platform metadata and a device label, and enables silent package updates by default. Use only if you trust the publisher and Beatra account boundary, and consider disabling automatic updates with scripts/mcp_client.py update --auto off immediately after installation.

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:30
Finding
Overprivileged Device Token and Unrestricted MCP Tool Invocation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:30-33`; `scripts/mcp_client.py:1463-1482`; `scripts/mcp_client.py:1488-1490` **Vulnerability Type**: Excessive authorization scope and unrestricted privileged tool dispatch **Risk Level**: High ### Evidence `scripts/authorize.py:30-33` requests a broad authorization scope: ```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" ) ``` `scripts/mcp_client.py:1463-1482` accepts an arbitrary tool name and forwards it to the authenticated MCP service: ```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}, ) ``` `scripts/mcp_client.py:1488-1490` exposes the unrestricted tool selector: ```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 function is serialized webnovel narration. Its minimum legitimate permissions include speech generation, voice selection or cloning, narrowly scoped artifact upload/read access, and task status reads. The authorization request additionally obtains image, video, and music generation, broad wallet spending, artifact access ...[truncated 1885 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the shared full-scope authorization with a package-specific least-privilege token. 2. Restrict this Skill to the exact required capabilities, such as: - Speech generation. - Voice listing and, only when requested, voice cloning. - Narrowly scoped artifact upload/read operations. - Task creation/status reads for this Skill's own jobs. 3. Remove image, video, and music generation scopes. 4. Separate wallet reads from spending authorization; require explicit user approval immediately before spending. 5. Do not grant task cancellation by default. Request or enable it only after an explicit cancellation instruction. 6. Add a strict local allowlist in `_run_command`, rejecting every tool not required by this Skill. 7. Prefer server-enforced package/tool restrictions in addition to local checks. 8. Use separate credentials for different packages so compromise of one Skill cannot exercise every capability associated with the shared connection. 9. Add tests that verify unrelated MCP tool names are rejected before any authenticated network request is sent. ]]>

other

Warning
Location
scripts/authorize.py:345
Finding
Undisclosed Hostname and Agent-Environment Telemetry<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:345-369`; `scripts/authorize.py:445-456`; `scripts/mcp_client.py:1150-1164`; `scripts/mcp_client.py:1219-1230` **Vulnerability Type**: Environment reconnaissance and unnecessary device telemetry **Risk Level**: Medium ### Evidence `scripts/authorize.py:345-369` inspects agent-related environment variables and collects the local hostname: ```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] ``` `scripts/authorize.py:445-456` transmits the collected hostname during device authorization: ```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, ...[truncated 3387 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove hostname collection unless it is strictly necessary for a user-requested device-management feature. 2. Default the platform field to `unknown` rather than inspecting the process environment. 3. Make both hostname and platform telemetry opt-in and disabled by default. 4. Before authorization, clearly disclose every transmitted metadata field, its purpose, retention period, and whether it is required. 5. Provide a `--no-telemetry` option that omits `device_name`, `platform`, and recurring source-attribution fields. 6. If a device label is needed, ask the user to supply a non-sensitive label rather than using the operating-system hostname. 7. Avoid persisting telemetry in `host.json` when the user has not opted in. 8. Minimize repeated transmission by sending any approved metadata only during registration rather than with every tool call. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Default Silent Self-Update Permits Post-Review Remote Code Replacement<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:515-522`; `scripts/mcp_client.py:969-1018`; `scripts/mcp_client.py:1542-1544` **Vulnerability Type**: Automatic retrieval and installation of mutable remote executable content **Risk Level**: High ### Evidence `scripts/mcp_client.py:515-522` enables automatic updates by default, including when state is missing 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 ``` `scripts/mcp_client.py:969-1018` silently checks for a remote release, downloads it, and replaces package 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(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 ...[truncated 4132 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Missing or invalid state should resolve to `"auto_update": false`. 2. Separate update checking from installation: - A silent check may report availability. - File replacement must require explicit user confirmation for the exact version. 3. Display the version, publisher identity, affected files, and release notes before installation. 4. Sign release manifests with an offline-protected publisher key and embed only the verification public key in the audited client. 5. Verify signatures independently of HTTPS and CDN-provided checksums. 6. Consider reproducible package builds and publish transparency-log entries for every accepted release. 7. Pin an approved version where environments require stable, reviewed behavior. 8. Never execute newly downloaded code in the updating process. Require a new process or agent session after explicit approval. 9. Preserve the existing redirect, path, size, ownership, checksum, transaction, and rollback protections as defense-in-depth. 10. Surface update failures and successful replacements in an auditable local log that excludes credentials and user content. ]]>
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 (20)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes a bundled Python client for uploads, task polling, wallet access, and updates, which implies filesystem, shell, environment, and network capabilities despite declaring no permissions. That mismatch is dangerous because operators and policy systems cannot accurately assess or constrain what the skill can access, increasing the chance of over-privileged execution and unintended data exposure.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill presents itself as a webnovel audio production tool, but its documented behavior includes generic remote tool invocation, persistent credential storage, browser-based OAuth/device auth, local file upload, telemetry/registration, uninstall cleanup, and self-updating package management. This broad hidden operational surface materially increases risk because users may authorize a narrow-content workflow without realizing the skill can manage credentials, modify local state, communicate with multiple remote services, and replace its own code.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
The documentation describes an automatic backend registration on first use that transmits package and environment metadata unrelated to the core audiobook-generation function. Even if labeled non-billable and non-secret, this is still telemetry-like behavior that can surprise users, expand data collection surface, and create privacy or governance concerns if done without explicit consent and clear disclosure.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The skill requests a very broad OAuth scope set that far exceeds its stated purpose of generating serialized webnovel audio. Excessive privileges violate least privilege and materially increase blast radius: if the skill, its storage, or downstream tooling is compromised, the token could be used for unrelated content generation, artifact/task operations, and spending actions.

Context-Inappropriate Capability

Critical
Confidence
97% confidence
Finding
The requested scope also grants voices:write, tasks:read, tasks:cancel, artifacts:read, and artifacts:write, which extend beyond simple speech generation and can affect other resources and workflows. In the context of a serial-audio skill, these permissions create unnecessary access to modify voices and manipulate task/artifact data, increasing lateral impact if abused.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The requested scope also grants voices:write, tasks:read, tasks:cancel, artifacts:read, and artifacts:write, which extend beyond simple speech generation and can affect other resources and workflows. In the context of a serial-audio skill, these permissions create unnecessary access to modify voices and manipulate task/artifact data, increasing lateral impact if abused.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The requested scope also grants voices:write, tasks:read, tasks:cancel, artifacts:read, and artifacts:write, which extend beyond simple speech generation and can affect other resources and workflows. In the context of a serial-audio skill, these permissions create unnecessary access to modify voices and manipulate task/artifact data, increasing lateral impact if abused.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The client contains a full self-update and installation-state management subsystem that is unrelated to a webnovel audiobook skill's stated purpose. Even with checksum and path validations, embedding remote code/package replacement logic expands trust boundaries substantially: compromise of the update channel, package publisher, or signing/distribution process could silently replace local code on user systems.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The code records local skill inventory and transmits installation telemetry that is not necessary for chapter-by-chapter webnovel audio generation. This creates privacy and governance risk by collecting environment and installation metadata outside the user's expected task flow, and it increases the attack surface for profiling or backend misuse.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The skill fingerprints the host environment by inspecting environment variables and local host metadata to identify which agent platform is running it. For an audiobook-production skill this is unnecessary and increases privacy risk while enabling platform-specific behavior that users are not expecting from the advertised functionality.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The CLI exposes arbitrary Beatra tool invocation by accepting any tool name and JSON arguments from stdin, which exceeds the narrow purpose of serialized webnovel audio production. This effectively turns the skill into a general remote API client, allowing access to unrelated backend capabilities if the credential has broader scope.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The uninstall script manages a shared Beatra device credential and can influence remote authorization state, which is outside the narrowly described webnovel audiobook function of the skill. Even though this occurs during uninstall and includes conservative safeguards, it still grants the package authority over shared authentication material unrelated to content generation, increasing the blast radius if the script is modified, abused, or unexpectedly invoked.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
This code issues a POST to revoke a shared remote device authorization using a bearer token read from local state. Revoking a shared authorization can affect other installed skills and is not necessary for the stated webnovel narration purpose, so embedding this capability in the skill creates unnecessary privileged behavior and a mismatch between declared and actual capabilities.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that it silently checks for updates and installs newer releases by default without separate confirmation. Even with signature and checksum verification, automatic code replacement expands the trust boundary after installation and can introduce supply-chain risk, unexpected behavior changes, or execution of newly delivered capabilities without informed user approval.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The documentation states that the client silently checks for updates and automatically installs them by default before ordinary commands. Even though later text describes integrity checks and rollback protections, default background network activity plus automatic file replacement without explicit per-update confirmation creates a real trust and transparency risk for users, especially in environments with strict change-control or limited network expectations.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The text says the client automatically performs registration and sends platform and installation reference data, but it does not describe an explicit user-facing warning or consent flow before doing so. This makes the behavior risky from a transparency and privacy perspective, especially because it occurs on first use and is not directly necessary for the requested creative task.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The client performs silent automatic self-updates that modify local installation files during normal execution without contemporaneous user-facing disclosure. Silent code changes reduce user control and auditability; if the update path or publisher is compromised, execution behavior can change unexpectedly on the next run.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The skill writes local inventory entries and attempts telemetry registration as a best-effort background action without user-facing warning. While not directly code-execution dangerous, undisclosed metadata collection and local state mutation are suspicious in the context of a narrowly described audiobook skill.

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
89% confidence
Finding
The function reads an access token from ~/.beatra/credentials.json so the skill can authenticate a remote revocation request. Accessing bearer tokens from shared credential storage gives this package direct access to authentication material beyond its apparent business purpose, and any compromise or repurposing of the script could misuse that token for unauthorized API actions.

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 self-update command allows the package to replace its own installed code, which is a self-modification capability unnecessary for the advertised webnovel audio workflow. Self-modifying mechanisms are inherently high risk because they let remote content alter trusted local execution state, amplifying supply-chain compromise impact.

Static analysis

No suspicious patterns detected.