Back to skill

Security audit

Cctv News Fetcher 1.0.0

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it claims, but its crawler can follow unvalidated links from remote pages and should be reviewed before use.

Install only if you are comfortable with the agent running a local Node/Bun crawler that makes outbound web requests. Prefer running it in a network-restricted environment or updating it to allowlist CCTV domains, remove unnecessary Cookie/Host headers, add timeouts and response limits, and strictly validate dates.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/news_crawler.js:9
Finding
Unrestricted Fetching of URLs Extracted from Remote HTML## Vulnerability Details **File Location**: `scripts/news_crawler.js:9-10, 27, 54-57, 72, 99-100, 115` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through unvalidated remote URLs **Risk Level**: Medium ### Vulnerable Code ```js const rawList = text.match(/title_array_01\((.*)/g) || []; const pageUrls = rawList.slice(1).map(item => item.match(/(http.*)/)?.[0].split('\'')[0] || ''); const data = await Promise.all(pageUrls.map(async pageUrl => { try { const pageResponse = await fetch(pageUrl, { headers }); const pageText = await pageResponse.text(); const soup = parse(pageText); const title = soup.querySelector('h3')?.text.replace('[视频]', '').trim() || ''; const content = soup.querySelector('.cnt_bd')?.text.replace(/\n/g, ' ').trim() || ''; return { date, title, content }; } catch (err) { console.error(`Error fetching page ${pageUrl}:`, err.message); return null; } })); ``` The same vulnerable pattern is repeated in the middle and recent news crawlers: ```js const pageUrls = soup.querySelectorAll('#contentELMT1368521805488378 li a') .slice(1) .map(a => a.getAttribute('href') || ''); // ... const pageResponse = await fetch(pageUrl, { headers }); ``` ```js const pageUrls = soup.querySelectorAll('li a').slice(1).map(a => a.getAttribute('href') || ''); // ... const pageResponse = await fetch(pageUrl, { headers }); ``` ### Technical Analysis The crawler first downloads an index page from CCTV and then extracts article URLs from that remotely supplied HTML. It passes each extracted value directly to `fetch()` without validating: - The URL scheme - The destination hostname - The destination port - Whether DNS resolves to a loopback, private, link-local, or reserved address - Redirect destinations This crosses a trust boundary: although the initial index URL is fixed to a CCTV do ...[truncated 2608 chars]
Remediation
## Remediation Suggestions 1. **Apply an explicit destination allowlist** - Parse every extracted link with the standard `URL` class. - Permit only `https:`. - Permit only required CCTV hostnames, such as `tv.cctv.com` and `cctv.cntv.cn`. - Reject embedded credentials, nonstandard ports, malformed URLs, and empty links. 2. **Prevent DNS-based SSRF bypasses** - Resolve the hostname before connecting. - Reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges. - Account for IPv4-mapped IPv6 addresses and alternate IP representations. - Revalidate the destination after every redirect, or disable automatic redirects and process them manually. 3. **Remove unnecessary headers** - Remove the hardcoded `Cookie`, `Host`, `Proxy-Connection`, and `Upgrade-Insecure-Requests` headers. - Do not forward authentication or session headers to URLs obtained from remote pages. - Allow the HTTP client to derive the `Host` header from the validated destination. 4. **Bound network resource usage** - Add an `AbortController` timeout to every request. - Limit the number of article links processed. - Enforce maximum response sizes while streaming bodies. - Restrict redirect counts and concurrent requests. - Verify `response.ok` and validate expected content types before parsing. 5. **Validate the date argument** - Require a strict `YYYYMMDD` value using `/^\d{8}$/`. - Confirm that it represents a real calendar date before constructing the index URL. 6. **Use a centralized safe-fetch helper** - Implement URL validation, DNS/IP checks, redirect handling, timeout enforcement, and size limits in one function. - Use that helper consistently in `fetchOlderNews`, `fetchMidNews`, and `fetchRecentNews`.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill instructs the agent to execute a local JavaScript crawler that fetches remote content, but the manifest declares no explicit tool scope or permissions boundaries. That creates an authorization gap where the skill can invoke code with network access without clear least-privilege constraints, increasing the chance of unintended external requests or abuse if the script is modified or misused.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The code hard-codes the request header `Accept-Language: zh-CN,zh;q=0.9,en;q=0.8`, which forces a specific language/locale preference rather than respecting user choice. This matches the policy category for language or locale constraints because the file provides no opt-in, configurability, or justification for imposing the Chinese locale.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This second header block again fixes `Accept-Language` to `zh-CN,zh;q=0.9,en;q=0.8`, reinforcing a mandatory locale choice in another execution path. Because there is no user opt-in or documented region-specific constraint, it constitutes the same natural-language locale policy issue.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The recent-news path likewise sets `Accept-Language` to Chinese-first values, meaning all crawler variants force the same locale behavior. The file does not expose a language choice to the user or explain why a fixed locale is required, so this is a policy violation under the language/locale rule.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The natural-language description and usage examples constrain the skill to CCTV News Broadcast (Xinwen Lianbo), a Chinese state-media source, without any opt-in or alternative language/locale choice. Under the policy rule, forcing a specific language/locale context without user choice can be a natural-language policy violation unless clearly justified as region-specific.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This markdown file shows the agent will execute a bash command to run a local script, but it does not include any warning about command execution or its potential effects. For markdown files, user-facing descriptions should disclose behaviors that may affect the user's system or data, even when the action appears routine.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The manifest description states the skill will "Fetch news from CCTV," which implies a fixed source associated with a specific locale without offering user opt-in or alternative language/source selection. Under the policy rule for natural-language violations, this can be a locale/source constraint that is not documented as optional or justified.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"description": "Fetch news from CCTV",
    "main": "scripts/news_crawler.js",
    "dependencies": {
        "node-html-parser": "^7.0.2"
    }
}
Confidence
91% confidence
Finding
Using a caret range for node-html-parser allows automatic installation of newer minor/patch releases, which can introduce unexpected behavior or a compromised upstream version through the dependency supply chain. In this skill's context, the risk is limited because there is only one direct dependency and package.json alone shows no postinstall hooks or obviously dangerous packages, but it still reduces build reproducibility and trust.

Static analysis

No suspicious patterns detected.