Back to skill

Security audit

财报引文逐字核对

Security checks for vulnerabilities and agentic risk

Overview

The filing workflow fits its purpose, but unchecked file paths can overwrite unrelated files and redirects can expose an API key.

Use a restricted workspace without write access to unrelated files until source IDs are validated and output paths are contained. Credential-bearing requests should reject redirects or revalidate every destination before forwarding authorization. Optional Cue and model services receive filing URLs or excerpts; approve paid parsing and review repaired quotes against their surrounding text.

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/cue.py
Finding

Externally supplied source IDs escape the data directory

Content
View full analysis

Impact

For example, sid ../../outside causes write_source to create or truncate ../outside.jsonl and ../outside.txt relative to the selected data directory. Downloaded filing text supplies the resulting document content. Writes remain limited by filesystem permissions and the appended extensions, but existing files outside the authorized output directory can be overwritten.

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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (37)

Tainted flow: 'url' from os.environ.get (line 531, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/cue.py (reported line 67)May include surrounding context.

python
def get(url, headers=None, data=None, tries=4):
    for a in range(tries):
        try:
            with urllib.request.urlopen(urllib.request.Request(url, data=data, headers=headers or {'User-Agent': 'Mozilla/5.0'}), timeout=90) as r: return r.read()
        except urllib.error.HTTPError as e:
            if a == tries - 1 or (400 <= e.code < 500 and e.code != 429): raise   # 4xx (except rate limit) will not fix itself on retry
            time.sleep(2 * (a + 1))

Tainted flow: 'req' from os.environ.get (line 188, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/cue_connectivity_check.py (reported line 193)May include surrounding context.

python
code = None
    raw = ""
    try:
        with urllib.request.urlopen(req, timeout=args.timeout) as resp:
            code, raw = resp.status, resp.read().decode("utf-8", "replace")
    except urllib.error.HTTPError as err:
        detail = ""

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.en.md (reported line 44)May include surrounding context.

bash
   CATALOG="https://cuecue.cn/api/mcp-catalog"
   curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<replace with one of the ids listed by the line above>"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.en.md (reported line 48)May include surrounding context.

md
rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<replace with one of the ids listed by the line above>"
   ROUTING_URL="$(curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("Not found: that id is not in the catalog response (or its routing has no url field) - run the line above and pick one of the ids listed")' "$DOMAIN_ID")"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.md (reported line 44)May include surrounding context.

bash
   CATALOG="https://cuecue.cn/api/mcp-catalog"
   curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<把这里换成上一行列出的那个域 id>"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.md (reported line 48)May include surrounding context.

md
rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<把这里换成上一行列出的那个域 id>"
   ROUTING_URL="$(curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("未取到:该 id 不在目录现值里(或它没有 routing 里的 url 字段)——先跑上面那行列出现场的 id 再填")' "$DOMAIN_ID")"

Incomplete referenced artifact analysis

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**中文主入口(本文件);英文对应版为包内 `SKILL.en.md`,仓库内查阅。渠道页不解析相对链接,故不给可点切换。**

Incomplete referenced artifact analysis

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
脚本:本文件同目录下的 `scripts/cue.py`(Python 3.9+ 标准库)。下文 `CUE` 指它的完整路径,`DIR` 指这家公司的数据目录。

Incomplete referenced artifact analysis

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
官方技能的调用契约以各自 `SKILL.md` 为准(那三枚技能面属包外件,不随本发布物交付):omni 首调永远 `parse`,按实时 schema 调用;data-MCP 先匿名 `GET https://cuecue.cn/api/mcp-catalog` 拿 live 域与 `routing`,再按 `too

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/test_skill_regression.py (reported line 96)May include surrounding context.

python
FX = _HERE / 'fixtures' / 'omni_real_shape.json'
    def _fx(self): return rj(self.FX)
    def _ingest(self, d, path, **env):
        e = dict(os.environ, **env)
        return subprocess.run([sys.executable, str(CUE), 'ingest', d, '--omni', path, '--sid', 'ANN-1', '--kind', 'announcement', '--date', '2026-10-01',
                               '--title', 't', '--company', 'TestCN', '--market', 'CN'], capture_output=True, text=True, env=e)
    def _bridge_env(self, fx_path, log):

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/test_skill_regression.py (reported line 134)May include surrounding context.

python
d = os.path.join(self.d, 'cn'); os.makedirs(d)
        wj({'company': 'TestCN', 'id': '000000', 'market': 'CN', 'sources': [{'sid': 'ANN-1', 'kind': 'announcement', 'date': '2026-10-01', 'title': 't',
                   'source_url': 'https://example.com/a.pdf', 'parser': 'pending', 'n_pages': 0}]}, f'{d}/sources.json')
        log = os.path.join(self.d, 'bridge.log'); env = dict(os.environ, **self._bridge_env(self.FX, log))
        r = subprocess.run([sys.executable, str(CUE), 'omni', d], capture_output=True, text=True, env=env)
        self.assertEqual(r.returncode, 3); self.assertIn('--yes', r.stdout); self.assertFalse(os.path.exists(log))
        r = subprocess.run([sys.executable, str(CUE), 'omni', d, '--yes'], capture_output=True, text=True, env=env); self.assertEqual(r.returncode, 0, r.stderr)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/test_skill_regression.py (reported line 176)May include surrounding context.

python
self.assertEqual(cue.pagespec('1,3-4'), [1, 3, 4])
        out = subprocess.run([sys.executable, str(CUE), 'page', self.d, '10-K_FY2025', '1-2'], capture_output=True, text=True, check=True).stdout
        self.assertIn('[10-K_FY2025 p2]', out)
        env = {k: v for k, v in os.environ.items() if not k.startswith('CUE_LLM_')}
        out = subprocess.run([sys.executable, str(CUE), 'brief', self.d], capture_output=True, text=True, check=True, env=env).stdout
        self.assertIn('10-K_FY2025', out); self.assertEqual(rj(f'{self.d}/leads.json')['aligner'], 'fallback')
class QuietStream:

MCP server referenced without pinned version: 'npx skills'.

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.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

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.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

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.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

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.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.en.md (reported line 53)May include surrounding context.

md
hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("Not found: that id is not in the catalog response (or its routing has no url field) - run the line above and pick one of the ids listed")' "$DOMAIN_ID")"
   [ -n "$ROUTING_URL" ] || { echo "Fetch failed: ROUTING_URL is empty - run the line above, then set DOMAIN_ID to one of the ids listed" >&2; exit 1; }
   curl -s -X POST "$ROUTING_URL" \
     -H "Authorization: Bearer $CUE_API_KEY" \
     -H "Accept: application/json, text/event-stream" \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.md (reported line 53)May include surrounding context.

md
hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("未取到:该 id 不在目录现值里(或它没有 routing 里的 url 字段)——先跑上面那行列出现场的 id 再填")' "$DOMAIN_ID")"
   [ -n "$ROUTING_URL" ] || { echo "取值失败:ROUTING_URL 为空——先跑上面那行列出现场 id,再把 DOMAIN_ID 换成其中之一" >&2; exit 1; }
   curl -s -X POST "$ROUTING_URL" \
     -H "Authorization: Bearer $CUE_API_KEY" \
     -H "Accept: application/json, text/event-stream" \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, network, shell.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/cue.py (reported line 67)May include surrounding context.

python
def get(url, headers=None, data=None, tries=4):
    for a in range(tries):
        try:
            with urllib.request.urlopen(urllib.request.Request(url, data=data, headers=headers or {'User-Agent': 'Mozilla/5.0'}), timeout=90) as r: return r.read()
        except urllib.error.HTTPError as e:
            if a == tries - 1 or (400 <= e.code < 500 and e.code != 429): raise   # 4xx (except rate limit) will not fix itself on retry
            time.sleep(2 * (a + 1))

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/cue.py (reported line 83)May include surrounding context.

python
finally: doc.close()
    except ImportError:
        import subprocess
        t = subprocess.run(['pdftotext', '-layout', path, '-'], capture_output=True, text=True).stdout.split('\f')
        if len(t) > 1 and not t[-1].strip(): t = t[:-1]            # pdftotext ends with a form feed: no extra empty page
        return t[:max_pages]
class _Txt(__import__('html.parser').parser.HTMLParser):

Tainted flow: 'path' from os.environ.get (line 197, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/cue.py (reported line 83)May include surrounding context.

python
finally: doc.close()
    except ImportError:
        import subprocess
        t = subprocess.run(['pdftotext', '-layout', path, '-'], capture_output=True, text=True).stdout.split('\f')
        if len(t) > 1 and not t[-1].strip(): t = t[:-1]            # pdftotext ends with a form feed: no extra empty page
        return t[:max_pages]
class _Txt(__import__('html.parser').parser.HTMLParser):

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/cue.py (reported line 86)May include surrounding context.

python
t = subprocess.run(['pdftotext', '-layout', path, '-'], capture_output=True, text=True).stdout.split('\f')
        if len(t) > 1 and not t[-1].strip(): t = t[:-1]            # pdftotext ends with a form feed: no extra empty page
        return t[:max_pages]
class _Txt(__import__('html.parser').parser.HTMLParser):
    BLOCK = {'p', 'div', 'br', 'tr', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'table', 'hr'}
    def __init__(s): super().__init__(); s.o = []; s.skip = 0
    def handle_starttag(s, t, a):

Whitespace Padding

Medium
Category
Prompt Injection
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.

Content

Scanner excerpt · scripts/cue.py (reported line 131)May include surrounding context.

python
body = urllib.parse.urlencode({'pageNum': page, 'pageSize': 30, 'column': 'sse' if code.startswith('6') else 'szse', 'tabName': 'fulltext',
                                           'stock': f"{code},{org['orgId']}", 'category': category, 'seDate': f'{s}~{e}', 'isHLtitle': 'true'}).encode()
            r = json.loads(get('http://www.cninfo.com.cn/new/hisAnnouncement/query', data=body, headers={'User-Agent': 'Mozilla/5.0', 'X-Requested-With': 'XMLHttpRequest',
                                                                                                          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}))
            for a in r.get('announcements') or []:
                out.append({'id': str(a['announcementId']), 'title': re.sub('</?em>', '', a['announcementTitle'] or ''), 'url': a['adjunctUrl'],
                            'date': datetime.datetime.fromtimestamp(a['announcementTime'] / 1000, datetime.timezone(datetime.timedelta(hours=8))).date(

Static analysis

No suspicious patterns detected.