Back to skill

Security audit

Field Check Pack

Security checks for vulnerabilities and agentic risk

Overview

This field-check image skill is mostly coherent, but it asks for broader account powers and silent self-updating than its still-image purpose needs.

Install only if you are comfortable granting this Beatra package a shared bearer credential with powers beyond field-check images, including other media tools, wallet spending, artifact/task access, and task cancellation. Consider disabling automatic updates with scripts/mcp_client.py update --auto off, using a dedicated Beatra account or low-balance wallet, and revoking the device from the Beatra Console when finished.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Automatic Updates Permit Post-Audit Remote Code Replacement<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1018` **Vulnerability Type**: Silent remote payload retrieval and installation without an independent signature trust root **Risk Level**: High ### Vulnerable Code ```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_bytes) _apply_update( install_root=r ...[truncated 2333 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default; require explicit, informed user approval before replacing code. 2. Separate update checking from update installation. 3. Sign release metadata and manifests with an offline or otherwise strongly protected publisher key. 4. Embed or securely provision the verification public key independently of the mutable discovery response. 5. Verify signatures before trusting versions, URLs, manifests, or hashes. 6. Preserve the existing path, archive-size, file-size, rollback, and downgrade protections. 7. Display the target version and verified signer identity before installation. 8. Consider distributing immutable, reviewed package versions through the host platform rather than implementing in-package self-replacement. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:34
Finding
Device Authorization Requests Capabilities Unrelated to the Skill’s Declared Function<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-37` **Vulnerability Type**: Excessive OAuth/device-token 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" ) ``` ### Technical Analysis The Skill is declared as a field-check still-image generator. Its documented workflow primarily requires model lookup, image generation and editing, optional asset upload, task polling, and limited billing queries. The requested token additionally grants video generation, music generation, speech generation, voice read/write access, broad wallet spending, and task cancellation. These capabilities are not necessary to produce field-check image packs. This violates least privilege and increases the value and blast radius of the shared bearer credential. The authorization documentation discloses that one approval covers multiple media types, but disclosure does not make unrelated privileges technically necessary. ### Attack Path 1. The user authorizes the Skill and receives a token containing the full scope. 2. The token is exposed through local compromise, permissive file access, a malicious future update, or misuse of the bundled generic tool client. 3. The attacker invokes unrelated video, music, speech, voice-write, spending, or cancellation operations. 4. The server accepts those operations because the token was intentionally issued with the corresponding permissions. ### Impact Assessment Misuse can consume account credits, create unrelated paid media, write voice resources, read account artifacts or tasks, and cancel tasks. The privileges exceed the minimum required by the Skill’s image-generation purpose and materially amplify the consequences of credential compromise. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a package-specific least-privilege scope. 2. Retain only the permissions required for model lookup, image generation/editing, approved uploads, image artifact retrieval, task status, and explicitly documented read-only billing operations. 3. Remove video, music, speech, voice-write, and unrelated cancellation permissions. 4. Replace broad `wallet:spend` authority with a server-side permission restricted to approved image operations where supported. 5. Bind credentials to the package and permitted tool names on the server, not only to broad capability families. 6. Require a separate authorization event if the user later requests a materially different media capability. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/mcp_client.py:1469
Finding
Bundled Client Allows Arbitrary MCP Tool Invocation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1469-1487` **Vulnerability Type**: Missing client-side tool allowlist for a broadly privileged token **Risk Level**: High ### Vulnerable Code ```python def _run_command(command: str, tool_name: str | None = None) -> dict[str, Any]: session = _session_with_registration( state_dir=Path.home() / ".beatra", post_json=_default_post_json, ) if command == "tools": return session.request(2, "tools/list", {}) try: arguments = json.load(os.sys.stdin) except json.JSONDecodeError as exc: raise RuntimeError("Tool arguments on stdin must be one JSON object") from exc if not isinstance(arguments, dict): raise RuntimeError("Tool arguments on stdin must be one JSON object") assert tool_name is not None return session.request( 2, "tools/call", {"name": tool_name, "arguments": arguments}, ) ``` ### Technical Analysis The `call` interface accepts any tool name supplied on the command line and forwards it to the remote MCP server with the stored bearer token. There is no allowlist tying the client to the tools needed by this Skill. Server-side authorization may reject tools outside the token’s scope, but the token itself has broad media, spending, voice, artifact, and task permissions. The absence of a package-level allowlist therefore leaves all authorized server tools reachable through the bundled client. This creates a privilege-boundary weakness: prompt injection, operator error, or another process capable of invoking the script can turn a narrowly described image Skill into a general-purpose Beatra command channel. ### Attack Path 1. An attacker-controlled instruction, compromised agent context, or local caller causes execution of `mcp_client.py call` with an unexpected tool name. 2. The caller supplies a valid JSON object on standard input. 3. `_run_command()` forwards the name and argum ...[truncated 551 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a fixed package-level allowlist of required MCP tool names. 2. Reject all unrecognized tool names before creating an authenticated session. 3. Separate diagnostic, billing, upload, generation, and administrative commands into explicit subcommands. 4. Do not expose `tools/list` in production unless genuinely necessary. 5. Enforce the same package-specific tool policy server-side so bypassing the local client does not expand privileges. 6. Require explicit user confirmation for billable calls and task cancellation at the point of execution. 7. Add tests proving unrelated video, music, speech, voice-write, and administrative tools are rejected locally. ]]>

other

Warning
Location
scripts/authorize.py:362
Finding
Authorization Collects and Transmits Host and Persistent Installation Identifiers<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:362-370, 445-465` **Vulnerability Type**: Environment reconnaissance and persistent device telemetry **Risk Level**: Medium ### 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] ``` ```python external_reference = _installation_reference(state_dir) 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 authorization flow reads the local hostname and identifies the agent platform from environment signatures. It also creates a stable installation reference and transmits these values to Beatra. Subsequent client operations perform recurring installation registration and source attribution using the package slug, platform, version, and stable installation reference. A recognizable device label can support account administration, but automatically collecting the operating environment’s hostname is not required to generate field-check images. The stable identifier and recurring attribution permit correlation of activity across requests and sessions. This behavior constitutes limited environment reconnaissance and telemetry rather than direct credential theft. It is documented in part, but the user is not given a clear opt-in choice before collection. ### Attack Path 1. The authorization helper inspects environment variables to identify the a ...[truncated 739 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not collect the hostname by default. 2. Ask the user to enter an optional device label or generate a non-identifying local label. 3. Make installation telemetry and recurring source attribution opt-in. 4. Clearly disclose every transmitted field, purpose, retention period, and deletion mechanism before authorization. 5. Use a rotating or package-specific pseudonymous identifier if persistent correlation is not essential. 6. Avoid deriving the platform from environment variables unless required for compatibility. 7. Provide a configuration option that disables registration and attribution without disabling core image generation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows Credential Confidentiality Is Assumed Rather Than Enforced<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1044-1051` **Vulnerability Type**: Missing Windows ACL creation and validation for a broad-scope bearer token **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") ``` ### Technical Analysis The POSIX implementation verifies directory ownership and mode `0700`, opens the credential file without following symbolic links, and verifies file ownership and mode `0600`. The Windows branch performs no equivalent ACL validation and simply reads the file. The accompanying documentation states that the current Windows user must be the only principal granted access. That property is neither established nor checked by the implementation. User-profile defaults are often restrictive, but inherited ACLs may be altered by enterprise policy, migration, administrator configuration, shared-profile arrangements, or prior filesystem changes. Because the file contains a bearer token with broad spending and media capabilities, relying solely on assumed defaults creates a meaningful local credential-exposure risk. ### Attack Path 1. The Skill creates or uses `credentials.json` under a directory with unexpectedly permissive inherited Windows ACLs. 2. The client does not verify the effective ACL. 3. Another local principal or process with inherited read permission accesses the bearer token. 4. The token is used directly against Beatra or through the generic MC ...[truncated 370 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the state directory and credential file with an explicit ACL limited to the current user and required system principals. 2. Validate the effective ACL before reading or using the token. 3. Fail closed with a clear remediation message when unexpected principals have access. 4. Prefer Windows Credential Manager, DPAPI-protected storage, or another operating-system secret store. 5. Apply equivalent protections when writing `installation.json`, registration state, and other files used for security decisions. 6. Add automated tests for permissive inherited ACLs, shared directories, reparse points, and ownership changes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/uninstall.py:229
Finding
Uninstall Deletes the Local Credential When Remote Revocation Is Unreachable<![CDATA[ ## Vulnerability Details **File Location**: `scripts/uninstall.py:229-260` **Vulnerability Type**: Unsafe credential revocation lifecycle **Risk Level**: Medium ### Vulnerable Code ```python token = _device_token(state_dir) revoked = False revoke_state = "no_credential" if token is not None: try: status = post_revoke(token) if status == 200: revoked = True revoke_state = "revoked" elif status == 401: # The server no longer recognises this token; the local copy # is worthless either way, but nothing was confirmed revoked. revoke_state = "not_recognized" else: # Reached but refused (rate limit, server error): keep every # local file so a retry — or a Console revoke followed by a # rerun — can still finish the job cleanly. 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}) return result ``` ### Technical Analysis When the revocation service returns an explicit non-success response other than `401`, the script preserves local state and requests a retry. In contrast, when the service is unreachable, the exception handler marks the state as `unreachable` but continues to delete the credential and other local connection files. Network unreachability does not invalidate the server-side bearer token. Deleting the only local copy prevents this installation from retrying authenticated revocation later. If an attacker previously copied the token, that copy may remain valid until its sliding idle expiry or manual console revocation. The script reports that ...[truncated 1078 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat an unreachable revocation service as `revoke_retry`, preserving the credential and local state. 2. Remove local credentials only after confirmed revocation, a definitive `401`, or explicit informed acceptance by the user. 3. Store a pending-revocation marker and retry safely when connectivity returns. 4. If the user elects immediate local deletion, prominently warn that the server credential may remain active and provide the exact console revocation procedure. 5. Consider server-issued credential identifiers that can be revoked through an authenticated account session without retaining the bearer secret. 6. Add tests covering DNS failure, TLS failure, timeout, offline operation, HTTP errors, and interrupted uninstall. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares no permissions, yet its instructions clearly require shell execution, local file access, network access, credential handling, and package modification via self-update. This mismatch prevents informed consent and can cause the host agent to grant or exercise powerful capabilities that users would not reasonably expect from an image-pack skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The stated purpose is simple image generation, but the skill also instructs the runtime to perform OAuth login, store bearer credentials, upload files, register with a backend, revoke tokens, and automatically download and install updated code. That is a major trust-boundary expansion: a user invoking a content-generation skill could unknowingly trigger account access, data exfiltration, persistent local state changes, and remote code supply-chain risk.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The manifest explicitly configures the skill to use a local credential file for MCP authentication, but the skill metadata and descriptions provide no user-facing disclosure that local bearer credentials may be accessed to contact a remote service. Even if this is a normal integration pattern, it creates a security transparency problem because users may invoke the skill without understanding that local secrets are involved.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The document states that the client silently checks for and automatically installs newer releases by default before ordinary commands, without separate confirmation. Even with checksum and source verification, this is security-relevant system-modifying behavior that can surprise users, widen supply-chain blast radius if the trusted update source is compromised, and reduce informed consent for changes made on the user's machine.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script requests an unusually broad OAuth scope set, including artifacts, media generation, wallet spending, and task control, but the user-facing authorization flow only tells the user to click Allow and does not clearly disclose the privileges being granted. This creates over-privilege risk: if the credential is later misused, compromised, or reused by other local skills, an attacker gains far more access than is necessary for a field inspection content skill.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The client performs automatic silent self-updates and can replace package files during normal command execution without prompting at the point of change. Although the implementation includes several integrity checks, silent code replacement materially expands trust in the remote update service and creates a supply-chain execution path that users may not notice in time if the update channel or signing workflow is compromised.

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
Referencing ~/.beatra/credentials.json indicates the skill depends on locally stored authentication material to access a remote MCP endpoint. This is dangerous because any skill that can trigger credential-backed remote operations expands the attack surface for secret misuse, account abuse, or unintended data access, especially when the skill's stated purpose is image/checklist generation rather than authentication-sensitive operations.

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
86% confidence
Finding
Self-modifying update functionality is inherently risky because it allows the package to overwrite its own installed code, creating a privileged path from remote content to local executable files. Even with path, hash, and rollback checks, compromise of the upstream release pipeline, discovery endpoint, or CDN trust chain could turn this mechanism into remote code deployment across installations.

Static analysis

No suspicious patterns detected.