Back to skill

Security audit

marriott

Security checks for vulnerabilities and agentic risk

Overview

This Marriott booking skill has a coherent purpose, but it needs Review because it reuses raw browser session data and can submit real reservations with weak safeguards.

Install only if you are comfortable giving the skill control of a logged-in Marriott browser session and accepting the risk that copied cookies, screenshots, and booking data may remain on disk. Use a dedicated Marriott-only browser profile, avoid copying your main Chrome cookies, remove cookies.json and debug files after use, and manually verify the final room, rate, cancellation policy, total price, and payment method before any reservation is submitted.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
skill-search.js:84
Finding

Browser Cookies Are Exported Broadly and Stored in Plaintext

Content
View full analysis
c.name === 's_loginState'); ``` ```js // 页面加载后保存最新 cookies(remembered → authenticated 后更新) const freshCookies = await ctx.cookies(); fs.writeFileSync(path.join(__dirname, 'cookies.json'), JSON.stringify(freshCookies, null, 2)); ``` The stored cookies are later loaded into new browser contexts: ```js const cookiesFile = path.join(__dirname, 'cookies.json'); if (!fs.existsSync(cookiesFile)) { process.stderr.write('❌ 没有 cookies.json 且无法连接 Chrome\n'); process.exit(1); } ... await ctx.addCookies(JSON.parse(fs.readFileSync(cookiesFile))); ``` ### Technical Analysis `BrowserContext.cookies()` is called without restricting the request to Marriott URLs. Consequently, the code may export every cookie accessible through the selected Chrome browser context rather than only cookies belonging to `marriott.com.cn`. The resulting cookie collection is serialized as plaintext JSON in the project directory. The write does not explicitly set restrictive permissions, encrypt the file, establish a retention period, or remove the file after the operation. The same file is subsequently treated as a reusable authenticated browser state. This creates two related security failures: 1. The Skill collects more authentication material than is necessary for its declared Marriott booking function. 2. High-value session credentials are persisted as an ordinary plaintext file. ### Attack Path ...[truncated 1536 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
使用指南.md:19
Finding

Documentation Copies Sensitive Chrome Profile State into a Predictable Temporary Directory

Content
View full analysis
/dev/null; sleep 2 # 复制你的 Cookie 到临时目录(带上登录态) rm -rf /tmp/chrome-marriott-profile mkdir -p /tmp/chrome-marriott-profile/Default cp ~/Library/Application\ Support/Google/Chrome/Default/Cookies \ /tmp/chrome-marriott-profile/Default/ cp ~/Library/Application\ Support/Google/Chrome/Local\ State \ /tmp/chrome-marriott-profile/ # 启动 Chrome "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \ --remote-debugging-port=9222 \ --user-data-dir=/tmp/chrome-marriott-profile \ --no-first-run \ --no-default-browser-check \ --disable-blink-features=AutomationControlled \ > /tmp/chrome.log 2>&1 & ``` The guide also recommends refreshing the copied cookie database: ```bash pkill -f "Google Chrome" 2>/dev/null; sleep 1 cp ~/Library/Application\ Support/Google/Chrome/Default/Cookies \ /tmp/chrome-marriott-profile/Default/ ``` ### Technical Analysis The documented setup copies the user's default Chrome cookie database and `Local State` file into a fixed path under `/tmp`. The directory is neither created atomically nor protected with an explicit restrictive `umask` or permission setting. A predictable temporary path creates opportunities for local race conditions and path manipulation. For example, another local process may monitor the path, pre-create its components, or attempt symbolic-link manipulation. Even without a race, copying the entire default cookie database places authentication material for potentially unrelated websites in a second location with an unclear lifecycle. Copying `Local State` alongside the cookie database also broadens exposure of browser-profile metadata and key-management material. The operation exceeds the minimum data access required ...[truncated 1616 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
skill-book.js:76
Finding

Chromium Sandbox Is Disabled During Processing of Remote Web Content

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
skill-book.js:133
Finding

Fragile DOM Selection Can Submit an Unintended Booking or Payment Choice

Content
View full analysis
{ const btns = [...document.querySelectorAll('button')].filter(b => b.textContent.trim() === '选择'); const btn = btns[idx]; if (btn) { btn.click(); return true; } return false; }, btnIdx); if (!clicked) { process.stderr.write(`❌ 未找到第 ${btnIdx} 个"选择"按钮\n`); await page.screenshot({ path: path.join(__dirname, 'debug-no-btn.png') }); await browser.close(); process.exit(1); } ``` The payment logic falls back to the first arbitrary radio input: ```js const paymentClicked = await page.evaluate(() => { const radios = [...document.querySelectorAll('input[type="radio"]')] .filter(r => { const label = r.closest('label') || document.querySelector(`label[for="${r.id}"]`); const text = (label ? label.innerText : '') + r.name + r.id; return /card|credit|信用|payment|pay/i.test(text); }); if (radios.length > 0) { radios[0].click(); return 'radio:' + (radios[0].id || radios[0].name); } const btns = [...document.querySelectorAll('button, a')]; const useBtn = btns.find(b => /使用此卡|使用|Use this card/i.test(b.textContent)); if (useBtn) { useBtn.click(); return 'use-btn:' + useBtn.textContent.trim(); } const anyRadio = document.querySelector('input[type="radio"]'); if (anyRadio) { anyRadio.click(); return 'any-radio'; } return null; }); ``` The final action also uses broad text and class selectors: ```js const confirmBtn = page.locator( 'button.m-button-l:has-text("立即预订"),button:has-text("立即预订"),' + 'button:has-text("确认预订"),button:has-text("提交"),button:has-text("完成预订"),' + 'button:has-text("Reserve Now"),button[class*="book-now"],button[class*="confirm"]' ).first(); if (await confirmBtn.isVisible({ tim ...[truncated 3105 chars]
Remediation
View remediation
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (50)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill also relies on a local logged-in Chrome state, writes data to disk, and calls a third-party geocoding service, none of which are obvious from the top-level description. Even if these behaviors support the booking workflow, failing to disclose off-site requests and local persistence can lead to unexpected data sharing and mistaken assumptions about the skill’s capabilities and privacy impact.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill also relies on a local logged-in Chrome state, writes data to disk, and calls a third-party geocoding service, none of which are obvious from the top-level description. Even if these behaviors support the booking workflow, failing to disclose off-site requests and local persistence can lead to unexpected data sharing and mistaken assumptions about the skill’s capabilities and privacy impact.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill also relies on a local logged-in Chrome state, writes data to disk, and calls a third-party geocoding service, none of which are obvious from the top-level description. Even if these behaviors support the booking workflow, failing to disclose off-site requests and local persistence can lead to unexpected data sharing and mistaken assumptions about the skill’s capabilities and privacy impact.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The skill also relies on a local logged-in Chrome state, writes data to disk, and calls a third-party geocoding service, none of which are obvious from the top-level description. Even if these behaviors support the booking workflow, failing to disclose off-site requests and local persistence can lead to unexpected data sharing and mistaken assumptions about the skill’s capabilities and privacy impact.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This is a true vulnerability because the instructions allow execution of a live booking step without a strong warning that the action may submit a real reservation tied to the user’s account and potential payment obligations. In a travel-booking skill, insufficient warning materially increases the risk of unintended purchases, cancellation penalties, and disputes over account activity.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

md
- **Linux 环境**:Mac 上运行完 `skill-search.js` 后,将 `cookies.json` + `selection.json` + `rooms-results.json` 传至 Linux,再运行 `skill-book.js`(自动使用 headless Chromium)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

md
- **Linux 环境**:Mac 上运行完 `skill-search.js` 后,将 `cookies.json` + `selection.json` + `rooms-results.json` 传至 Linux,再运行 `skill-book.js`(自动使用 headless Chromium)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 15)May include surrounding context.

sh
# 安装 Playwright Chromium
npx playwright install chromium

# 初始化 .env
if [ ! -f "$SKILL_DIR/.env" ]; then
  cp "$SKILL_DIR/.env.example" "$SKILL_DIR/.env"
  echo "已创建 .env(当前版本无需填写账号,保留备用)"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 20)May include surrounding context.

sh
# 安装 Playwright Chromium
npx playwright install chromium

# 初始化 .env
if [ ! -f "$SKILL_DIR/.env" ]; then
  cp "$SKILL_DIR/.env.example" "$SKILL_DIR/.env"
  echo "已创建 .env(当前版本无需填写账号,保留备用)"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 16)May include surrounding context.

sh
npx playwright install chromium

# 初始化 .env
if [ ! -f "$SKILL_DIR/.env" ]; then
  cp "$SKILL_DIR/.env.example" "$SKILL_DIR/.env"
  echo "已创建 .env(当前版本无需填写账号,保留备用)"
else

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 17)May include surrounding context.

sh
npx playwright install chromium

# 初始化 .env
if [ ! -f "$SKILL_DIR/.env" ]; then
  cp "$SKILL_DIR/.env.example" "$SKILL_DIR/.env"
  echo "已创建 .env(当前版本无需填写账号,保留备用)"
else

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding

The skill uses 'env' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Content

No source excerpt is available for this finding.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Confidence
88% confidence
Finding

The script reuses an existing Chrome session over CDP or imports cookies from cookies.json into a fresh browser context, enabling authenticated actions as whoever owns those session cookies. In an automation skill that can place bookings, this creates significant account-takeover and unauthorized-transaction risk if cookies are copied, stale, or obtained without strong provenance and user consent.

Content

Scanner excerpt · skill-book.js (reported line 73)May include surrounding context.

js
ium.connectOverCDP('http://localhost:9222', { timeout: 3000 });
    const ctx = b.contexts()[0];
    const pages = ctx.pages();
    const page = pages.find(p => p.url().includes('marriott')) || await ctx.newPage();
    return { browser: b, page };
  } catch (_) {}

  const cookiesFile = path.join(__dirname, 'cookies.json');
  if (!fs.existsSync(cookiesFile)) {
    process.stderr.write('❌ 没有 cookies.json 且无法连接 Chrome\n');
    process.exit(1);
  }
  const isLinux = process.platform === 'linux';
  const launchOpts = {
    headless: isLinux,
    args: ['--disable-blink-features=AutomationControlled', '--no-sandbox'],
  };
  if (process.platform === 'darwin') { launchOpts.channel = 'chrome'; launchOpts.headless = false; }

  const b = await chromium.launch(launchOpts);
  const ctx = await b.newContext({
    viewport: { width: 1366, height: 768 },
    locale: 'zh-CN',
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Confidence
96% confidence
Finding

The script reuses an existing Chrome session via CDP or imports cookies from cookies.json into a browser context, effectively leveraging authenticated session material for automated access. In an agent skill, this is dangerous because session cookies are sensitive credentials; if mishandled, copied, or broadened beyond the intended domain, they can enable account hijacking or unauthorized booking actions.

Content

Scanner excerpt · skill-rooms.js (reported line 46)May include surrounding context.

js
ium.connectOverCDP('http://localhost:9222', { timeout: 3000 });
    const ctx = b.contexts()[0];
    const pages = ctx.pages();
    const page = pages.find(p => p.url().includes('marriott')) || await ctx.newPage();
    return { browser: b, page };
  } catch (_) {}

  const cookiesFile = path.join(__dirname, 'cookies.json');
  if (!fs.existsSync(cookiesFile)) {
    process.stderr.write('❌ 没有 cookies.json 且无法连接 Chrome\n');
    process.exit(1);
  }
  const isLinux = process.platform === 'linux';
  const launchOpts = {
    headless: isLinux,
    args: ['--disable-blink-features=AutomationControlled', '--no-sandbox'],
  };
  if (process.platform === 'darwin') { launchOpts.channel = 'chrome'; launchOpts.headless = false; }

  const b = await chromium.launch(launchOpts);
  const ctx = await b.newContext({
    viewport: { width: 1366, height: 768 },
    locale: 'zh-CN',
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding

The skill uses 'network' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The comments and flow explicitly describe avoiding Akamai detection and use direct URL jumping plus browser-state reuse to bypass normal anti-bot controls. Circumventing a target site's protective mechanisms is outside ordinary hotel-search behavior and can facilitate unauthorized automation using a logged-in user's session.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill-book.js (reported line 11)May include surrounding context.

js
*/

const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const { chromium } = require('playwright');
const minimist = require('minimist');
const fs = require('fs');

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill-rooms.js (reported line 12)May include surrounding context.

js
*/

const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const { chromium } = require('playwright');
const minimist = require('minimist');
const fs = require('fs');

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill-search.js (reported line 11)May include surrounding context.

js
*/

const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const { chromium } = require('playwright');
const minimist = require('minimist');
const fs = require('fs');

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code reads cookies from an authenticated browser context and persists the full cookie jar to cookies.json on disk. Stored session cookies can be replayed by other local processes or later exfiltrated, enabling account takeover or unauthorized actions in the user's Marriott session.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

Enabling Chrome remote debugging on port 9222 exposes a powerful browser control interface that can read cookies, inspect pages, execute actions, and drive authenticated sessions. The guide presents this as routine setup without warning that local malware, other users, or misconfigured network exposure could take over the browser and any logged-in accounts.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The guide explicitly instructs users to copy Chrome's cookie database from their real browser profile into a temporary profile so the skill can inherit an authenticated session. That is credential/session theft behavior in practice: cookies are authentication material, and duplicating them enables account access without re-authenticating through a supported flow.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The guide tells users to copy highly sensitive cookie files and local state into another profile with no security warning, storage protection guidance, or cleanup requirements. This materially increases the chance of session compromise because those files can be reused by the skill or any other process with access to the temporary directory.

Content

No source excerpt is available for this finding.

Ssd 4

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The guide provides a step-by-step narrative for borrowing a user's privileges by extracting real browser cookies, launching a debuggable profile, and then using that authenticated session for automation. In this context, the staged workflow meaningfully increases danger because it operationalizes session hijacking rather than merely referencing it abstractly.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% 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).

Content

Scanner excerpt · 使用指南.md (reported line 22)May include surrounding context.

md
pkill -f "Google Chrome" 2>/dev/null; sleep 2

# 复制你的 Cookie 到临时目录(带上登录态)
rm -rf /tmp/chrome-marriott-profile
mkdir -p /tmp/chrome-marriott-profile/Default
cp ~/Library/Application\ Support/Google/Chrome/Default/Cookies \
   /tmp/chrome-marriott-profile/Default/

Static analysis

No suspicious patterns detected.