Back to skill

Security audit

CNKI Watch

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent CNKI lookup purpose, but it needs Review because it includes under-disclosed automated verification bypass, unsafe credential/session handling, and recurring chat injection behavior.

Install only if you are comfortable giving the skill CNKI session credentials and allowing it to create recurring OpenClaw cron jobs that inject CNKI metadata into chat. Review the CAPTCHA-solving code, disabled TLS validation, gateway-token handling, and unpinned npx guidance before using it with real credentials or persistent sessions.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/cnki-watch.mjs:1331
Finding
Untrusted CNKI Metadata Is Injected into the Main Agent Session<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cnki-watch.mjs:1331-1365` **Vulnerability Type**: Remote content injection into an agent session **Risk Level**: High ### Vulnerable Code ```javascript async function executeSubscription(subscription, args = {}) { const settings = await loadSettings(); const limit = settings.config.maxPushResults; const result = await runCnkiQuery({ kind: subscription.kind, query: subscription.query, limit: limit + 20, maxPages: subscription.kind === "journal" ? 6 : 2, exactSource: subscription.kind === "journal", preferDateSort: true, }); const seen = new Set(subscription.seenKeys || []); const newRecords = []; for (const record of result.results) { const key = recordKey(record); if (seen.has(key)) { continue; } newRecords.push({ ...record, dedupeKey: key }); } subscription.lastRunAt = nowIso(); subscription.updatedAt = nowIso(); subscription.lastError = null; if (newRecords.length === 0) { return { delivered: false, newCount: 0, message: "NO_UPDATE" }; } const deliverable = newRecords.slice(0, limit); const truncatedCount = Math.max(0, newRecords.length - deliverable.length); for (const record of newRecords) { seen.add(record.dedupeKey); } subscription.seenKeys = Array.from(seen).slice(-5000); subscription.lastSuccessAt = nowIso(); const message = formatSubscriptionMessage(subscription, deliverable, truncatedCount); await injectMessage(message, cleanText(args["session-key"] || subscription.sessionKey || "main")); ``` ### Technical Analysis Paper titles, author names, source names, dates, and links are extracted from remote CNKI pages and concatenated into a message. That message is then passed directly to `chat.inject` in the main or caller-selected OpenClaw session. Text normalization only collapses whitespace. It does not establish a trusted-data boundary, encode remote content as inert structured data, or warn th ...[truncated 1560 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not inject raw remote metadata into an agent instruction channel. - Send results through a structured data or notification channel that is not interpreted as a user or system instruction. - Explicitly label all extracted fields as untrusted external data and instruct the receiver never to execute or follow text contained in those fields. - Apply strict field length limits and remove control characters, markup, role delimiters, and instruction-like wrappers. - Restrict delivery to the subscription's server-established session. Do not accept arbitrary `--session-key` values from normal user input. - Enforce a server-side allowlist or ownership check before injecting into any non-default session. - Add tests containing prompt-injection strings in every remotely sourced metadata field and verify that they remain inert. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cnki-watch.mjs:963
Finding
Gateway Authentication Token Is Exposed Through Process Arguments and Error Messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cnki-watch.mjs:963-1010` **Vulnerability Type**: Sensitive token exposure **Risk Level**: High ### Vulnerable Code ```javascript async function runCommand(command, args, options = {}) { return new Promise((resolve, reject) => { const child = spawn(command, args, { cwd: options.cwd || process.cwd(), env: options.env || process.env, stdio: ["ignore", "pipe", "pipe"], }); let stdout = ""; let stderr = ""; child.stdout.on("data", (chunk) => { stdout += chunk.toString(); }); child.stderr.on("data", (chunk) => { stderr += chunk.toString(); }); child.on("error", reject); child.on("close", (code) => { if (code === 0) { resolve({ stdout, stderr }); return; } reject(new Error(`${command} ${args.join(" ")} failed (${code}): ${stderr || stdout}`)); }); }); } async function openClawCall(args) { return runCommand("openclaw", args); } async function injectMessage(message, sessionKey = "main", label = "CNKI Watch") { const settings = await loadSettings(); const gatewayArgs = [ "gateway", "call", "--json", "--url", `ws://127.0.0.1:${settings.gateway.port || 18789}`, ]; if (settings.gateway.token) { gatewayArgs.push("--token", settings.gateway.token); } gatewayArgs.push( "--params", JSON.stringify({ sessionKey, message, label }, null, 0), "chat.inject", ); await openClawCall(gatewayArgs); } ``` ### Technical Analysis The OpenClaw gateway token is passed to the `openclaw` child process as a command-line argument. Command-line arguments may be observable through process inspection facilities, monitoring agents, crash reports, or audit logs. The exposure is made more direct by `runCommand`: if the child exits unsuccessfully, the generated exception contains every argument joined into a string, including the value following `--token`. The top-level err ...[truncated 1290 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never place authentication secrets in command-line arguments. - Prefer an authenticated SDK or IPC interface that accepts the token in memory. - If supported by the CLI, pass the token through a protected file descriptor, standard input, or a narrowly scoped environment variable. - Redact arguments associated with `--token`, credentials, cookies, and message payloads before constructing errors. - Replace the error expression with a sanitized command summary and preserve only non-sensitive stderr. - Ensure logs and crash telemetry apply secret filtering. - Rotate any gateway token that may already have appeared in process monitoring or logs. - Add automated tests asserting that known token values never appear in thrown errors or captured output. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cnki-watch.mjs:449
Finding
Browser Disables TLS Certificate Validation for Authenticated CNKI Requests<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cnki-watch.mjs:449-460` **Vulnerability Type**: Improper certificate validation **Risk Level**: Medium ### Vulnerable Code ```javascript const browserEnv = await buildBrowserEnv(process.env); const browser = await chromium.launch({ headless: true, executablePath, args: ["--no-sandbox", "--disable-blink-features=AutomationControlled", "--no-proxy-server"], env: browserEnv, }); const context = await browser.newContext({ ignoreHTTPSErrors: true, locale: "zh-CN", timezoneId: "Asia/Shanghai", userAgent: USER_AGENT, viewport: { width: 1440, height: 960 }, }); ``` ### Technical Analysis Setting `ignoreHTTPSErrors: true` instructs Playwright to continue when HTTPS certificate validation fails. The same browser context receives the user's CNKI cookie or performs username/password login. This eliminates a fundamental authenticity check for encrypted connections. An attacker capable of redirecting or intercepting traffic can present an invalid or attacker-controlled certificate without causing the browser to abort. The `--no-proxy-server` option reduces exposure to configured proxies but does not protect against DNS poisoning, compromised routing, malicious local networks, or host-level interception. ### Attack Path 1. The user configures a CNKI cookie or account credentials. 2. An attacker gains a network-position, DNS, routing, or local-host capability sufficient to redirect CNKI traffic. 3. The attacker presents a certificate that would normally fail validation. 4. Because `ignoreHTTPSErrors` is enabled, the browser continues the connection. 5. Authentication material or sensitive session traffic may be exposed, and forged query results may be returned. 6. Forged metadata can subsequently be injected into the OpenClaw chat by a subscription run. ### Impact Assessment The primary impact is compromise of CNKI credentials or session cookies and loss of integrity of retrieved research ...[truncated 224 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `ignoreHTTPSErrors: true` and use Playwright's secure default certificate validation. - If a controlled development environment requires a custom certificate authority, install that authority explicitly rather than disabling validation. - Fail closed on certificate errors and provide an actionable diagnostic without retrying insecurely. - Use a dedicated browser context for authenticated requests and restrict navigation to expected HTTPS CNKI hostnames. - Add navigation and redirect checks to reject unexpected origins before entering credentials or sending cookies. - Add an integration test confirming that invalid certificates cause the query to terminate. ]]>

other

Warning
Location
scripts/cnki-watch.mjs:660
Finding
Embedded CAPTCHA Solver Circumvents Human-Verification Controls and Contradicts the Skill Contract<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cnki-watch.mjs:660-689` **Vulnerability Type**: Automated CAPTCHA circumvention **Risk Level**: Medium ### Vulnerable Code ```javascript async function maybeSolveCaptcha(page) { for (let attempt = 0; attempt < 5; attempt += 1) { if (!page.url().includes("/verify/home")) { return true; } const gap = await computeCaptchaGap(page); const panel = await page.locator(".verify-img-panel").boundingBox(); const piece = await page.locator(".verify-sub-block").boundingBox(); const block = await page.locator(".verify-move-block").boundingBox(); const initialLeft = piece.x - panel.x; const delta = gap.x - initialLeft; const startX = block.x + block.width / 2; const startY = block.y + block.height / 2; await page.mouse.move(startX, startY); await page.mouse.down(); const steps = [0.12, 0.25, 0.38, 0.52, 0.66, 0.79, 0.9, 0.97, 1].map((value) => Math.round(delta * value), ); for (const step of steps) { await page.mouse.move(startX + step, startY + (Math.random() * 1.6 - 0.8), { steps: 4 }); await page.waitForTimeout(90 + Math.floor(Math.random() * 90)); } await page.mouse.up(); await page.waitForTimeout(2500); if (!page.url().includes("/verify/home")) { return true; } } return false; } ``` ### Technical Analysis The script analyzes CAPTCHA images, estimates the slider gap, and generates randomized mouse movements to imitate a human. This is deliberate circumvention of an access-control and anti-automation mechanism. It also conflicts with the declared behavior in `SKILL.md:28` and `SKILL.md:108`, which says that the skill must stop and request a fresh cookie or manually refreshed session when CAPTCHA or slider verification appears. The implementation instead attempts automated solving up to five times during login, initial navigation, and pagination. This discrepancy prevents operators from relyi ...[truncated 922 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `computeCaptchaGap`, `maybeSolveCaptcha`, and all automatic calls to them. - When a verification path or challenge is detected, immediately stop the operation. - Return a clear error requesting a fresh `CNKI_COOKIE` or a manually refreshed authorized session, as required by the documented contract. - Apply bounded retries only to ordinary transient network failures, never to CAPTCHA challenges. - Add tests verifying that `/verify/home`, MFA, slider, and similar human-verification pages terminate execution without simulated interaction. - Review CNKI terms and obtain explicit authorization for any automated access pattern. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (19)

Ae1

High
Category
analysis-evasion
Content
node scripts/cnki-watch.mjs --help
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
Topic lookups must include the paper title and source. Journal lookups should keep the requested journal name visible in either `source` or `matched_query`.

## Authentication and CNKI access

Credential precedence:
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The script automates CNKI login and actively attempts to solve slider CAPTCHAs by analyzing page images and simulating mouse movement. That is a deliberate bypass of anti-automation controls, which can violate access restrictions and enables unauthorized scripted use of an authenticated session.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares environment-variable usage for CNKI authentication but does not declare an explicit tool scope such as permissions or allowed-tools. That weakens least-privilege controls and makes it harder for reviewers and runtime policy to constrain what the skill can access, especially since it handles sensitive cookies and credentials.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The top-level skill description does not clearly warn that subscriptions will periodically push content into the main OpenClaw chat. That can lead to user surprise, inadvertent data propagation, and consent issues because a one-time setup causes ongoing background delivery into a shared or persistent context.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill instructs users to provide `CNKI_COOKIE` or username/password credentials but does not include a privacy and handling warning for these secrets. Because cookies may represent active sessions and credentials can grant account access, insufficient warning increases the chance of insecure storage, oversharing, or accidental disclosure through logs or chat context.

Behavior Manipulation

Medium
Category
Prompt Injection
Content
## Canonical entrypoint

Always use the bundled script instead of ad hoc CNKI browsing:

```bash
node {baseDir}/scripts/cnki-watch.mjs <command> [flags]
Confidence
70% confidence
Finding
Subtle instructions detected that may alter agent decision-making or introduce hidden biases.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
The documentation tells users to run `npx cnki-watch` without pinning a version, which can fetch and execute whatever package version is current at runtime. This creates a supply-chain execution risk: a compromised, typosquatted, or newly malicious package release could run arbitrary code in the user's environment.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
The documentation instructs users to run `npx cnki-watch` without a pinned version, which can fetch and execute the latest package from the registry at runtime. That creates a supply-chain risk: if the package is compromised, typosquatted, or updated maliciously, arbitrary code could run in the user's environment.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The skill automatically runs `npm install`/`npm ci` with inherited environment variables when `playwright-core` is missing. Installing dependencies at runtime can execute arbitrary lifecycle scripts from the package tree and introduces supply-chain risk unrelated to a simple CNKI lookup workflow.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
This is the same underlying issue as the earlier auto-install finding: the script executes npm automatically without an explicit user warning or approval. Silent package installation expands the trust boundary and can trigger unreviewed install scripts, making the skill dangerous in environments that expect passive data retrieval only.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The browser context is created with a hard-coded `locale: "zh-CN"`, which forces a specific language/locale regardless of user preference. The policy for this audit flags language or locale constraints unless the skill offers choice or clearly documents a justified region-specific limitation in the file.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This code persists subscription state to `runtime/subscriptions.json` via `fs.writeFile`, storing user query subscriptions and run metadata. In this file there is no confirmation prompt or clear disclosure comment/docstring warning that the skill writes persistent local state.

Missing User Warnings

Medium
Confidence
80% confidence
Finding
The skill reads sensitive authentication material such as `CNKI_COOKIE`, `CNKI_USERNAME`, `CNKI_PASSWORD`, and a gateway token from environment or config values. This file lacks a clear inline warning or explanatory comment notifying users that credentials will be consumed for browser login and gateway access.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
maxPages = 1,
  exactSource = false,
  preferDateSort = false,
  strictAuth = false,
}) {
  const settings = await loadSettings();
  const cookie = resolveCredential(settings, "CNKI_COOKIE");
Confidence
75% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Ssd 4

Medium
Confidence
93% confidence
Finding
The cron prompt instructs another agent to run a command exactly once, not ask follow-up questions, and not modify the command. This is prompt-level control designed to suppress safety checks and operator clarification, which becomes risky because it bridges scheduled automation into privileged command execution.

Context-Inappropriate Capability

Medium
Confidence
83% confidence
Finding
Recurring CNKI monitoring can justify scheduling, but this code also directly administers platform cron jobs and includes a manual trigger path for immediate execution. Those host scheduling control capabilities are broader than the manifest's user-facing description of creating subscriptions that periodically push updates into chat.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The defaults hard-code "Asia/Shanghai" and pair it with China-specific schedule handling, which imposes a locale assumption in natural-language-facing behavior. In this file there is no visible user choice, opt-in, or policy justification for forcing that locale as the default.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/cnki-watch.mjs:79

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/cnki-watch.mjs:798