Back to skill

Security audit

goldskill

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent commodity-market analysis skill, but it relies on third-party data feeds and has some install and robustness risks users should understand.

Install only if you are comfortable with the skill contacting Yahoo Finance, Stooq, metals.live, Google News, and multiple RSS providers for market data. Prefer running it in a virtual environment, pinning or removing unused dependencies, and treating returned headlines as untrusted news text rather than instructions.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T01 · Skill Instruction Hijacking

Error
Location
agent.py:399
Finding
Untrusted RSS Headlines Can Influence Downstream Agent Instructions<![CDATA[ ## Vulnerability Details **File Location**: `agent.py:383, 399-417, 488-501, 513-529` **Vulnerability Type**: Indirect prompt injection through remotely controlled content **Risk Level**: High ### Vulnerable Code ```python with urlopen(req, timeout=8) as r: raw_bytes = r.read() ``` ```python results = [] for item in items[:10]: title = _get_text(item, "title") if not title: continue link_el = item.find("link") link = (link_el.text or "").strip() if link_el is not None else "" desc = _get_text(item, "description", "summary") pub_ts = parse_date(_get_text(item, "pubDate", "published")) agency = source["agency"] if source.get("extract_source"): agency, title = _extract_source(title, agency) text = title + " " + desc sent, score = sentiment_score(text) syms, rel_scores = rel_symbol(text, source.get("symbols_hint", [])) results.append({ "title": title[:200], "link": link, "agency": agency, "pub_ts": pub_ts, "pub_time": datetime.fromtimestamp(pub_ts).strftime("%Y-%m-%d %H:%M"), "sentiment": sent, "score": score, "symbols": syms, "rel_scores": rel_scores, }) ``` ```python for n in shown: shown_titles.add(n["title"][:60].lower()) icon = SENT_ICON[n["sentiment"]] label = SENT_LABEL[n["sentiment"]] star = "★ " if n["agency"] in PRIORITY_AGENCIES else " " lines.append(f" {star}{icon} [{label}] {n['title'][:80]}") lines.append(f" 来源: {n['agency']} 时间: {n['pub_time']}") if n.get("link"): lines.append(f" 链接: {n['link'][:100]}") ``` ```python for n in shown: icon = SENT_ICON[n["sentiment"]] label = SENT_LABEL[n["sentiment"]] star = "★ " if n["agency"] in PRIORITY_AGENCIES else " " syms_str = " / ".join(n.get("symbols", [])) lines.append(f" {star}{icon} [{label}] {n['title'][:80]}") lines.append(f" 来源: {n['agency']} 时间: {n['pub_time']} 品种: {syms_str}") if n ...[truncated 2472 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Return news as structured data with explicit fields such as `untrusted_title`, `source`, and `link`, rather than mixing it into instructional prose. 2. Add a trusted instruction outside the remote data stating that feed content is untrusted and must never be followed as commands. 3. Place remote fields inside strong delimiters and clearly identify the beginning and end of each untrusted value. 4. Strip control characters, bidirectional text controls, and other characters that can obscure data boundaries. 5. Apply prompt-injection detection as defense in depth, while not relying on keyword filtering as the primary control. 6. Prevent remote content from triggering tools or privileged operations without an independent trusted decision and explicit authorization. 7. Preserve source provenance so downstream consumers can distinguish skill-authored text from third-party content. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
agent.py:132
Finding
Unbounded HTTP Response Reads Permit Memory and CPU Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `agent.py:132-137, 379-394` **Vulnerability Type**: Unbounded network response buffering and XML parsing **Risk Level**: Medium ### Vulnerable Code ```python def http_get(url, timeout=8, headers=None): hdrs = {"User-Agent": "Mozilla/5.0 GoldSkill/3.2"} if headers: hdrs.update(headers) req = Request(url, headers=hdrs) with urlopen(req, timeout=timeout) as r: return r.read().decode("utf-8", errors="replace") ``` ```python for ua in uas: try: req = Request(source["url"], headers={"User-Agent": ua, "Accept": "application/rss+xml,*/*"}) with urlopen(req, timeout=8) as r: raw_bytes = r.read() break except: time.sleep(0.3) if not raw_bytes: return [] raw = _sanitize_xml(raw_bytes, source.get("encoding")) try: root = ET.fromstring(raw.encode("utf-8")) except ET.ParseError: try: root = ET.fromstring(raw[:raw.rfind(">")+1].encode("utf-8")) except: return [] ``` ### Technical Analysis The HTTP timeout limits how long an operation may block, but it does not limit the number of bytes accepted. Both the general HTTP helper and RSS fetcher call `read()` without a maximum size, causing the entire remote response to be buffered in memory. RSS documents are fetched concurrently from numerous sources. A large response can therefore coexist with other large responses. RSS processing also creates additional in-memory representations during byte decoding, XML sanitization, UTF-8 encoding, and construction of the XML tree. This can amplify memory use and consume significant CPU. Although all configured endpoints are fixed HTTPS URLs, exploitation remains possible if an upstream service is compromised, a redirect reaches an attacker-controlled resource, or a provider returns an unexpectedly large document. The XML parser is not used to execute code, but parsing a very large tree can still exhaust resources. ### Attack Path 1 ...[truncated 1085 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Implement a bounded streaming reader and stop after a conservative maximum response size. 2. Validate `Content-Length` when present, while still enforcing a runtime byte limit because that header can be absent or false. 3. Read responses in fixed-size chunks and reject the response as soon as the cumulative size exceeds the limit. 4. Apply separate limits appropriate to price JSON, CSV, and RSS documents. 5. Disable redirects or validate every redirect destination against an explicit hostname and scheme allowlist. 6. Limit concurrent downloads and set process-level memory and CPU restrictions. 7. Reject oversized XML documents before decoding or parsing. 8. Use a hardened parser such as `defusedxml`, and enforce limits on element count, nesting depth, text length, and item count. 9. Avoid unnecessary full-document copies during decoding, sanitization, and re-encoding. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unpinned and Unused Runtime Dependencies Increase Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-2; SKILL.md:6` **Vulnerability Type**: Unbounded third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code ```text yfinance>=0.2.36 requests>=2.28.0 ``` The skill installation configuration executes the requirements file: ```yaml install: command: pip3 install -r requirements.txt ``` ### Technical Analysis The requirements file specifies minimum versions without upper bounds, exact pins, or package hashes. A future installation may therefore resolve materially different releases and transitive dependency sets from those reviewed during this audit. Python package installation can execute package build logic with the privileges of the user performing installation. If a package account, release, distribution artifact, or transitive dependency is compromised, the installation process can introduce malicious code. Non-malicious future releases can also cause incompatible or unexpected behavior. Neither `yfinance` nor `requests` is imported by `agent.py`; the implementation uses standard-library networking through `urllib`. These packages therefore increase installation and supply-chain exposure without supporting the audited runtime behavior. There is no evidence in the reviewed project that either named package is currently malicious. The finding concerns unnecessary and non-reproducible dependency resolution. ### Attack Path 1. An operator installs the skill using the command declared in `SKILL.md`. 2. `pip` resolves any available `yfinance` version at or above `0.2.36` and any `requests` version at or above `2.28.0`, together with their transitive dependencies. 3. A compromised or unexpectedly changed future release is selected because no exact version or artifact hash is enforced. 4. Package build or installation code executes with the installing user's privileges. 5. Malicious package code could alter files or access data available to that user; alternativel ...[truncated 628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `yfinance` and `requests` because the audited implementation does not import them. 2. If they become necessary, pin exact reviewed versions rather than using minimum-only constraints. 3. Generate and enforce cryptographic hashes for every direct and transitive artifact. 4. Use a lock file or a hash-locked requirements file to make installations reproducible. 5. Install dependencies in a dedicated virtual environment under an unprivileged account. 6. Restrict installation to an approved package index and monitor dependencies for advisories and ownership changes. 7. Re-audit and update locked dependencies through a controlled process rather than allowing automatic resolution to arbitrary future releases. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Vague Triggers

Medium
Confidence
87% confidence
Finding
The trigger includes the generic term "期货" alongside broad category terms like "大宗商品" and "commodity", which can appear in many everyday finance or news queries not specifically requesting this skill. The file does not provide exclusion conditions or narrower context boundaries, increasing the risk of unintended invocation.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Natural-language strings throughout the file, including the module description and generated output, are presented only in Chinese with no user language selection or opt-in. This can violate language/locale policy because the skill imposes a specific locale rather than offering a choice or documenting a justified regional scope.

Whitespace Padding

Medium
Category
Prompt Injection
Content
{"name": "FXStreet 大宗商品",          "url": "https://www.fxstreet.com/rss/news/commodity",                                                                         "agency": "FXStreet",    "country": "ES", "symbols_hint": ["XAUUSD","XAGUSD","CRUDEOIL"]},
    {"name": "Rigzone 石油天然气",         "url": "https://www.rigzone.com/news/rss/rigzone_latest.aspx",                                                                "agency": "Rigzone",     "country": "US", "symbols_hint": ["CRUDEOIL","NATGAS"]},
    {"name": "MarketWatch 市场",           "url": "https://feeds.marketwatch.com/marketwatch/marketpulse/",                                                              "agency": "MarketWatch", "country": "US", "symbols_hint": ["XAUUSD","XAGUSD","CRUDEOIL","NATGAS","COPPER"]},
    {"name": "MetalMiner 金属",            "url": "https://agmetalminer.com/feed/",                                                                                      "agency": "MetalMiner",  "country": "US", "symbols_hint": ["COPPER","XAGUSD"]},
]

NEWS_MAX_AGE = 7 * 24 * 3600
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill performs numerous outbound requests to third-party market-data and news providers without explicit user disclosure or a consent/control mechanism. In an agent setting, this can leak usage patterns, IP/address metadata, timestamps, and potentially user-driven query intent to external services, increasing privacy and supply-chain exposure.

External Transmission

Medium
Category
Data Exfiltration
Content
def fetch_price_metals_live(cfg):
    if cfg["yf"] not in ("GC=F", "SI=F"):
        raise ValueError("metals.live: only gold/silver")
    data  = json.loads(http_get("https://api.metals.live/v1/spot", timeout=8))
    key   = "gold" if cfg["yf"] == "GC=F" else "silver"
    price = data.get(key)
    if price is None:
Confidence
83% confidence
Finding
This function transmits requests to an external pricing API, introducing dependency on an unaffiliated remote service for runtime behavior and data integrity. In this skill's context, the request does not appear to include secrets, but it still exposes environment metadata and allows a third-party service to influence financial analysis output if the upstream data is wrong, manipulated, or unavailable.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The trigger phrase "天然气" is very broad and can easily appear in ordinary user conversation about commodities, news, or general discussion. In an agent skill, such an ambiguous trigger can cause unintended invocation of the skill, leading to unexpected actions, confusing responses, or accidental use of external data sources when the user did not explicitly request the skill.

Vague Triggers

Medium
Confidence
86% confidence
Finding
The trigger phrase "铜价" is also broad and likely to collide with normal market commentary or casual discussion about copper prices. Because this skill performs analysis functions, accidental triggering could cause the agent to route benign conversation into the skill workflow without a deliberate user request.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The natural-language description is presented exclusively in Chinese, and the file does not indicate that users may choose another language or locale. This can violate a language-choice policy where skills should not force a specific language without user opt-in.

Intent-Code Divergence

Low
Confidence
88% confidence
Finding
The top-level docstring names the skill 'GoldSkill' and describes it as an 'international futures quantitative analysis system', which implies a gold-focused or at least more narrowly scoped tool. In practice, the code explicitly supports gold, silver, crude oil, natural gas, and copper, and also fetches general commodity news across many sources.

Unpinned Dependencies

Low
Category
Supply Chain
Content
yfinance>=0.2.36
requests>=2.28.0
Confidence
92% confidence
Finding
The dependency is specified with a lower-bound version only, which permits installation of any newer release and prevents reproducible builds. This increases supply-chain risk because a future breaking or compromised release could be pulled into the environment without review.

Unpinned Dependencies

Low
Category
Supply Chain
Content
yfinance>=0.2.36
requests>=2.28.0
Confidence
97% confidence
Finding
The requests package is also unpinned, so installations are not deterministic and may resolve to different versions over time. Because requests has a significant history of security advisories, leaving it unpinned raises the chance of deploying a vulnerable or otherwise unreviewed release.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding
The manifest includes requests without an exact version, while multiple known advisories exist for that package. Because the resolved installed version cannot be verified from this manifest, there is a real risk that deployments may pick an affected release, especially across different environments or rebuilds.

Static analysis

No suspicious patterns detected.