Back to skill

Security audit

research analyst

Security checks for vulnerabilities and agentic risk

Overview

This finance-analysis skill mostly matches its purpose, but its dependency verification claims and local portfolio storage need careful review before installation.

Review requirements.txt before installing, prefer a fresh virtual environment, and do not bypass hash failures by deleting hashes unless you independently trust the packages. If you use portfolio tracking, treat the stored portfolios.json as private financial data and keep CLAWDBOT_STATE_DIR in a directory only your user can read or write.

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)

T08 · Insecure Dependencies

Warning
Location
requirements.txt:9
Finding
Invalid dependency hashes combined with ineffective installation verification<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:9-49`; `verify_install.sh:29-39` **Vulnerability Type**: Supply-chain integrity control failure **Risk Level**: Medium ### Vulnerable Code `requirements.txt:9-13`: ```text yfinance==0.2.40 \ --hash=sha256:2be58b9e7c69e6d92a61f1e0b8c8df7b3d4c8f77f59f0b7e5b33f1c6e50e6b6f requests==2.31.0 \ --hash=sha256:942c5a758f98d844f6e0e4f3d1c7e1c7a6e9f8c1f1a8e8c8d8e8f8a8b8c8d8e8 beautifulsoup4==4.12.3 \ ``` Additional suspicious hash entries at `requirements.txt:30-37`: ```text pandas==2.2.0 \ --hash=sha256:1187589f2c6a0f3c7a52c9c1e8dc7f2f8e7f5f5d5e6f5e5c5d5a5b5c5d5e5f5a numpy==1.26.3 \ --hash=sha256:697f3f8c8e1a8d1f4c1e8b6c3d1e1f5d5e5c5d5a5b5c5d5e5f5a5b5c5d5e5f5a ``` `verify_install.sh:29-39`: ```bash # Check 2: Verify requirements.txt echo "2. Verifying requirements.txt..." if [ -f requirements.txt ]; then echo -e "${GREEN} ✓ requirements.txt found${NC}" DEP_COUNT=$(grep -c "==" requirements.txt || echo 0) if [ "$DEP_COUNT" -gt 0 ]; then echo -e "${GREEN} ✓ Contains $DEP_COUNT pinned dependencies${NC}" else echo -e "${YELLOW} ⚠ No pinned dependencies found${NC}" WARNINGS=$((WARNINGS + 1)) fi ``` ### Technical Analysis The manifest supplies SHA-256 values that appear synthetic and do not provide a trustworthy binding to the intended PyPI artifacts. Several values contain conspicuous repeating patterns. Because hashes are present in the requirements file, pip's hash-checking behavior can reject distributions whose actual digest does not match the declared value. The bundled verification script does not validate any digest, download a candidate distribution, run dependency resolution, or invoke pip in hash-verification mode. It only counts lines containing `==`. Consequently, it can report that verification passed even when the requirements cannot be installed or the declared integrity metadata is invalid. This contradicts the pr ...[truncated 1660 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Regenerate hashes from trusted PyPI artifacts using a reproducible workflow such as: ```bash pip-compile --generate-hashes requirements.in ``` Alternatively, download each approved artifact and calculate its digest with `pip hash`. 2. Include valid hashes for every permitted wheel and source distribution needed on supported platforms. 3. Enforce verification explicitly: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` 4. Update `verify_install.sh` so it performs dependency resolution and fails on invalid hashes: ```bash python3 -m pip install \ --require-hashes \ --dry-run \ -r requirements.txt ``` 5. Make warnings affect the verifier's exit status when an integrity check fails. Do not print `VERIFICATION PASSED` based only on file presence and pin counts. 6. Test the locked manifest in clean virtual environments for every supported Python version and platform. 7. Recommend virtual-environment installation consistently and avoid suggesting privileged or global pip installation. 8. Correct documentation claims about dependency integrity and platform independence. In particular, packages such as NumPy, pandas, and lxml commonly use platform-specific compiled distributions. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/portfolio_manager.py:43
Finding
Portfolio data and temporary files are created without explicit access restrictions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/portfolio_manager.py:43-49`, `scripts/portfolio_manager.py:91-110`, `scripts/portfolio_manager.py:133-151` **Vulnerability Type**: Insecure sensitive-data storage and unsafe predictable temporary file **Risk Level**: Low ### Vulnerable Code `scripts/portfolio_manager.py:43-49`: ```python def get_storage_path() -> Path: """Get the portfolio storage path.""" # Use ~/.clawdbot/skills/research-analyst/portfolios.json state_dir = os.environ.get("CLAWDBOT_STATE_DIR", os.path.expanduser("~/.clawdbot")) portfolio_dir = Path(state_dir) / "skills" / "research-analyst" portfolio_dir.mkdir(parents=True, exist_ok=True) return portfolio_dir / "portfolios.json" ``` `scripts/portfolio_manager.py:91-110`: ```python def _acquire_lock(self, timeout: float = 5.0) -> bool: """Acquire a file lock with timeout.""" start_time = time.time() while time.time() - start_time < timeout: try: # Try to create lock file exclusively with open(self._lock_path, "x") as f: f.write(str(os.getpid())) return True except FileExistsError: # Lock exists, wait and retry time.sleep(0.1) return False def _release_lock(self) -> None: """Release the file lock.""" try: if self._lock_path.exists(): self._lock_path.unlink() except Exception: pass # Best effort cleanup ``` `scripts/portfolio_manager.py:133-151`: ```python def _save(self) -> None: """Save portfolios to disk with atomic write and file locking.""" if self._data is None: return # Acquire lock before writing if not self._acquire_lock(): raise RuntimeError("Could not acquire file lock after timeout") try: # Ensure directory exists self.path.parent.mkdir(parents=True, exist_ok=True) # Atomic write: write to temp file, then rename tmp_path = se ...[truncated 3121 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the portfolio directory with owner-only permissions and verify existing permissions: ```python portfolio_dir.mkdir(parents=True, exist_ok=True, mode=0o700) portfolio_dir.chmod(0o700) ``` 2. Create portfolio, lock, and temporary files with mode `0600`. 3. Use a securely generated temporary file in the same directory so the final replacement remains atomic: ```python import os import tempfile fd, temp_name = tempfile.mkstemp( prefix=".portfolios-", suffix=".tmp", dir=self.path.parent, ) try: os.fchmod(fd, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(self._data, f, indent=2) f.flush() os.fsync(f.fileno()) os.replace(temp_name, self.path) os.chmod(self.path, 0o600) except Exception: try: os.unlink(temp_name) except FileNotFoundError: pass raise ``` 4. For the lock file, use low-level exclusive creation with restrictive permissions and symbolic-link protection where supported: ```python flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(self._lock_path, flags, 0o600) ``` 5. Reject a state directory or portfolio path that is a symbolic link, is owned by another user, or is writable by group/other users. 6. Validate `CLAWDBOT_STATE_DIR` before use and document that it must point to a private directory controlled by the current user. 7. After replacement, verify that `portfolios.json` is a regular file owned by the current user and enforce mode `0600`. ]]>
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
  • Rogue AgentSelf-Modification, Session Persistence
Findings (35)

Exfiltration Commands

High
Category
Prompt Injection
Content
- ❌ Execute subprocess calls
- ❌ Modify system files or cron
- ❌ Require credentials or API keys
- ❌ Upload data to external servers
- ❌ Use eval/exec for dynamic code execution
- ❌ Install additional packages at runtime
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code does use a public API library (yfinance), does not use credentials, and does not spawn subprocesses, which aligns with parts of the description. However, its primary behavior is not merely minimal local stock/crypto analysis: it is a stateful portfolio management tool with CRUD operations for portfolios and holdings, persisting data to local files under the user's home/state directory. That is a materially broader and different capability than the declared purpose, and the description omits the filesystem persistence and management features entirely. Therefore this is a description/behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The core description is partly accurate: this script does perform stock and crypto analysis, uses read-only public market data, and does not use credentials or subprocesses. However, the code materially exceeds the declared 'minimal local stock/crypto analysis' scope by supporting portfolio analysis through a separate portfolio_manager module and by integrating additional local modules such as cn_stock_quotes for Chinese-market fallback. Those are meaningful capabilities/resources not disclosed in the declared purpose. Also, the script includes sentiment/options-chain analysis paths and market-wide context features that go beyond a narrowly described minimal analyzer. Separately, the header/documentation claims Google News breaking-news analysis, but in this code that feature is stubbed out and returns None, showing description/behavior inconsistency. Overall, the primary purpose is related, but the declared description does not accurately represent all meaningful capabilities and accessed resources.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The code chunk does not implement stock or crypto analysis at all; its primary purpose is installation and bundle verification. It runs as a bash script, invokes system commands (e.g., python3, awk, grep, find, wc), and scans the local filesystem for files and code patterns. That is materially different from the declared purpose of a minimal local analysis skill. While some checks are framed as validating claims like '5 core scripts' and 'no subprocess,' the actual behavior shown is still an undeclared verification/auditing utility rather than analysis functionality. Therefore this is a description-behavior mismatch.

Known Vulnerable Dependency: lxml==5.1.0 — 2 advisory(ies): CVE-2026-41066 (lxml: Default configuration of iterparse() and ETCompatXMLParser() allows XXE to); CVE-2026-41066 (lxml is a library for processing XML and HTML in the Python language. Prior to 6)

High
Category
Supply Chain
Confidence
89% confidence
Finding
lxml==5.1.0 is flagged for XXE-related parser weaknesses in default configurations for certain XML parsing modes. In a skill that fetches and parses external market or finance data, any XML parsing of untrusted remote content could enable file disclosure, SSRF, or parser abuse if insecure parser defaults are used.

Known Vulnerable Dependency: soupsieve==2.5 — 4 advisory(ies): CVE-2026-49476 (Soup Sieve has Memory Exhaustion via Large Comma-Separated Selector Lists); CVE-2026-49477 (Soup Sieve: Regular Expression Denial of Service (ReDoS) via Selector Parser); CVE-2026-49476 (Soup Sieve has Memory Exhaustion via Large Comma-Separated Selector Lists) +1 more

High
Category
Supply Chain
Confidence
83% confidence
Finding
soupsieve==2.5 has reported denial-of-service style issues such as memory exhaustion and regex/parser abuse on crafted selectors. As a transitive dependency of BeautifulSoup, risk depends on whether attacker-controlled selectors are ever processed, but retaining a known-vulnerable parser component can still expose the skill to resource exhaustion in edge cases.

Known Vulnerable Dependency: idna==3.6 — 4 advisory(ies): CVE-2026-45409 (Internationalized Domain Names in Applications (IDNA): Specially crafted inputs ); CVE-2024-3651 (Internationalized Domain Names in Applications (IDNA) vulnerable to denial of se); CVE-2024-3651 (A vulnerability was identified in the kjd/idna library, specifically within the ) +1 more

High
Category
Supply Chain
Confidence
86% confidence
Finding
idna==3.6 is flagged for denial-of-service and crafted-input handling issues in internationalized domain name processing. Because this skill uses HTTP libraries and may consume remote URLs or hostnames from public data sources, malformed IDN input could potentially trigger resolution or parsing weaknesses in upstream request flows.

Known Vulnerable Dependency: urllib3==2.1.0 — 12 advisory(ies): CVE-2025-66471 (urllib3 streaming API improperly handles highly compressed data); CVE-2024-37891 (urllib3's Proxy-Authorization request header isn't stripped during cross-origin ); CVE-2026-21441 (Decompression-bomb safeguards bypassed when following HTTP redirects (streaming ) +9 more

High
Category
Supply Chain
Confidence
96% confidence
Finding
urllib3==2.1.0 is a core transport dependency and the cited advisories include cross-origin header leakage and decompression/streaming issues. In a tool built around fetching stock/crypto data from public endpoints, a vulnerable HTTP transport layer is especially relevant because malformed responses, redirects, or proxy interactions could directly reach the affected code paths.

Known Vulnerable Dependency: certifi==2024.2.2 — 2 advisory(ies): CVE-2024-39689 (Certifi removes GLOBALTRUST root certificate); CVE-2024-39689 (Certifi is a curated collection of Root Certificates for validating the trustwor)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Ae1

High
Category
analysis-evasion
Content
| `stock_analyzer.py` | 99KB | 8-dimension stock/crypto analysis |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `stock_analyzer.py` | 99KB | 8-dimension stock/crypto analysis |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `stock_analyzer.py` | 99KB | 8-dimension stock/crypto analysis |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `stock_analyzer.py` | 99KB | 8-dimension stock/crypto analysis |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `stock_analyzer.py` | 99KB | 8-dimension stock/crypto analysis |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `stock_analyzer.py` | 99KB | 8-dimension stock/crypto analysis |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Session Persistence

Medium
Category
Rogue Agent
Content
### Permission Errors
```bash
mkdir -p ~/.clawdbot/skills/research-analyst/
ls -la ~/.clawdbot/skills/research-analyst/
```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

File System Enumeration

Medium
Category
Data Exfiltration
Content
### Permission Errors
```bash
mkdir -p ~/.clawdbot/skills/research-analyst/
ls -la ~/.clawdbot/skills/research-analyst/
```

### API Rate Limits
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- ❌ Download external code (all bundled)
- ❌ Execute subprocess calls
- ❌ Modify system files or cron
- ❌ Require credentials or API keys
- ❌ Upload data to external servers
- ❌ Use eval/exec for dynamic code execution
Confidence
60% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
- ❌ Execute subprocess calls
- ❌ Modify system files or cron
- ❌ Require credentials or API keys
- ❌ Upload data to external servers
- ❌ Use eval/exec for dynamic code execution
- ❌ Install additional packages at runtime
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Known Vulnerable Dependency: requests==2.31.0 — 6 advisory(ies): 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); CVE-2026-25645 (Requests has Insecure Temp File Reuse in its extract_zipped_paths() utility func) +3 more

Medium
Category
Supply Chain
Confidence
95% confidence
Finding
The requirements pin requests==2.31.0, and the cited advisories include real security issues affecting URL handling, session verification behavior, and ancillary helper functionality. Even though this file is only a dependency manifest and the skill claims to use only public APIs, a network-facing research tool commonly performs outbound HTTP requests, so keeping a known-vulnerable HTTP client increases exposure to credential leakage, TLS verification edge cases, or other request-layer abuse if code paths trigger the affected behavior.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The code and comments indicate the skill is specifically tailored to A-share and Hong Kong Chinese market data, and line L010 hardcodes Chinese field labels in comments. Under the policy rule for natural-language locale violations, this reflects a fixed language/locale assumption without offering a user choice or documenting opt-in.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The skill manifest frames the capability as minimal analysis, while this file implements a full CRUD portfolio manager that stores and mutates user portfolio data on disk. Persistent portfolio tracking is a broader behavior than read-only local analysis and is not reflected in the manifest description.

Session Persistence

Medium
Category
Rogue Agent
Content
Portfolio management for research-analyst skill.

Usage:
    python3 portfolio_manager.py create "Portfolio Name"
    python3 portfolio_manager.py list
    python3 portfolio_manager.py show [--portfolio NAME]
    python3 portfolio_manager.py delete "Portfolio Name"
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
verify_install.sh:66