Back to skill

Security audit

REDnote Note Copywriter

Security checks for vulnerabilities and agentic risk

Overview

This copywriting skill includes a broad remote Beatra client with shared credentials, silent self-updates, file upload, and telemetry that go beyond text-only REDnote copywriting.

Review this skill before installing. It is not just a static writing prompt: it can authorize a broad Beatra account token, store shared local state, upload selected local files, call arbitrary Beatra MCP tools, send host/package metadata, revoke shared Beatra access on uninstall, and silently update its executable files. Install only if you accept those Beatra integration and update behaviors for this copywriting package.

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)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Self-Update Mechanism Permits Post-Review Remote Code Replacement<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1018` **Related Locations**: `scripts/mcp_client.py:31-32`, `scripts/mcp_client.py:334-358`, `scripts/mcp_client.py:469-490`, `scripts/mcp_client.py:1543-1544`; `SKILL.md:77-94`; `references/automatic-updates-and-safety.md:3-7` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: Critical ### 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, ) ...[truncated 3573 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic updates by default. Require explicit, informed user confirmation before downloading or replacing executable files. 2. Sign discovery metadata and release artifacts with an offline-controlled release key. 3. Embed or securely pin the corresponding public key in the reviewed package. 4. Verify a detached signature over the version, package identity, channel, manifest hash, and archive hash before accepting an update. 5. Do not treat hashes supplied by the same update server as proof of publisher authenticity. 6. Add rollback protection based on signed release metadata rather than semantic-version comparison alone. 7. Prefer updates delivered through an audited package repository or host-managed Skill installation mechanism instead of runtime self-modification. 8. Present the target version and changed files to the user before installation. 9. Preserve the existing archive traversal, symlink, file-size, ownership, locking, backup, and rollback protections as defense-in-depth. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:33
Finding
Text-Only Copywriter Requests Unrelated Wallet, Generation, Upload, and Task Privileges<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:33-37` **Related Locations**: `scripts/mcp_client.py:1428-1457`, `scripts/mcp_client.py:1460-1483`; `references/installation-and-auth.md:72-74`; `SKILL.md:71-75` **Vulnerability Type**: Excessive authorization scope and generic remote capability exposure **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 bundled client exposes local-file upload functionality: ```python def upload( path: Path, *, mime_type: str, state_dir: Path | None = None, post_json: PostJson = _default_post_json, put_bytes: PutBytes = _default_put_bytes, ) -> dict[str, str]: if re.fullmatch(r"[a-z0-9][a-z0-9.+-]*/[a-z0-9][a-z0-9.+-]*", mime_type) is None: raise RuntimeError("Local upload MIME type is invalid") filename, content = _read_local_upload(path) resolved_state_dir = state_dir or Path.home() / ".beatra" session = _session_with_registration(state_dir=resolved_state_dir, post_json=post_json) result = session.request( 2, "tools/call", { "name": "beatra.assets.upload", "arguments": { "filename": filename, "mime_type": mime_type, "size_bytes": len(content), }, }, ) if failure := _tool_failure_message(result): raise RuntimeError(f"Beatra rejected the upload grant request: {failure}") return _complete_upload( result, mime_type=mime_type, content=content, put_bytes=put_bytes, ) ``` It also supports arbitrary MCP tool names: ```python def _run_command(command: str, tool_name: str | None = None) -> dict[str, Any]: session = _session_with_registration( state_dir=Path.home() / ".beatra", ...[truncated 3091 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove remote authorization entirely if the output is generated exclusively in the current conversation. 2. If remote access is genuinely required, define a dedicated copywriting scope that cannot spend funds, generate media, upload files, manage voices, or cancel tasks. 3. Remove `wallet:spend`, media-generation, voice-write, artifact-write, upload, and task-cancellation permissions. 4. Replace the generic tool-name dispatcher with a strict allowlist of operations required by this specific Skill. 5. Remove the local-file upload command from this package. 6. Use per-package credentials instead of one shared full-scope device token. 7. Add server-side authorization checks that bind the credential to the package and allowed tool names. 8. Clearly disclose every requested permission before authorization and obtain separate confirmation for paid or data-upload capabilities. 9. Use short-lived tokens and support immediate local and server-side revocation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/authorize.py:346
Finding
Authorization Collects and Transmits Hostname, Agent Environment, and Stable Installation Identity<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:346-368` **Related Locations**: `scripts/authorize.py:479-493`; `scripts/mcp_client.py:1145-1163`, `scripts/mcp_client.py:1213-1224`, `scripts/mcp_client.py:1354-1396`; `references/installation-registration.md:4-19` **Vulnerability Type**: Unnecessary host reconnaissance and installation telemetry **Risk Level**: Medium ### Vulnerable Code ```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 and platform are included in 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, "pac ...[truncated 3062 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove hostname collection and transmission from the authorization flow. 2. Default platform attribution to an anonymous value unless it is technically required. 3. Make diagnostic and telemetry collection opt-in rather than mandatory. 4. Display the exact telemetry fields and their purpose before obtaining consent. 5. Avoid stable cross-session identifiers when an ephemeral session identifier is sufficient. 6. If installation registration is operationally necessary, use a random, rotating pseudonymous identifier with a documented retention period. 7. Do not attach package and platform telemetry to every tool call unless required for security enforcement. 8. Provide a local configuration option that disables registration and source attribution without disabling the core copywriting function. 9. Document server-side retention, access controls, deletion procedures, and telemetry minimization. ]]>
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 (18)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill presents itself as a text-only copywriting package, yet the documentation indicates capabilities consistent with shell execution, filesystem access, environment access, and network activity without declaring permissions. This creates a hidden trust boundary: users and hosting systems may grant or route the skill as low-risk content generation while it can access local state and external services.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior goes far beyond generating REDnote copy: it includes authentication, credential storage, remote tool invocation, file upload, telemetry/registration, uninstall logic, and self-update. That mismatch is dangerous because reviewers and users may authorize the skill for harmless marketing assistance while it actually introduces remote-code-adjacent supply-chain and data-exfiltration pathways.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill documentation instructs use of a bundled client that performs network contact, self-registration, and other lifecycle actions unrelated to copy generation. Embedding operational side effects inside a content skill increases the chance of unnoticed credential handling, unexpected outbound communication, and abuse of the agent runtime under a misleading package identity.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
Automatic updates and installation registration are not necessary for a static copywriting workflow, so including them expands attack surface without clear business need. If the update or registration channel is compromised, the skill could be silently modified or enrolled into remote infrastructure beyond what users expected when invoking a writing assistant.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script requests a very broad OAuth scope set including artifacts, images, videos, music, speech, wallet spending, and task control, which is far beyond what a REDnote/Xiaohongshu copywriting skill should need. Over-scoped tokens violate least privilege and materially increase blast radius if the credential is abused, leaked, or reused by other components sharing the same Beatra state directory.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The authorization flow detects and persists host platform information and a device-recognizable hostname, then transmits platform metadata during authorization. For a copywriting skill, collecting and storing device-identifying metadata is not obviously necessary and creates avoidable privacy exposure and host fingerprinting value.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The script records a local inventory of installed skills including absolute install paths and platform data in ~/.beatra/skills.json. For a note-copy generation skill this exceeds functional necessity, and the stored path inventory can reveal sensitive local filesystem layout, user behavior, and installed package footprint to any process that can read the file.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The client includes broad self-update, installation registration, and local inventory behavior that is unrelated to a REDnote copywriting skill. Even though the update path has multiple integrity checks, bundling autonomous code modification and device-tracking capabilities into a content-generation skill materially expands attack surface and trust requirements; compromise of the vendor/update channel would let the package replace its own local files.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The code fingerprints the host environment and sends installation registration telemetry that is not necessary for generating REDnote copy. In this skill context, collecting platform identity and installation references increases privacy risk and creates hidden data flows users would not reasonably expect from a copywriter tool.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The client can upload arbitrary local files via a generic upload flow, which is not justified by the stated copywriting purpose. In the context of an agent skill, unnecessary file-upload capability raises exfiltration risk if the tool is misused, invoked unexpectedly, or later combined with prompt-driven workflows.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The uninstall flow can revoke a shared Beatra device authorization and remove global state under ~/.beatra, affecting other skills on the device. Although the script contains guardrails to avoid revoking when inventory is uncertain or other skills appear present, this behavior still exceeds the expected scope of a copywriting skill and creates cross-skill impact if inventory is stale, tampered with, or incomplete.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The script performs a network POST to revoke a device token, which gives the package capability to alter authentication state outside its stated copywriting purpose. Even if used only during uninstall, embedding credential-revocation logic inside the skill increases the attack surface and enables disruption of platform access if the package or uninstall path is abused.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that newer releases install automatically without separate confirmation, which is a classic supply-chain risk. Silent modification of locally installed package files can bypass user review and change the skill's behavior after initial approval, especially dangerous in an agent environment with file, network, and shell capabilities.

Missing User Warnings

Low
Confidence
92% confidence
Finding
The document states that the client performs an outbound registration call and writes a local cache file, but it does not explicitly warn users that metadata will be transmitted off-host or that filesystem state will be modified on first use. While the transmitted data appears limited and non-secret, the lack of clear disclosure can undermine informed consent, create privacy/compliance issues, and surprise users in restricted or audited environments.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The maybe_auto_update() path silently performs network checks and can modify installed package files during normal command execution without runtime disclosure. Silent background updates are especially risky in a copywriting skill because they introduce hidden behavior changes and code replacement outside the user’s expected task scope.

Credential Access

High
Category
Privilege Escalation
Content
scope = _required_string(polled, "scope")
            if set(scope.split()) != set(SCOPE.split()):
                raise RuntimeError("Beatra authorization returned an unsupported scope")
            credential_path = state_dir / "credentials.json"
            _atomic_json(
                credential_path,
                {
Confidence
93% confidence
Finding
The script stores a live Bearer access token in a shared local credentials.json under ~/.beatra for reuse across skills. Even though file permissions are restricted, compromise of the local user context, another overly privileged skill, or accidental inclusion of the state directory in backups/logs would expose a broadly scoped token with significant account capabilities.

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
88% confidence
Finding
The script reads an access token from ~/.beatra/credentials.json and uses it to attempt revocation against the Beatra API. While there is no evidence of exfiltration, this is still direct handling of shared credentials by a skill package that does not need such access for its advertised copywriting function, making accidental or malicious misuse possible.

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 package exposes a self-update mechanism that can replace its own installed files. In isolation this may be an intended maintenance feature, but in the context of a narrowly scoped REDnote copywriting skill it is a dangerous capability because any compromise of the distribution or trust chain enables local code modification under the guise of ordinary skill usage.

Static analysis

No suspicious patterns detected.