Back to skill

Security audit

Beijing Signed Price Tracker

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its housing-tracker purpose, but it ships a real Feishu credential and an under-documented monitor that can send local OpenClaw run-log details to Feishu.

Review before installing. Do not use this package as-is with the bundled Feishu configuration: the app secret should be revoked, removed from the artifact, and supplied only through protected configuration. The cron-run monitor should be removed or made explicit opt-in with a user-controlled path, recipient, and redacted payloads. Project URL fetching should be restricted to the intended Beijing Housing Commission host, and users should understand that housing data, cache-derived room details, and run notifications are sent to Feishu.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
projects.json:2
Finding
Live Feishu Application Credential Committed in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `projects.json:2-10` **Vulnerability Type**: Hardcoded application credential and sensitive configuration **Risk Level**: High ### Vulnerable Code ```json "feishu": { "sheetUrl": "https://my.feishu.cn/sheets/Y944sbj2khtLcNtb7jec7MIrnxd", "spreadsheetToken": "Y944sbj2khtLcNtb7jec7MIrnxd", "sheetId": "eee767", "sheetTitle": "Sheet1", "appId": "cli_a941fc5f68399bd2", "appSecret": "nba3Taz4YJ9vJSAKMQ99SdqQuHa5L7xm", "updatedAt": "2026-04-01 13:47:00", "notifyUserOpenId": "ou_41d56543ad3ecf3fad8752c1d98e0030" } ``` The credential is consumed directly by both scripts: ```js async function getTenantAccessToken(appId, appSecret) { if (!appId || !appSecret) throw new Error('缺少飞书 app_id 或 app_secret,请在配置文件、环境变量或命令参数中提供'); const json = await fetchJson(`${FEISHU_BASE_URL}/open-apis/auth/v3/tenant_access_token/internal`, { method: 'POST', body: JSON.stringify({ app_id: appId, app_secret: appSecret }) }); return json.tenant_access_token; } ``` ### Technical Analysis A live Feishu application secret is distributed in a project configuration file. Unlike a spreadsheet token or user identifier, the application secret is an authentication credential. The scripts submit it to Feishu's internal tenant-token endpoint and receive a reusable tenant access token. Anyone who can read the project archive, source repository, backup, build artifact, or deployment directory can recover the credential. The repository also identifies the corresponding application ID, spreadsheet, worksheet, and notification recipient, substantially reducing the information an attacker must discover independently. Although the network transmission itself uses the legitimate HTTPS Feishu endpoint and is required for the declared synchronization feature, storing the source credential in plaintext is unnecessary. Environment-variable support already exists in `scripts/tracker.js:561-568`, so committing the credential e ...[truncated 1524 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed Feishu application secret immediately. Treat it as compromised even if no abuse has been observed. 2. Remove the secret from the current project and all source-control history, release archives, backups, logs, and cached build artifacts. 3. Replace `projects.json` in distributed packages with a sanitized example containing placeholders only. 4. Require `FEISHU_APP_SECRET` from a protected environment variable or retrieve it from an operating-system or cloud secret manager. 5. Do not accept secrets through ordinary command-line arguments where they can appear in shell history and process listings. 6. Apply restrictive filesystem permissions to any runtime-only configuration containing credentials. 7. Audit the Feishu application's recent token issuance and API activity for unauthorized use. 8. Restrict the application to the minimum spreadsheet and messaging scopes needed by the Skill. 9. Limit document authorization to a dedicated application-owned spreadsheet rather than granting broad tenant document access. 10. Add automated secret scanning to source-control and release pipelines to prevent recurrence. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/monitor-tracker-runs.js:9
Finding
Undocumented Collection and External Transmission of OpenClaw Cron-Run Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor-tracker-runs.js:9-10, 43-49, 95-106, 108-130` **Vulnerability Type**: Undeclared access to private runtime logs and transmission of operational data **Risk Level**: High ### Vulnerable Code The script contains a user-specific absolute path and fixed job identifier: ```js const TARGET_JOB_ID = '407511f7-5f9f-4a1e-aee9-c2e0764fb5e4'; const TARGET_JOB_NAME = 'beijing-signed-price-tracker-hourly'; const RUNS_PATH = '/home/SENSETIME/zhangjiazhao/.openclaw/cron/runs/407511f7-5f9f-4a1e-aee9-c2e0764fb5e4.jsonl'; const TZ = 'Asia/Shanghai'; ``` It reads and parses the entire referenced cron-run history: ```js function readJsonLines(filePath) { if (!fs.existsSync(filePath)) return []; return fs.readFileSync(filePath, 'utf8') .split(/\r?\n/) .map(line => line.trim()) .filter(Boolean) .map(line => JSON.parse(line)); } ``` The latest run's error or summary is placed into a message: ```js function buildStatusText(_today, latestRun) { const time = formatShanghaiDateTime(Number(latestRun?.runAtMs || latestRun?.ts || 0)); const status = String(latestRun?.status || 'unknown'); const detail = String(latestRun?.error || latestRun?.summary || '无详细信息'); const title = status.toLowerCase() === 'ok' ? '北京签约跟踪最新状态' : '北京签约跟踪执行告警'; return [ title, `最近一次执行时间:${time}`, `状态:${status}`, `详情:${detail}` ].join('\n'); } ``` The resulting content is transmitted to a configured Feishu recipient: ```js async function main() { const config = readJson(CONFIG_PATH); const feishu = config?.feishu || {}; if (!feishu.appId || !feishu.appSecret || !feishu.notifyUserOpenId) { throw new Error('projects.json 缺少飞书告警所需配置(appId/appSecret/notifyUserOpenId)'); } const today = getTodayShanghai(); const entries = readJsonLines(RUNS_PATH); const latestRun = getLatestFinishedRun(entries); if (!latestRun) { console.log(JSON.stringify({ ok: true, notified: f ...[truncated 3139 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `monitor-tracker-runs.js` from the Skill if cron monitoring is not an explicitly supported feature. 2. If retained, document the monitoring behavior, exact data categories read, destination, retention implications, and required permissions in `SKILL.md`. 3. Require explicit opt-in configuration for the run-log path, job ID, and Feishu recipient. Do not ship user-specific absolute paths or recipient identifiers. 4. Verify that the configured path belongs to the invoking user and remains within an approved OpenClaw run directory. 5. Replace raw `error` and `summary` forwarding with a minimal allowlisted status object, such as timestamp, success/failure, and a controlled error code. 6. Redact credentials, tokens, URLs containing query secrets, local paths, command lines, and other sensitive patterns before transmission. 7. Limit message size and reject unexpected structured or multiline content. 8. Require a separate, least-privileged Feishu credential for monitoring rather than reusing the spreadsheet application's credential. 9. Add a dry-run mode that displays exactly what will be transmitted and require operator confirmation during initial setup. 10. Apply restrictive permissions to run-history files and audit which service accounts can read them. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/tracker.js:247
Finding
Unrestricted Project URL Fetching Permits Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tracker.js:247-292, 940-951, 1018-1024` **Vulnerability Type**: Server-side request forgery and insecure cleartext transport **Risk Level**: Medium ### Vulnerable Code The default government endpoint uses cleartext HTTP: ```js const BASE_URL = 'http://bjjs.zjw.beijing.gov.cn'; const FEISHU_BASE_URL = 'https://open.feishu.cn'; ``` The generic fetch routine accepts arbitrary URLs and follows redirects: ```js async function fetchText(url, options = {}, retries = 3) { let lastError; for (let attempt = 1; attempt <= retries; attempt += 1) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 25000); try { const response = await fetch(url, { redirect: 'follow', signal: controller.signal, ...options, headers: { 'user-agent': 'Mozilla/5.0 (OpenClaw Skill Tracker)', ...(options.headers || {}) } }); const text = await response.text(); if (!response.ok) throw new Error(`HTTP ${response.status}`); clearTimeout(timeout); return text; } catch (error) { clearTimeout(timeout); lastError = error; if (attempt < retries) await new Promise(resolve => setTimeout(resolve, attempt * 1500)); } } throw lastError; } ``` Project URLs are stored without protocol, host, port, or path validation: ```js if (command === 'add') { if (!options.name || !options.url) throw new Error('add 命令需要同时提供 --name 和 --url'); const project = upsertProject(config, options.name, options.url); saveConfig(configPath, config); console.log(`已保存项目映射: ${project.name} (${project.urls.length} 个链接)`); console.log(`配置文件: ${configPath}`); return; } ``` A temporary URL can also be supplied directly to synchronization: ```js let projectsToSync = []; if (options.name && options.url) projectsToSync = [{ name: options.name, url: options.url }]; else if (options ...[truncated 3565 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https:` URLs. 2. Enforce an exact hostname allowlist, preferably only `bjjs.zjw.beijing.gov.cn`, before every request. 3. Validate project URLs when they are added, loaded from configuration, and supplied through temporary command options. 4. Validate every extracted building and room URL independently rather than trusting it because it came from an allowed page. 5. Disable automatic redirects or inspect and validate every redirect target before following it. 6. Reject URLs containing embedded credentials, nonstandard ports, fragments, unexpected paths, or malformed encodings. 7. Resolve hostnames and reject loopback, unspecified, link-local, multicast, carrier-grade NAT, and private IPv4/IPv6 ranges. Repeat this check after redirects to mitigate DNS rebinding. 8. Enforce response-size limits, redirect limits, content-type expectations, and a maximum number of building and room requests. 9. Use outbound firewall or sandbox rules so the Skill process cannot reach local services, private networks, or cloud metadata endpoints. 10. If the government service does not support HTTPS, document that limitation, isolate the crawler, pin the expected destination network where practical, and treat all returned content as untrusted. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
A severe description-behavior mismatch is a strong trust violation: the skill claims to scrape housing data and update a spreadsheet, but the analysis indicates different behavior centered on local cron-run data and Feishu notifications. Such mismatches can conceal unauthorized data access or exfiltration because users approve one purpose while the skill executes another.

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
使用 `scripts/tracker.js` 维护多个地块名到北京住建委项目详情链接的映射,并把新发现的“已签约 / 网上联机备案”房屋写入**飞书表格**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises behavior that requires network access and use of secrets from environment/config, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, missing scope declarations can cause overbroad execution or user surprise about external access and secret usage, especially since the skill performs outbound requests and messaging.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill explicitly persists data locally, writes to a remote Feishu spreadsheet, and sends automatic DMs, but it does not clearly warn users that project data will be stored and transmitted off-system. This creates privacy, compliance, and operational risks because users may invoke the skill without understanding retention and disclosure effects.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script automatically sends the latest run status and detail text to a Feishu user, and that detail is derived from `latestRun.error` or `latestRun.summary` without any filtering or minimization. If those fields contain sensitive operational data, stack traces, internal paths, project identifiers, or other unintended content from the run log, the script will exfiltrate that information to an external messaging channel as part of normal operation.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
Most of the natural-language documentation and operational instructions are presented only in Chinese, with no indication that users may choose another language or that the locale restriction is intentional and justified. Under the language/locale policy, forcing a specific language without user opt-in is a policy concern.

Vague Triggers

Low
Confidence
78% confidence
Finding
This file is a JSON file, so vague-trigger checks apply. The content is purely operational state and does not document any activation phrases, scope limits, or exclusion conditions, so if this file is being used as a manifest/config for the skill, trigger specificity is missing.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The project names and URLs indicate this skill is configured specifically for Chinese-language, Beijing-region project data, but the file provides no natural-language indication that this locale constraint is optional or user-selected. Under the policy, forcing a specific language or locale without opt-in can be a natural-language policy concern when the configuration implicitly restricts operation to one locale.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/tracker.js:561