Back to skill

Security audit

hiring-avatar-studio

Security checks for vulnerabilities and agentic risk

Overview

This skill can perform the advertised hiring-avatar workflow, but it also stores broad shared account credentials and silently updates its own executable files by default.

Review this skill before installing. It is not just a prompt for making hiring videos: it authorizes a broad Beatra device token, stores that token locally, can spend credits through the account, sends some device/agent metadata to Beatra, and silently self-updates package code by default. Install only if you are comfortable with Beatra as the account, credential, billing, telemetry, and update authority, and consider disabling automatic updates with the documented update command 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
  • 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

Warning
Location
scripts/authorize.py:34
Finding
Authorization Token Requests Capabilities Beyond the Hiring-Video Workflow<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-38` **Vulnerability Type**: Excessive OAuth/device-token authorization scope **Risk Level**: Medium ### Complete Code Snippet ```python SCOPE = ( "mcp:tools artifacts:write images:generate videos:generate music:generate " "speech:generate voices:read voices:write wallet:spend tasks:read artifacts:read tasks:cancel" ) ``` ### Technical Analysis The declared workflow requires artifact upload/read access, voice selection or cloning, speech synthesis, video generation, task management, and limited wallet visibility or payment authorization. The requested token additionally receives capabilities such as: - `images:generate` - `music:generate` - Generic `mcp:tools` - Broad `wallet:spend` Image and music generation are not part of the declared hiring-avatar workflow. A broadly scoped spending capability also provides more authority than a package-specific or operation-specific payment grant would provide. Because the token is shared between Beatra Skills and stored as a reusable bearer credential, unnecessary scopes increase the consequences of local credential disclosure, client compromise, or unintended tool invocation. This violates the principle of least privilege even though the authorization is shown to the user through a device-authorization flow. ### Attack Path 1. The user authorizes the Skill through `scripts/authorize.py`. 2. Beatra issues a bearer token containing all scopes listed in `SCOPE`. 3. An attacker obtains the token through a local compromise, weak Windows ACL, malicious future update, or another process running as the user. 4. The attacker submits authenticated MCP requests using the stolen token. 5. Subject to server-side tool availability, the attacker invokes unrelated image or music generation operations or other paid operations covered by the broad token. 6. Charges and generated content can extend beyond the hiring-video functionality the user in ...[truncated 373 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `music:generate` and `images:generate` unless a documented workflow actually requires them. 2. Replace generic `mcp:tools` access with an allowlist of the exact tools needed by this package. 3. Replace broad `wallet:spend` authority with operation-specific payment authorization or narrowly scoped spending grants. 4. Separate read-only wallet access from paid-operation authorization. 5. Issue package-specific credentials rather than sharing one full-scope credential across unrelated Skills. 6. Validate returned scopes as a subset of the minimum required scopes rather than requiring the current broad set. 7. Display the exact requested capabilities and their financial implications before device authorization. 8. Add automated tests that fail when newly requested scopes are not mapped to a documented workflow operation. ]]>

other

Note
Location
scripts/authorize.py:342
Finding
Hostname and Agent-Environment Metadata Are Transmitted Without Explicit Telemetry Consent<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/authorize.py:342-367`, `scripts/authorize.py:455-467`, `scripts/mcp_client.py:1219-1229`, and `scripts/mcp_client.py:1364-1383` **Vulnerability Type**: Unnecessary host reconnaissance and installation telemetry **Risk Level**: Low ### Complete Code Snippet ```python def detect_host_platform(explicit: str | None = None) -> str: """The agent environment this process runs inside (docs/device-model.md). Order: explicit agent self-report > environment signatures > unknown. Detection reads the process environment only — nothing else runs, nothing reaches the network. """ if explicit: candidate = explicit.strip().lower().replace(" ", "-") if _PLATFORM_VALUE.fullmatch(candidate): return candidate env = os.environ if env.get("CLAUDECODE") == "1" or "CLAUDE_CODE_ENTRYPOINT" in env: return "claude-code" if any(key.startswith("CODEX_") for key in env): return "codex" ai_agent = env.get("AI_AGENT", "").lower() matched = re.match(r"([a-z0-9-]+)_", ai_agent) if matched and _PLATFORM_VALUE.fullmatch(matched.group(1)): return matched.group(1) return "unknown" def device_display_name() -> str | None: """A hostname the user will recognise in the console device list.""" try: name = socket.gethostname().strip() except OSError: return None if not name or not name.isprintable(): return None return name[:120] ``` The collected hostname is added to the outbound 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 ``` Agent-platform attribution ...[truncated 2061 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make installation telemetry and source attribution explicitly opt-in. 2. Do not transmit the hostname by default; use a user-selected device label if one is desired. 3. Generate a random, revocable, package-specific identifier instead of combining a stable installation identifier with host metadata. 4. Provide command-line options such as `--telemetry off` and `--device-name`. 5. Document every collected field, destination, retention purpose, and deletion mechanism before authorization. 6. Avoid adding telemetry fields to every business call when installation-level registration is sufficient. 7. Keep platform detection local unless a specific compatibility feature requires it. 8. Ensure refusing telemetry does not block authorization or creative operations. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Default Silent Updater Retrieves and Replaces Executable Skill Code<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/mcp_client.py:31-32`, `scripts/mcp_client.py:520-522`, `scripts/mcp_client.py:969-1013`, and `scripts/mcp_client.py:1542-1544` **Vulnerability Type**: Automatic remote payload retrieval and executable-file replacement **Risk Level**: High ### Complete Code Snippet ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/hiring-avatar-studio/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/hiring-avatar-studio/channels/clawhub/v{version}" ``` Automatic updating defaults to enabled when local 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 ``` The ordinary-command update path downloads and applies remotely supplied package files: ```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, ...[truncated 2484 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default; permit silent update checks only. 2. Require explicit user confirmation before replacing any executable or instruction file. 3. Sign release metadata with an offline-protected release key. 4. Embed or securely provision a pinned public verification key in the audited package. 5. Use a framework such as TUF, including root-key rotation, threshold signatures, version counters, and expiration metadata. 6. Separate discovery, manifest, and payload trust so compromise of one publication system cannot authorize all three. 7. Display the target version, changed files, signer identity, and release notes before installation. 8. Keep the existing path validation, size limits, ownership checks, lock, recovery journal, and rollback protections. 9. Consider handing updates to the Skill host or package manager instead of allowing the Skill to overwrite itself. 10. Run updated code only in a new process after successful signature verification and user approval. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows Credential ACL Requirement Is Documented but Not Enforced<![CDATA[ ## Vulnerability Details **File Locations**: `references/installation-and-auth.md:12-21`, `scripts/authorize.py:121-132`, and `scripts/mcp_client.py:1044-1052` **Vulnerability Type**: Insufficient local access-control validation for bearer credentials **Risk Level**: Medium ### Complete Code Snippet The documentation states a current-user-only Windows ACL requirement: ```text On POSIX systems the directory must be mode `0700` and both files mode `0600`. On Windows the current user must be the only principal granted access through the file ACL. Never copy the Device Token into conversation context, stdout, stderr, a command argument, an environment variable, a log, a backup, a diff, or another file. ``` Authorization does not create or validate such an ACL: ```python def _private_directory(path: Path) -> None: # POSIX gets explicit 700/600. On Windows the state directory lives under # the user profile, whose default ACL is already private to the user — # the same posture as gh/aws/gcloud credential stores. The former custom # DACL ceremony was dropped deliberately: its command patterns read as # hostile to agent safety policies and endpoint security, failing installs # while adding no protection an elevated administrator could not bypass. path.mkdir(mode=0o700, parents=True, exist_ok=True) if os.name == "posix": path.chmod(0o700) def _restrict_file(path: Path) -> None: if os.name == "posix": path.chmod(0o600) ``` The MCP client reads the token on Windows without checking ownership or ACL entries: ```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 ...[truncated 2064 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `~/.beatra` and `credentials.json` with explicit Windows DACLs granting access only to the current user and required system principals. 2. Disable inherited ACL entries where appropriate. 3. Before every credential read, validate the file type, owner, reparse-point status, and effective ACL. 4. Reject the credential with a clear recovery message if unexpected users or groups have read access. 5. Use Windows-native APIs through a small reviewed helper or a maintained security library rather than shelling out to ACL-management commands. 6. Protect the token with DPAPI bound to the current user as defense in depth. 7. Preserve atomic file replacement while ensuring the replacement file receives the intended DACL. 8. Add automated tests for permissive parent ACLs, inherited group access, reparse points, alternate home directories, and credential migration. 9. Amend the documentation if the implementation intentionally provides only best-effort profile protection rather than a current-user-only guarantee. ]]>
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 (16)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares no permissions while its documented behavior includes environment access, local file read/write, network use, and shell execution via a bundled client and updater. This creates a transparency and least-privilege failure: users and host systems cannot accurately assess or constrain what the skill can do, increasing the chance of unexpected credential access, file modification, or remote communication.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill's stated purpose is narrow video generation, but the content describes materially broader behaviors: OAuth/device auth, local credential storage, arbitrary remote tool invocation through a generic MCP client, local uploads, telemetry/registration, uninstall-side state deletion, and self-updating code. This mismatch is dangerous because it hides high-risk behaviors behind an innocuous recruiting-video description, undermining informed consent and enabling credential, data, and code-integrity risks far outside user expectations.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The skill includes a silent self-updater that downloads and replaces local package files, which is a code-execution and persistence mechanism unrelated to the core hiring-video task. Even with integrity checks described, silent self-modification expands the trust boundary to the update infrastructure and allows future behavior changes without contemporaneous user review.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The authorization helper fingerprints the host agent environment by inspecting environment variables such as Claude Code, Codex, and AI_AGENT and persists that platform identity. For a skill advertised as creating hiring-avatar videos, collecting execution-environment identity is not necessary to fulfill the stated purpose and creates unnecessary telemetry about the user’s tooling and workstation context.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
This code captures the local hostname and records a device-local inventory of installed skill paths and package metadata in host.json and skills.json. That exceeds the stated recruiting-video purpose and exposes sensitive operational details such as recognizable device names and local installation paths, which can aid profiling or follow-on targeting if accessed by the service or another local component.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill requests a very broad OAuth scope set including wallet spending, task control, artifact read/write, image/video/music/speech generation, and voice read/write, which substantially exceeds what a hiring-avatar workflow would normally require. Overbroad scopes violate least privilege and make any token compromise far more damaging by enabling unrelated capabilities, including financial spend and access to other assets.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The client contains a full self-update and package installation subsystem that is materially unrelated to the advertised hiring-avatar workflow. Even though it includes integrity checks, it gives the skill ongoing authority to download archives and overwrite its own installed files, expanding the trust boundary and creating a software supply-chain risk if the vendor infrastructure, discovery metadata, or signing process is ever compromised.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The skill records local inventory, installation paths, host platform, and registration telemetry unrelated to generating recruiting videos. This broadens data collection beyond the stated purpose and can expose environment metadata and usage tracking to a remote service, which is especially concerning because the functionality is performed automatically and best-effort rather than as an obvious user-initiated action.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code fingerprints the execution environment using environment variables and host.json to derive a platform identity, then attaches that information to tool calls and registration traffic. For a hiring-video skill, this host detection is not obviously necessary and increases privacy and tracking risk by letting the backend distinguish agent environments and installations.

Context-Inappropriate Capability

High
Confidence
88% confidence
Finding
The script can revoke the shared device authorization token used by all Beatra skills on the device, which creates a broad cross-skill impact if its last-skill detection is wrong or manipulated. Even though the code tries to be cautious, granting a hiring-video skill package authority over shared platform credentials exceeds least privilege and could disrupt unrelated skills.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
Installing newer releases without separate confirmation is system-modifying behavior that is not clearly disclosed in the skill description. This is dangerous because users may invoke a media workflow but unintentionally authorize local code changes, making supply-chain or policy regressions harder to detect and audit.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The document states that the client silently checks for updates and automatically installs newer versions by default before ordinary commands, without an explicit user prompt at the time files are changed. Even though the text describes integrity checks and constrained update sources, silent default code replacement is still a supply-chain and user-consent risk because local behavior can change unexpectedly and users may not realize executable files are being modified.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documentation states that the client performs an automatic installation registration call and writes a local cache file, but it does not clearly warn users that metadata will be transmitted externally and that files will be created or updated on disk. Even if the data is described as non-secret and non-billable, silent telemetry and filesystem modification can violate user expectations, compliance requirements, or enterprise policies when performed without explicit notice or consent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill performs silent automatic updates during normal execution paths via maybe_auto_update(), including network fetches and replacement of installation files without a contemporaneous user-facing prompt. In the context of a recruiting-video tool, this is more dangerous because users would not expect routine avatar-generation actions to trigger code download and local package modification, making supply-chain compromise or unexpected behavior harder to notice.

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
86% confidence
Finding
The code reads the shared access token from ~/.beatra/credentials.json and uses it to call the revocation endpoint, giving this skill package direct access to a platform-wide credential. Even without exfiltration, unnecessary access to shared bearer tokens violates least privilege and increases risk if the package or uninstall path is tampered with.

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 skill explicitly exposes self-update capability, allowing it to modify its own installed code. Self-modifying or self-replacing behavior is inherently high risk for agent skills because it changes the executed code after installation, undermines review assumptions, and magnifies the impact of any compromise in the update distribution path.

Static analysis

No suspicious patterns detected.