Back to skill

Security audit

Publish Antigravity Rotator

Security checks for vulnerabilities and agentic risk

Overview

This skill matches its account-rotation purpose, but it exposes powerful account and session controls too broadly for safe default installation.

Install only if you are prepared to audit and harden it first. At minimum, bind the dashboard to 127.0.0.1, add authentication, remove the bundled OAuth secret, replace shell-string exec calls with argument-array process calls, validate model/account inputs, and make auth-profile/session changes explicit and reversible.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/dashboard.js:361
Finding
Unauthenticated Administrative API Exposed on All Network Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dashboard.js:361-414` **Vulnerability Type**: Missing authentication and authorization on a network-accessible management interface **Risk Level**: High ### Vulnerable Code ```js start() { const port = this.config.dashboardPort || 18090; const server = http.createServer(async (req, res) => { if (req.url === '/' && req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(this.generateHTML()); } else if (req.url === '/api' && req.method === 'POST') { let body = ''; req.on('data', chunk => body += chunk); req.on('end', async () => { try { const data = JSON.parse(body); const result = await this.handleApi(data); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(result)); } catch (e) { res.writeHead(400); res.end(JSON.stringify({ success: false, message: e.message })); } }); } else { res.writeHead(404); res.end('Not Found'); } }); server.listen(port, '0.0.0.0', () => { console.log(`\n🔮 Antigravity Dashboard started on http://0.0.0.0:${port}`); }); } async handleApi(data) { const { action } = data; let changed = false; const currentConfig = this.readJson(this.configPath); if (action === 'addAccount') { if (!currentConfig.accounts.includes(data.email)) { currentConfig.accounts.push(data.email); changed = true; } } else if (action === 'removeAccount') { currentConfig.accounts = currentConfig.accounts.filter(a => a !== data.email); changed = true; } else if (action === 'setPriority') { if (Array.isArray(data.order) && data.or ...[truncated 2675 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind to `127.0.0.1` or a Unix-domain socket by default: ```js server.listen(port, '127.0.0.1'); ``` 2. Require strong authentication before permitting any API action. For remote administration, place the dashboard behind an authenticated TLS reverse proxy. 3. Implement per-action authorization rather than treating every authenticated user as an administrator. 4. Validate the `Origin` header and use CSRF tokens for browser-originated state-changing requests. 5. Define strict schemas for every action and reject unknown fields, malformed account names, invalid indices, and unapproved model identifiers. 6. Set a small maximum request-body size and terminate oversized requests. 7. Add security logging and rate limiting for failed authentication and state-changing operations. 8. Do not expose the dashboard externally unless the operator explicitly opts in. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dashboard.js:389
Finding
Account Identities and Operational Logs Disclosed Through the Dashboard API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dashboard.js:389-403` **Vulnerability Type**: Sensitive information exposure **Risk Level**: Medium ### Vulnerable Code ```js } else if (action === 'syncAccounts') { const authData = this.readJson(this.paths.authProfiles); const antigravityAccounts = Object.keys(authData.profiles || {}) .filter(k => k.startsWith('google-antigravity:')) .map(k => k.replace('google-antigravity:', '')); let added = 0; for (const email of antigravityAccounts) { if (!currentConfig.accounts.includes(email)) { currentConfig.accounts.push(email); added++; changed = true; } } if (added > 0) { this.writeJson(this.configPath, currentConfig); this.config = currentConfig; } return { success: true, added, accounts: antigravityAccounts }; } else if (action === 'getDetailedLog') { const log = await this.getDetailedLog(); return { success: true, log }; } ``` The log-reading implementation is: ```js async getDetailedLog() { try { const cronLog = path.join( this.home, '.openclaw/workspace/memory/cron-rotate.log' ); if (fs.existsSync(cronLog)) { const content = fs.readFileSync(cronLog, 'utf8'); const lastPart = content.slice(-20000); const sections = lastPart.split( /=== (余量查询 & 自动轮换|Antigravity Rotator Engine)/ ); if (sections.length > 1) return '=== ' + sections.pop(); return lastPart; } } catch (e) {} return '暂无详细日志数据。'; } ``` ### Technical Analysis The `syncAccounts` operation reads profile keys from the OpenClaw authentication-profile store and returns the resulting account identifiers to the caller. The `getDetailedLog` operation returns up to 20 KB of a local workspace log. Neither operation performs authentication or ...[truncated 1364 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply authentication and authorization to every dashboard endpoint. 2. Do not return the complete `accounts` array from synchronization. Return only a count or redacted identifiers when possible. 3. Mask account names, for example by exposing only a short alias selected by the user. 4. Restrict log retrieval to a dedicated, sanitized application log rather than returning broad command output. 5. Redact tokens, email addresses, filesystem paths, command lines, and other secrets before storing or returning logs. 6. Establish restrictive file permissions on auth-profile, state, and log files. 7. Record access to sensitive administrative endpoints and alert on unusual enumeration behavior. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/rotator.js:202
Finding
Shell Command Injection Risk Through Unsafely Interpolated Configuration Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rotator.js:202-207` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js performRotation(authData, choice, currentEmail, currentModel) { const nextKey = `google-antigravity:${choice.email}`; authData.profiles[this.VIP_KEY] = { ...authData.profiles[nextKey], email: 'vip_rotation_active' }; this.writeJson(this.paths.authProfiles, authData); try { execSync(`${this.openclawBin} models set ${choice.model}`); } catch (e) {} try { const patch = JSON.stringify({ key: 'agent:main:main', model: choice.model }); execSync( `${this.openclawBin} gateway call sessions.patch --params '${patch}'` ); } catch (e) {} } ``` Related warm-up commands also interpolate model and executable values into shell strings: ```js const sId = `warmup-${Date.now()}`; execSync( `${this.openclawBin} gateway call sessions.patch --params ` + `'{"key":"agent:main:${sId}","model":"${m}"}'` ); execSync( `timeout 10 ${this.openclawBin} agent --session-id ${sId} ` + `--message "1" --json 2>/dev/null || true` ); ``` ### Technical Analysis `execSync()` invokes a shell. Values such as `this.openclawBin`, `choice.model`, and `m` are concatenated directly into command strings without shell-safe argument handling. The model-priority list can be replaced through the dashboard's unauthenticated `setPriority` operation, and the code does not enforce a fixed model allowlist. The direct rotation path additionally requires a corresponding status database entry and selection by the scheduling algorithm, so exploitation through the dashboard alone depends on reachable application state. However, a malicious or compromised configuration/state file can reliably introduce shell metacharacters. The executable path is also configuration-controlled. If an attacker can alter `co ...[truncated 1310 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace shell-string execution with `execFileSync()` or `spawn()` and explicit argument arrays: ```js execFileSync(this.openclawBin, ['models', 'set', choice.model]); execFileSync(this.openclawBin, [ 'gateway', 'call', 'sessions.patch', '--params', patch ]); ``` 2. Replace the shell-based `timeout` command with Node.js process timeout handling. 3. Validate every model identifier against a fixed server-side allowlist. Do not trust the list submitted by the dashboard. 4. Resolve and verify `openclawBin` as an absolute path to an expected executable. Reject whitespace, metacharacters, and unexpected files. 5. Validate account names, session IDs, project IDs, and all other values before passing them to child processes. 6. Restrict permissions on `config.json`, status files, and authentication profiles so other local users cannot modify them. 7. Propagate command failures rather than silently suppressing exceptions, and log sanitized error information for investigation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/rotator.js:21
Finding
OAuth Client Secret Embedded in Source and Example Configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rotator.js:21-23` **Vulnerability Type**: Hardcoded reusable credential **Risk Level**: Medium ### Vulnerable Code ```js // OAuth Credentials (from config or hardcoded Antigravity defaults) this.CLIENT_ID = config.clientId || '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com'; this.CLIENT_SECRET = config.clientSecret || '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'; this.DEFAULT_PROJECT_ID = config.defaultProjectId || 'bamboo-precept-lgxtn'; ``` The same credential is shipped in `assets/config.example.json:22-24`: ```json "clientId": "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com", "clientSecret": "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", "defaultProjectId": "bamboo-precept-lgxtn" ``` It is transmitted during token refresh: ```js const postData = new URLSearchParams({ client_id: this.CLIENT_ID, client_secret: this.CLIENT_SECRET, refresh_token: refreshToken, grant_type: 'refresh_token' }).toString(); ``` ### Technical Analysis The project embeds an OAuth client secret directly in source code and in the example configuration copied during initial setup. Anyone with access to the package can recover the value. Source-distributed applications cannot preserve a conventional confidential-client secret because every installation receives the same credential. The value may be intended for a public OAuth client, but it is explicitly used and documented as a client secret. If the OAuth provider treats it as confidential or uses it for client-level trust or quota attribution, disclosure permits third parties to impersonate the client. ### Attack Path 1. An attacker downloads or inspects the publicly distributed Skill. 2. The attacker extracts the client ID and client secret from `rotator.js` or `config.example.json`. 3. The attacker uses the credential in external OAuth token requests or automated abuse attributed to ...[truncated 487 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed OAuth client credential if it is intended to be confidential. 2. Remove the secret from source code, examples, documentation, and repository history. 3. For confidential-client deployments, load credentials from a protected environment variable or operating-system secret store. 4. For a distributed command-line or desktop application, register it as a public OAuth client and use Authorization Code with PKCE rather than relying on a bundled secret. 5. Do not copy secrets into plaintext `config.json`. 6. Apply restrictive file permissions to any configuration that contains OAuth material. 7. Add automated secret scanning to the development and release pipeline. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dashboard.js:130
Finding
Stored HTML and Script Injection in Dashboard Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dashboard.js:130-174` **Vulnerability Type**: Stored cross-site scripting **Risk Level**: Medium ### Vulnerable Code ```js for (const acc of this.config.accounts) { if (!acc || typeof acc !== 'string' || !acc.includes('@')) continue; const isActive = acc === activeAccount; const shortName = acc.split('@')[0]; accountCards += ` <div class="card ${isActive ? 'active-card' : ''}"> <div class="card-header"> <div class="account-name"> ${isActive ? '🟢' : '⚫'} ${shortName} </div> ${isActive ? '<span class="badge">正在使用</span>' : ''} <button class="delete-btn" onclick="confirmRemoveAccount('${acc}')" title="移除监控">×</button> </div> <div class="card-body"> ${modelsHtml || '<div style="color: #666; font-size: 0.8rem;">暂无配额数据</div>'} </div> <div class="card-footer">更新: ${updateTimeStr}</div> </div>`; } let logsHtml = logs.map(l => ` <div class="log-entry" onclick="viewLogDetail()"> <span class="log-time">${l.time || ''}</span> <span class="log-msg">${l.message}</span> <span class="log-arrow">›</span> </div> `).join(''); ``` Detailed log output is also assigned to `innerHTML` without escaping: ```js async function viewLogDetail() { try { const result = await apiCall('getDetailedLog', {}); showModal( '轮换日志详情 - 完整输出', '<pre style="background:#000;padding:15px;border-radius:10px;' + 'font-size:0.7rem;overflow-x:auto;border:1px solid #222;' + 'color:#8f8;line-height:1.4;">' + result.log + '</pre>' ); } catch (e) { showModal('错误', '无法获取日志。'); } } ``` The modal inserts this content using: ```js document.getElementById('modal-body').innerHTML = content; ``` ### Technical Analysis Account identifiers, sho ...[truncated 2048 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Construct dynamic UI elements with DOM APIs and assign untrusted values through `textContent`. 2. Apply context-aware escaping to every value rendered into HTML, attributes, JavaScript strings, or CSS. 3. Remove inline event handlers. Attach listeners with `addEventListener()` and keep account identifiers in validated data properties. 4. Render logs as text: ```js const pre = document.createElement('pre'); pre.textContent = result.log; ``` 5. Strictly validate account identifiers and model names on both client and server. 6. Deploy a restrictive Content Security Policy that disallows inline scripts and event handlers. 7. Add regression tests using payloads containing quotes, closing tags, event handlers, and script-like markup. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior materially understates what the skill does: it reportedly refreshes OAuth tokens, rewrites local auth profiles, makes direct network calls, executes external commands, and mutates live sessions, while also advertising a dashboard that is apparently absent. This mismatch is dangerous because users may authorize or run the skill without understanding that it can alter authentication state, rotate accounts automatically, and affect active sessions behind the scenes.

Ae1

High
Category
analysis-evasion
Content
- **逻辑引擎**: `scripts/rotator.js` (配额查询与账号调度)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **Web UI**: `scripts/dashboard.js` (基于 http 模块的极简服务器)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Context-Inappropriate Capability

High
Confidence
89% confidence
Finding
The dashboard component invokes local shell commands to query backend state via execSync using a command string that includes a configurable binary path. While the current command is not directly built from request input, executing shell commands from a web-facing process increases attack surface and can become command injection or privilege abuse if configuration is tampered with.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The HTTP server listens on 0.0.0.0, exposing the dashboard on all network interfaces instead of limiting it to local access. Because the same service also provides unauthenticated control APIs, this turns a local maintenance tool into a remotely reachable control surface.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises operational automation that involves shell execution, network access, and environment interaction, but it declares no explicit tool scope or permissions. This creates an unsafe trust boundary: users and hosting platforms are not clearly informed that the skill can execute commands, access credentials, and perform external calls, increasing the chance of unintended high-privilege execution.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill promotes 'seamless switching' and hot-updating of active sessions, but does not clearly warn users that background account/model changes can affect ongoing conversations or operational state. This is dangerous because silent switching can alter billing, data handling, model behavior, or reliability expectations mid-session without the operator realizing it.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly says it will 'scan and load' accounts already logged in, but does not present a prominent warning that it will automatically discover and ingest local credentials. Automatic credential scanning is security-sensitive because it can expose, aggregate, or act on multiple accounts without informed consent, especially in shared or multi-profile environments.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This JSON example contains natural-language instructions exclusively in Chinese across multiple comment fields. Per policy, forcing a specific language without offering a user choice or documenting a justified locale constraint is a natural-language policy violation.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The example configuration embeds a Google OAuth client secret directly in the repository. Even if intended as a default/demo value, publishing OAuth credentials enables unauthorized reuse of the OAuth client, phishing-style consent flows, or abuse against the associated Google project, and it is unrelated to a harmless local-only example config. In this skill’s context, which manages multiple accounts and OAuth-based authentication, shipping a real-looking secret is more dangerous because users are encouraged to rely on it for live credential flows.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The manifest describes automated account rotation, quota monitoring, hot session updates, and a dashboard. In this file, the optional setup path imports child_process and runs `which openclaw`, introducing shell execution behavior that is not described as part of the skill's purpose and goes beyond ordinary config/file handling for the stated workflow.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The package description is entirely in Chinese and provides no indication that other languages are supported or that the skill is intentionally limited to a Chinese-speaking audience. Under the stated policy, forcing a specific language without user opt-in or documented justification is a natural-language policy concern.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code persists changes directly to the skill's configuration file via writeFileSync, and API actions such as removeAccount, syncAccounts, and setPriority trigger those writes. While some UI flows confirm account removal, there is no visible disclosure that these actions modify local files and persist state on disk.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The dashboard exposes state-changing management actions over HTTP, including account removal, priority changes, account syncing, and rotation triggering, with no authentication, authorization, or origin protection. In combination with the network listener on 0.0.0.0, any reachable client can remotely alter operational state and control account rotation behavior.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This endpoint invokes a shell command through exec to run a rotation action, which is a safety-relevant operation affecting account state and automation behavior. The frontend button immediately calls the API and only reports success afterward, with no prior disclosure or confirmation prompt.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The manifest describes automated account rotation, quota monitoring, hot session updates, and a dashboard. While interacting with the local OpenClaw tool may be part of session switching, this implementation broadly shells out to system binaries (`which`, `curl`, `openclaw`, `timeout`) rather than limiting itself to direct application logic, introducing command-execution capability not explicitly justified by the stated purpose.

External Transmission

Medium
Category
Data Exfiltration
Content
refresh_token: refreshToken,
            grant_type: 'refresh_token'
        }).toString();
        const cmd = `curl -s --connect-timeout 10 --retry 1 -X POST "${this.REFRESH_TOKEN_URL}" -d "${postData}"`;
        try {
            const output = execSync(cmd, { encoding: 'utf8', timeout: 35000 });
            const json = JSON.parse(output);
Confidence
90% confidence
Finding
The skill transmits OAuth client credentials and refresh tokens to Google's token endpoint by constructing a shell command that embeds sensitive values directly in the command line. Even if the endpoint is legitimate, secrets in process arguments can be exposed via process listings, logs, crash reports, or shell-history-like telemetry, making credential theft possible on shared systems.

External Transmission

Medium
Category
Data Exfiltration
Content
const body = { project: projectId || this.DEFAULT_PROJECT_ID };
        const headerArgs = Object.entries(headers).map(([k, v]) => `-H "${k}: ${v}"`).join(' ');
        const bodyStr = JSON.stringify(body).replace(/"/g, '\\"');
        const cmd = `curl -s --connect-timeout 10 --retry 1 -X POST "${this.QUOTA_API_URL}" ${headerArgs} -d "${bodyStr}"`;
        try {
            const output = execSync(cmd, { encoding: 'utf8', timeout: 35000 });
            if (!output.trim()) throw new Error('Empty response');
Confidence
83% confidence
Finding
The quota-fetch request sends bearer tokens to an external service using curl with the Authorization header embedded in the shell command line. This risks token exposure through local process inspection or logging and expands trust to a nonstandard internal-looking endpoint, which is especially sensitive in a credential-rotation tool.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code overwrites the active auth profile and changes the selected model/session without user confirmation, effectively taking control of the operator's current runtime state. In a multi-account environment, silent credential switching can cause actions to run under the wrong identity and can disrupt ongoing sessions or auditability.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The warmup routine performs active inference runs across multiple accounts/models and rewrites the active VIP auth profile, behavior not disclosed by the stated quota/rotation function. This can consume quota, trigger account activity, and temporarily switch credentials in ways users may not expect, increasing the blast radius if the skill is misconfigured or abused.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The warmup flow repeatedly rewrites auth profiles and launches subprocesses with little visibility or consent, creating hidden state changes and background activity. If interrupted or raced, it may leave the wrong credentials active or consume resources/accounts unexpectedly.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The primary description is written in Chinese and the document continues to present core instructions in Chinese, while not stating that the skill is region-specific or offering a language choice. This can violate language/locale policy when a skill imposes a specific language without user opt-in.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The code formats times using the zh-CN locale, and the generated UI is also explicitly marked as zh-CN elsewhere in the file. Because the skill forces a specific language/locale without opt-in or explanation, it conflicts with the language/locale policy criteria.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The page is explicitly declared as Chinese-language content via the lang attribute, and the surrounding UI strings are written in Chinese. This enforces a specific language experience without presenting a user choice or documenting a justified regional limitation.

Context-Inappropriate Capability

Low
Confidence
91% confidence
Finding
The manifest focuses on account rotation, quota checks, and live model updates. Altering the inherited `PATH` environment for the entire process is a broader execution-environment manipulation capability that is not necessary to express at the intent level and can affect how later commands resolve binaries.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
index.js:39

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/dashboard.js:22

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/rotator.js:33

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/dashboard.js:10

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/rotator.js:24