Back to skill

Security audit

Ptt BBS

Security checks for vulnerabilities and agentic risk

Overview

This PTT scraper mostly matches its purpose, but its article command can fetch any web URL, which could reach unintended internal or local services.

Review before installing. Use this only in an environment with limited network access, pass only ptt.cc article URLs or relative /bbs paths, and avoid letting untrusted text choose article URLs. For long threads, ask for summaries or bounded comment ranges rather than all comments.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ptt_scraper.py:293
Finding

Arbitrary URL Fetching Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/ptt_scraper.py:69-72, scripts/ptt_scraper.py:293-296, and scripts/ptt_scraper.py:367-368
Vulnerability Type: Server-Side Request Forgery (SSRF) through an unrestricted user-controlled URL
Risk Level: High

Vulnerable code:

python
def fetch_soup(session: requests.Session, url: str, params: dict | None = None) -> BeautifulSoup:
    resp = session.get(url, params=params, timeout=15)
    resp.raise_for_status()
    return BeautifulSoup(resp.text, "html.parser")
python
def get_article(session: requests.Session, url_or_path: str) -> dict:
    url = url_or_path if url_or_path.startswith("http") else BASE_URL + url_or_path
    soup = fetch_soup(session, url)
    return parse_article(soup, url)
python
p_article = sub.add_parser("article", help="fetch one article with push/boo counts")
p_article.add_argument("url", help="full URL or /bbs/... path")
python
elif args.command == "article":
    result = get_article(session, args.url)

Technical Analysis

The article command accepts an arbitrary URL from the command line. In get_article(), any input beginning with http is passed to fetch_soup() without validating its scheme, hostname, port, resolved IP address, or path. fetch_soup() then performs the request with requests.Session.get().

Consequently, the request is not restricted to the Skill's declared destination, https://www.ptt.cc. An attacker who can control or influence the article argument can direct the process toward:

  • Loopback services such as 127.0.0.1 or [::1]
  • Private network services
  • Link-local addresses and cloud instance metadata services
  • Services on unusual ports
  • Attacker-controlled hosts that redirect to internal destinations

The requests library follows redirects by default. Therefore, checking only the initial URL would also be insufficient unless every redi ...[truncated 1825 chars]

Remediation
View remediation

Remediation Suggestions

  • Prefer accepting only relative PTT article paths matching a strict pattern such as /bbs/<board>/M.<identifier>.html.
  • If full URLs must be supported, parse them with urllib.parse.urlsplit() and require:
    • Scheme exactly equal to https
    • Hostname exactly equal to www.ptt.cc
    • No embedded username or password
    • No non-default port
    • A path matching an expected PTT article path
  • Disable automatic redirects with allow_redirects=False, or validate the destination of every redirect before following it.
  • Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses for both IPv4 and IPv6.
  • Do not rely solely on textual hostname checks, because DNS rebinding and alternate IP representations can bypass weak validation.
  • Keep network egress restricted at the container or host level so the process cannot access metadata endpoints or internal administrative networks.
  • Add tests covering localhost, IPv6 loopback, private addresses, link-local addresses, unusual ports, embedded credentials, malformed schemes, and redirect chains.

T08 · Insecure Dependencies

Note
Location
SKILL.md:43
Finding

Runtime Dependencies Are Installed Without Version or Integrity Pinning

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:43-47
Vulnerability Type: Unpinned third-party dependencies and non-reproducible installation
Risk Level: Low

Vulnerable documentation:

bash
pip install requests beautifulsoup4
# or without installing:  uv run --with requests --with beautifulsoup4 python scripts/ptt_scraper.py ...

Technical Analysis

The documented installation commands retrieve requests and beautifulsoup4 without specifying reviewed versions or cryptographic hashes. Each installation may consequently resolve to different package versions and transitive dependency sets.

No evidence was found that these package names are typographical imitations or that the currently intended packages are malicious. The security concern is that future releases, compromised package distribution infrastructure, or unexpected dependency-resolution changes would be incorporated without a reproducible review boundary.

Attack Path

  1. A user follows the Quick Start instructions.
  2. The package manager resolves the latest versions available from its configured package index.
  3. A compromised, malicious, or unexpectedly incompatible future release is selected because no version constraint or hash is enforced.
  4. Package installation hooks or imported dependency code execute with the privileges of the user running the installation or scraper.

This path depends on a supply-chain compromise or unsafe package-index configuration; the audited project does not itself demonstrate that either dependency is currently malicious.

Impact Assessment

Dependency code runs with the same operating-system privileges as the installer or scraper. In a successful supply-chain attack, this could expose files, environment variables, network credentials, and data accessible to that account, or permit arbitrary code execution within that account's security context.

The practical risk is reduced because the do ...[truncated 142 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin direct and transitive dependencies to reviewed versions using a lock file.
  • Use hash verification, such as a requirements file generated with hashes and installed using pip install --require-hashes.
  • Document the expected package index and avoid untrusted additional indexes.
  • Use an automated dependency scanner and a controlled update process.
  • Regenerate pins periodically after reviewing vulnerability advisories and release changes.
  • Install dependencies in an isolated virtual environment or container with minimal privileges.
Vulnerability Patterns
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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 (2)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill's trigger guidance is intentionally broad enough that routine mentions of PTT, boards, or vote-count terminology could invoke scraping behavior without a clear user request to crawl or retrieve site content. In an agent setting, this increases the chance of unnecessary external access, unexpected data collection, and overbroad handling of user queries beyond least-privilege tool use.

Content

No source excerpt is available for this finding.

Unbounded Output

Medium
Category
Output Handling
Confidence
86% confidence
Finding

The skill directs the agent to reproduce scraped content verbatim with 'no trimming' and to offer the remainder of comments on request, which can lead to very large outputs and bulk reproduction of third-party content. This creates risk of denial-of-service through oversized responses, accidental disclosure of personal data in comments, and policy/compliance issues from excessive verbatim reproduction.

Content

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

md
- **PTT-native data is shown raw.** Article metadata (board, title, author,
  time, url, score line), the poster's own fields (原文連結 / 發布時間 /
  心得評論), and push comments are reproduced verbatim as scraped — no
  paraphrase, no trimming, no added commentary. Summarize only on request.
- **Push comments default to the first 50**, in original order, each with its
  推/噓/→ tag, userid, content and timestamp. Offer the rest; show all or a
  specific range (e.g. 51–174) when asked.