Back to skill

Security audit

rollinggo-searchhotel

Security checks for vulnerabilities and agentic risk

Overview

This hotel-booking skill is not clearly malicious, but it needs Review because it installs mutable external executables, silently checks remote content, and can handle real booking and order data.

Install only if you trust RollingGo's npm package, GitHub releases, and CLI service. Do not run the installer as administrator/root, require explicit consent before installs, updates, bookings, payment-link generation, or order-history lookup, and prefer a pinned, checksum-verified CLI over @latest or latest-release downloads.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/install.py:62
Finding
Unverified Mutable Executable Download and Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.py:62-74, 134-167`; execution occurs through `scripts/rgh.js:17-24, 43-52` and `scripts/rgh.py:17-25, 43-49` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: Critical ### Vulnerable Code ```python def get_latest_release_assets(): """Query GitHub API for the latest release assets.""" api_url = "https://api.github.com/repos/RollingGo-AI/oauth-hotel-cli/releases/latest" req = urllib.request.Request( api_url, headers={'User-Agent': 'RollingGo-Installer/1.0'} ) try: with urllib.request.urlopen(req) as response: data = json.loads(response.read().decode('utf-8')) return data.get('assets', []), data.get('tag_name', 'latest') except Exception as e: print(f"⚠️ Could not fetch latest release info from GitHub API: {e}") return None, None ``` ```python assets, tag = get_latest_release_assets() download_url = None if assets: for asset in assets: name = asset.get('name', '').lower() if asset_keyword in name: download_url = asset.get('browser_download_url') print(f"Found matching asset for version {tag}: {asset.get('name')}") break if not download_url: print("Using hardcoded fallback download URL...") if system == "windows": download_url = "https://github.com/RollingGo-AI/oauth-hotel-cli/releases/latest/download/rgh-win.exe" elif system == "darwin": download_url = "https://github.com/RollingGo-AI/oauth-hotel-cli/releases/latest/download/rgh-macos" else: download_url = "https://github.com/RollingGo-AI/oauth-hotel-cli/releases/latest/download/rgh-linux" success = download_binary(download_url, dest_path) if not success and assets and system == "windows": print("Retrying with alternative Windows asset name...") download_url = "https://github.com/RollingGo-AI/oauth-hotel-cli/releases/late ...[truncated 3186 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin a specific, reviewed CLI version rather than resolving `latest`. 2. Maintain an explicit mapping of supported operating-system and architecture combinations to exact asset names and SHA-256 digests. 3. Download to a newly created temporary file and verify its digest before moving it atomically into `bin`. 4. Verify a cryptographic release signature against a trusted public key bundled with the Skill. 5. Validate that API-provided and final redirected URLs use HTTPS and match an exact approved host and repository path. 6. Reject ambiguous substring matches; require exact asset names and validate architecture as well as operating system. 7. Apply restrictive file permissions and refuse to overwrite an existing binary unless the replacement passes all checks. 8. Minimize the environment passed to the child process, supplying only variables required by the CLI. 9. Document the pinned version and provide a separately reviewed upgrade process rather than automatically trusting future releases. ]]>

T08 · Insecure Dependencies

Error
Location
scripts/install.py:17
Finding
Unpinned Global npm Package Installation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.py:17-31`; also declared in `SKILL.md:14-20, 43-47` **Vulnerability Type**: Insecure dependency installation **Risk Level**: High ### Vulnerable Code ```python def install_via_npm(): """Attempt to install @rollinggo/hotel via npm.""" print("Checking npm environment...") npm_cmd = "npm.cmd" if platform.system() == "Windows" else "npm" # Try global installation print("Attempting to install @rollinggo/hotel globally via npm...") code, stdout, stderr = run_command([npm_cmd, "install", "-g", "@rollinggo/hotel@latest"]) if code == 0: print("✅ Successfully installed @rollinggo/hotel globally via npm!") return True print("⚠️ npm global installation failed (might need administrator/sudo permissions).") print(stderr) return False ``` The same mutable dependency is declared and recommended in the Skill documentation: ```yaml { "id": "node", "kind": "node", "package": "@rollinggo/hotel@latest", "bins": ["rgh"], "label": "Install @rollinggo/hotel (npm)" } ``` ```bash npm install -g @rollinggo/hotel@latest ``` ### Technical Analysis The installer resolves `@rollinggo/hotel@latest`, so the installed code can change without any modification to the audited Skill. No exact version, lockfile, or package integrity value is enforced. npm packages may execute lifecycle scripts during installation. Consequently, compromise of the package publisher, registry account, release process, or a future package version can cause code to run during installation. The `-g` option installs the package globally rather than restricting it to the Skill, unnecessarily increasing persistence and the set of workflows that may later resolve the installed `rgh` command. The package name is scoped, reducing ordinary typosquatting exposure, but it does not address publisher compromise or unsafe future releases. ### Attack Path 1. An attacker gains control of t ...[truncated 1144 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `@latest` with an exact, reviewed package version. 2. Install the dependency locally inside the Skill rather than globally. 3. Commit and enforce a lockfile containing package integrity metadata. 4. Use a clean package manifest and `npm ci` so installation fails if the lockfile and manifest differ. 5. Disable lifecycle scripts with `--ignore-scripts` when the package does not strictly require them; otherwise audit every required script. 6. Verify package provenance or registry signatures where supported. 7. Avoid running installation as root or administrator. 8. Treat upgrades as explicit, reviewed changes that update both the pinned version and integrity data. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/rgh.js:32
Finding
Windows Shell Command Injection Through Forwarded CLI Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rgh.js:32-50`; corresponding Python behavior at `scripts/rgh.py:43-49` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```javascript function sanitizeArgs(args) { return args.map((arg) => { if (typeof arg === 'string' && arg.includes(' ') && !arg.startsWith('"')) { return `"${arg}"`; } return arg; }); } const { cmd, shell, fallbackCmd } = resolveExecutable(); function runChild(targetCmd, useShell) { const args = useShell ? sanitizeArgs(process.argv.slice(2)) : process.argv.slice(2); const child = spawn(targetCmd, args, { stdio: 'inherit', env: process.env, shell: useShell, }); ``` The Python wrapper has the same shell boundary for Windows `.cmd` targets: ```python def main(): args = [target_cmd] + sys.argv[1:] try: use_shell = is_win and target_cmd.endswith(".cmd") res = subprocess.run(args, shell=use_shell) sys.exit(res.returncode) ``` ### Technical Analysis On Windows, the JavaScript wrapper resolves the normal fallback to `rgh.cmd` with `shell: true`. The Python wrapper similarly uses `shell=True` when the resolved executable ends in `.cmd`. Arguments are forwarded from `process.argv` or `sys.argv`, and hotel search or booking fields may be derived from user conversation. The JavaScript `sanitizeArgs` function only adds quotes when an argument contains a space. It does not reliably escape Windows command-shell metacharacters or expansion syntax, including `&`, `|`, `<`, `>`, `^`, `%`, and related quoting edge cases. Arguments without spaces are returned unchanged. Manual quoting is also not a complete substitute for the platform-specific parsing and escaping required by `cmd.exe`. Although argument-array APIs are used, enabling the shell reintroduces command parsing. A crafted value can therefore be interpreted as additional shell syntax rather than as data intended solely for t ...[truncated 1211 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not invoke user-influenced arguments through `cmd.exe`. 2. Resolve and execute a native `rgh.exe` directly with an argument array and `shell: false`. 3. Remove `shell: true` and `shell=True` from the wrappers. 4. If compatibility with a `.cmd` launcher is unavoidable, use a well-tested Windows command-line escaping implementation rather than manual quoting. 5. Apply strict validation to command names and typed parameters: - allowlist supported subcommands; - enforce numeric formats for identifiers, prices, counts, and dates; - reject control characters and shell metacharacters from free-text fields where feasible. 6. Pass user-provided values only as discrete arguments and never concatenate them into a command string. 7. Add Windows-specific tests containing spaces, quotes, percent expansion, carets, ampersands, pipes, and redirection characters to confirm that all input remains literal data. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose is hotel booking, but the skill also instructs the agent to perform environment management, package installation, binary download, permission changes, remote version checks, and local file writes. That mismatch is dangerous because users and platform policy may authorize a booking assistant but not expect broad system administration and software installation behavior.

Ae1

High
Category
analysis-evasion
Content
> - **每日检查**:若当前目录下的 `.version_check` 不存在或记录日期非今天,静默请求远端 `SKILL.md`(地址拼接规则:将元数据中的 `{repository}` 域名替换为 `raw.githubusercontent.com`,并在末尾追加 `/main/skills/{name}/S
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill requires shell, network, and environment access but does not declare any explicit tool scope or permission boundaries. This increases the blast radius of the skill because an agent may execute installation, login, network fetch, and local file operations without a clear least-privilege policy.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
L005 的描述与整份技能说明均面向中文场景本身并非问题,但规则要求检查是否强制特定语言。文档中的交互模板和触发要求全部固定为中文,未说明可根据用户偏好切换语言,也未提供语言选择或 opt-in。

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The skill directs the agent to silently fetch a remote `SKILL.md` daily and write a `.version_check` file locally. Silent network access plus local state mutation exceeds the core booking function and can be abused for covert tracking, persistence markers, or unexpected remote-content influence.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill includes undisclosed silent remote requests and local file writes without clearly informing the user of those side effects. Lack of transparency undermines informed consent and can conceal network and filesystem activity that users would not reasonably expect from a hotel-search assistant.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The update instruction uses `npx skills` without pinning a version, which allows execution of whatever package version is current at runtime. This creates a supply-chain risk where behavior can change unexpectedly or a compromised upstream release could execute arbitrary code in the agent environment.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill instructs the agent to run npm and Python installation commands and to download standalone binaries from releases, all within the booking workflow. These actions grant code execution and software installation capabilities unrelated to hotel booking, creating significant supply-chain and host-compromise risk if the installer or downloaded artifacts are malicious or tampered with.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases are overly broad and include common expressions, increasing the chance the skill activates when the user did not intend to book or search for hotels. In this skill, mistaken activation is more dangerous because the workflow can proceed to login, network access, and potentially commercial actions.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The documented `book` command collects personally identifiable information including contact email and guest names, but the skill documentation provides no warning, minimization guidance, or handling constraints for this sensitive data. In an agent-driven workflow, that omission increases the chance the agent will solicit, echo, log, or persist PII without clear user consent or privacy safeguards.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The `orders` and `order-detail` capabilities expose historical booking records along with contact details, hotel stay data, and order identifiers, yet the documentation includes no warning about sensitive data access or visibility. In a conversational agent context, this can lead to overbroad retrieval or disclosure of prior bookings to the wrong user/session if identity and authorization checks are not emphasized.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_command(args):
    """Run a system command and return exit code and output."""
    try:
        result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        return result.returncode, result.stdout, result.stderr
    except Exception as e:
        return -1, "", str(e)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
print("✅ Successfully installed @rollinggo/hotel globally via npm!")
        return True
    
    print("⚠️ npm global installation failed (might need administrator/sudo permissions).")
    print(stderr)
    return False
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_latest_release_assets():
    """Query GitHub API for the latest release assets."""
    api_url = "https://api.github.com/repos/RollingGo-AI/oauth-hotel-cli/releases/latest"
    req = urllib.request.Request(
        api_url,
        headers={'User-Agent': 'RollingGo-Installer/1.0'}
Confidence
88% confidence
Finding
The installer fetches release metadata from GitHub and later downloads/install binaries from remote URLs without any authenticity or integrity verification such as signatures or pinned checksums. In an installation script for a hotel-booking skill, this is more dangerous because it introduces a software supply-chain risk unrelated to the core business function and can lead to arbitrary code execution if the upstream release channel or network path is compromised.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Check if node and npm are available
    has_node = False
    try:
        node_code = subprocess.call(["node", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        npm_code = subprocess.call(["npm" if platform.system() != "Windows" else "npm.cmd", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        has_node = (node_code == 0 and npm_code == 0)
    except Exception:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
has_node = False
    try:
        node_code = subprocess.call(["node", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        npm_code = subprocess.call(["npm" if platform.system() != "Windows" else "npm.cmd", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        has_node = (node_code == 0 and npm_code == 0)
    except Exception:
        has_node = False
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The wrapper will execute any `rgh` binary found in the local `bin` directory or on the system PATH, with all user-supplied arguments passed through unchanged. In a skill intended specifically for hotel booking, this creates trust in an external executable outside the reviewed skill code, so a malicious or trojanized `rgh` on PATH could perform arbitrary actions under the agent's privileges.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
args = [target_cmd] + sys.argv[1:]
    try:
        use_shell = is_win and target_cmd.endswith(".cmd")
        res = subprocess.run(args, shell=use_shell)
        sys.exit(res.returncode)
    except KeyboardInterrupt:
        sys.exit(130)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
This file contains natural-language comments and console error output in Chinese, including the user-facing messages at L62-L63, with no indication that the skill is Chinese-only or that users can choose a language. That creates a locale policy concern because the script imposes a specific language on users without opt-in or justification.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/rgh.js:46