Back to skill

Security audit

Fundraising Advisor

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent fundraising assistant, but it needs Review because its Python report generation can save sensitive PDFs by default and build output paths from unsanitized company names.

Review this skill before installing if you plan to process real financial statements or confidential fundraising data. Use an isolated workspace, redact unnecessary sensitive details, avoid running administrator install commands unless you understand them, disable automatic PDF generation where possible, and only save reports to explicitly chosen safe paths.

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

T09 · Insecure Skill Coding Practices

Error
Location
fa_advisor/advisor.py:131
Finding
User-Controlled Project Name Enables Path Traversal and Arbitrary PDF Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `fa_advisor/advisor.py:131-143` **Related Validation Location**: `fa_advisor/types/project.py:193` **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```python # fa_advisor/types/project.py:193 name: str = Field(min_length=1) ``` ```python # fa_advisor/advisor.py:131-143 output_dir = Path("output") output_dir.mkdir(exist_ok=True) result['pdf_reports'] = { 'assessment': await self.pdf_generator.generate_assessment_report( assessment, project.name, output_dir / f"{project.name}_assessment.pdf" ), 'valuation': await self.pdf_generator.generate_valuation_report( valuation, project.name, output_dir / f"{project.name}_valuation.pdf" ) } ``` The same unsafe construction is used for investment memos: ```python # fa_advisor/advisor.py:191-199 output_dir = Path("output") output_dir.mkdir(exist_ok=True) result['pdf_memo'] = await self.pdf_generator.generate_investment_memo( memo, project.name, output_dir / f"{project.name}_investment_memo.pdf" ) ``` The resulting path is passed directly to ReportLab: ```python # fa_advisor/pdf/generator.py:94-95 output_path = Path(output_path) doc = SimpleDocTemplate( str(output_path), pagesize=self.page_size, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18 ) ``` ### Technical Analysis `Project.name` is only constrained to have a minimum length. It is not restricted to safe filename characters and may contain absolute paths, path separators, or traversal sequences such as `../`. The value is interpolated directly into output filenames. `pathlib` does not automatically guarantee that the resulting destination remains inside the intended `output` directory. Traversal components can escape that directory, while an absolute interpolated path can override the base path. The PDF generator subsequently opens th ...[truncated 1471 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use the raw company name as a filesystem component. 2. Convert names to a strict filename slug containing only approved characters, such as ASCII letters, digits, hyphens, and underscores. 3. Reject absolute paths, `/`, `\`, `..`, null bytes, control characters, and platform-specific reserved names. 4. Resolve the destination and enforce containment beneath the approved output directory: ```python import re from pathlib import Path output_dir = Path("output").resolve() output_dir.mkdir(mode=0o700, parents=True, exist_ok=True) safe_name = re.sub(r"[^A-Za-z0-9_-]+", "_", project.name).strip("_") if not safe_name: raise ValueError("Project name cannot produce an empty filename") destination = (output_dir / f"{safe_name}_assessment.pdf").resolve() if output_dir not in destination.parents: raise ValueError("Invalid output path") ``` 5. Use collision-resistant filenames or exclusive file creation where overwriting is unnecessary. 6. Require explicit authorization before replacing an existing file. 7. Apply the same centralized path-validation routine to assessment, valuation, and investment-memo outputs. 8. Add tests covering traversal sequences, absolute paths, Windows separators, reserved names, and symbolic-link edge cases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
fa_advisor/advisor.py:67
Finding
Confidential Financial Reports Are Persisted by Default Without Separate Save Consent<![CDATA[ ## Vulnerability Details **File Location**: `fa_advisor/advisor.py:67-72, 129-145, 150-154, 189-201` **Vulnerability Type**: Unexpected persistence of sensitive business information **Risk Level**: Medium ### Vulnerable Code ```python async def startup_package( self, project: Project, financial_pdf: Optional[str | Path] = None, generate_pdf: bool = True ) -> Dict: ``` ```python if generate_pdf: print("📝 Generating PDF reports...") output_dir = Path("output") output_dir.mkdir(exist_ok=True) result['pdf_reports'] = { 'assessment': await self.pdf_generator.generate_assessment_report( assessment, project.name, output_dir / f"{project.name}_assessment.pdf" ), 'valuation': await self.pdf_generator.generate_valuation_report( valuation, project.name, output_dir / f"{project.name}_valuation.pdf" ) } ``` ```python async def investor_package( self, project: Project, generate_pdf: bool = True ) -> Dict: ``` ```python if generate_pdf: print("📝 Generating PDF investment memo...") output_dir = Path("output") output_dir.mkdir(exist_ok=True) result['pdf_memo'] = await self.pdf_generator.generate_investment_memo( memo, project.name, output_dir / f"{project.name}_investment_memo.pdf" ) ``` ### Technical Analysis Both comprehensive package methods default `generate_pdf` to `True`. Merely invoking these analysis methods therefore writes reports to persistent local storage unless the caller explicitly opts out. These reports can contain confidential company assessments, fundraising amounts, valuation conclusions, financial performance, team analysis, risk findings, and investment recommendations. The Skill instructions present saving or exporting materials as an optional follow-up action, but the Python implementation persists reports during the initial workflow by defa ...[truncated 1336 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change both method defaults to `generate_pdf=False`. 2. Obtain explicit user approval before creating files. 3. Display and confirm the intended destination before writing confidential reports. 4. Let callers provide an approved output directory rather than always using the current working directory. 5. Create directories and files with restrictive permissions, such as owner-only access where supported. 6. Document retention, deletion, backup, and synchronization implications. 7. Avoid embedding confidential details in filenames. 8. Provide an in-memory result by default and a separate explicit export method, for example: ```python async def startup_package( self, project: Project, financial_pdf: Optional[str | Path] = None, generate_pdf: bool = False, ) -> Dict: ... ``` 9. Add audit logging that records report creation without logging the report's confidential contents. 10. Provide secure cleanup functionality for temporary or no-longer-needed reports. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
fa_advisor/pdf/ocr.py:37
Finding
Unbounded PDF Conversion and OCR Permit Resource-Exhaustion Attacks<![CDATA[ ## Vulnerability Details **File Location**: `fa_advisor/pdf/ocr.py:37-74` **Vulnerability Type**: Uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code ```python async def ocr_pdf( self, pdf_path: str | Path, dpi: int = 300, preprocess: bool = True ) -> PDFExtractionResult: """ Perform OCR on a PDF file """ try: pdf_path = Path(pdf_path) if not pdf_path.exists(): return PDFExtractionResult( success=False, error=f"PDF file not found: {pdf_path}" ) # Convert PDF to images images = convert_from_path(str(pdf_path), dpi=dpi) # Perform OCR on each page text_parts = [] for i, image in enumerate(images): logger.info(f"Processing page {i + 1}/{len(images)}") if preprocess: image = self._preprocess_image(image) page_text = pytesseract.image_to_string( image, lang=self.language ) if page_text.strip(): text_parts.append(f"--- Page {i + 1} ---\n{page_text}") full_text = "\n\n".join(text_parts) return PDFExtractionResult( success=True, text=full_text ) ``` ### Technical Analysis The OCR method accepts a caller-controlled PDF and DPI value without enforcing: - Maximum input-file size. - Maximum page count. - Maximum DPI. - Maximum rendered dimensions or pixel count. - Maximum extracted text size. - Memory or CPU budgets. - Per-page or total execution timeouts. `convert_from_path()` converts the PDF into a list of page images before OCR begins. A large document or high DPI can therefore allocate substantial memory for every rendered page. Tesseract then performs CPU-intensive OCR on each image, while extracted text is accumulated in memory. The function is declared asynchronous but invokes blocking conversion and OCR ope ...[truncated 1302 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce an input-file size limit before parsing. 2. Inspect PDF metadata and reject documents exceeding a configured page limit. 3. Restrict DPI to a safe range and do not accept arbitrary values: ```python MIN_DPI = 72 MAX_DPI = 400 if not MIN_DPI <= dpi <= MAX_DPI: raise ValueError("DPI is outside the permitted range") ``` 4. Render and process bounded page ranges instead of materializing every page at once. 5. Enforce maximum image dimensions and total pixel budgets. 6. Limit extracted text size. 7. Run PDF conversion and OCR in isolated worker processes with CPU, memory, temporary-storage, and execution-time limits. 8. Add per-page and whole-document timeouts. 9. Move blocking OCR work out of the asynchronous event loop using an executor or dedicated job queue. 10. Validate the file signature and parser result rather than relying only on the path or extension. 11. Treat malformed documents as failures and avoid returning excessive parser error details to untrusted callers. 12. Add tests with oversized files, many-page documents, decompression bombs, malformed PDFs, and extreme DPI values. ]]>

T08 · Insecure Dependencies

Note
Location
pyproject.toml:52
Finding
Unnecessary Third-Party Asyncio Dependency Expands the Supply-Chain Attack Surface<![CDATA[ ## Vulnerability Details **File Location**: `pyproject.toml:52-54` **Vulnerability Type**: Unnecessary and inconsistent third-party dependency **Risk Level**: Low ### Vulnerable Code ```toml # Async Support "aiofiles>=23.2.0", "asyncio>=3.4.3", ``` The separate `requirements.txt` includes `aiofiles` but does not include the third-party `asyncio` package. ### Technical Analysis The project requires Python 3.10 or later, where `asyncio` is part of the Python standard library. Installing a separately distributed package named `asyncio` is unnecessary for the supported runtime. This expands the dependency graph and introduces third-party package code under the name of a standard-library module. It can create module-shadowing, resolver, or compatibility problems. It also causes installation behavior to differ depending on whether users install from `pyproject.toml` or `requirements.txt`. The dependency is specified only with a lower bound and without a lockfile in the reviewed project, reducing reproducibility. No evidence was found that the dependency itself is malicious. The finding concerns unnecessary supply-chain exposure and inconsistent dependency management. ### Attack Path 1. A user follows the documented editable installation process, such as `pip install -e .`. 2. The package resolver reads `pyproject.toml`. 3. It downloads and installs the external package named `asyncio`. 4. Additional third-party code enters the environment despite Python already providing the required module. 5. Installation behavior may differ from a deployment using `requirements.txt`, where the dependency is absent. 6. A compromised package release, resolver confusion, or incompatible module resolution could affect the installed environment. ### Impact Assessment No direct privilege escalation or confirmed malicious execution was identified. Any dependency installation code executes with the privileges of the account performing installation, so unnecessary depe ...[truncated 378 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `"asyncio>=3.4.3"` from `pyproject.toml`. 2. Use Python's standard-library `asyncio` module for all supported Python versions. 3. Synchronize `pyproject.toml`, `requirements.txt`, and development dependency files. 4. Generate and review lockfiles or constraint files for deployment environments. 5. Pin or constrain dependency versions according to a documented update policy. 6. Use dependency hashes where practical for production builds. 7. Add automated dependency scanning and software-bill-of-materials generation. 8. Periodically remove unused packages, including dependencies such as `aiofiles` if they are not used by the implementation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (129)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Most of the declared description aligns with the demonstrated behavior: startup project assessment, valuation analysis, pitch deck generation, and PDF-related capabilities are all represented. However, the declared purpose includes investor matching as a capability, while the code's own summary says '投资人匹配算法' needs to be completed and references the TypeScript version for that remaining module. That makes the description overstate the implemented functionality for this code chunk. No undeclared sensitive behaviors, resource access, or unrelated triggers are evident.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
Most of the declared fundraising advisory capabilities are accurately represented: the code clearly supports startup assessment, pitch deck and business plan generation, valuation, investor matching, and investor analysis. However, the description explicitly claims PDF processing, including financial statements, OCR, and reports, and no such functionality appears in this code chunk. The actual code is centered on advisory workflows and exported modules, with no evidence of file handling, PDF parsing, OCR, or report extraction. Therefore, this is a description-to-behavior mismatch due to overclaiming document-processing capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The supplied code focuses on analyzing a startup project object and producing investor-oriented outputs: an investment memo, a formatted memo document, due diligence checklist, risk analysis, financial analysis, and a recommendation decision. This partially overlaps with the declared 'project assessment' and 'valuation analysis' themes, but several prominent declared capabilities are not represented here, including pitch deck generation, investor matching, and any PDF/OCR or document-processing behavior. Additionally, the code’s framing is more for investment institutions generating memos and diligence materials than for a general startup fundraising advisor. No suspicious external access or trigger mismatch is visible in this chunk, but the description overstates and somewhat mischaracterizes the implemented behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The code is aligned with one portion of the description: project assessment for startup fundraising readiness. However, the declared description presents a broader multi-capability fundraising advisory skill including pitch deck generation, valuation analysis, investor matching, and PDF/OCR processing. None of those additional capabilities appear in this code chunk. There is no file handling, OCR, PDF parsing, investor database access, valuation modeling, or presentation generation logic. Therefore the code chunk materially under-implements the declared functionality, so the description does not accurately represent what this supplied code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The supplied code matches part of the declared description: it generates pitch deck content and a business plan for startup fundraising materials. However, the declared purpose is substantially broader and specifically includes project assessment, valuation analysis, investor matching, and PDF/OCR processing. None of those capabilities appear in this code chunk. The code is a deterministic content formatter/builder over a Project object, with no document ingestion, OCR, external matching, valuation logic, or analysis engine. Therefore the description does not accurately represent what this code chunk actually does.

Intent-Code Divergence

High
Confidence
97% confidence
Finding
The skill tells users it cannot read external files or documents while simultaneously advertising PDF processing and OCR capabilities. This contradiction can cause users to misunderstand what data may be ingested or exposed, weakening informed consent around sensitive financial documents.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This markdown file is predominantly written in Chinese, which effectively forces a specific language for contributors. Under the policy, language constraints should either offer user choice or be clearly documented as a justified locale-specific limitation, neither of which appears here.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This markdown file presents all user-facing instructions, status, and usage guidance exclusively in Chinese. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
This markdown file uses Chinese as the default language throughout, including headings, instructions, and release guidance. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale constraint is clearly documented and justified, which is not present here.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
该文档从标题到正文均以中文呈现,且没有说明这是特定受众或地区限定的发布指南,也没有提供其他语言选项。根据语言/地区政策,强制单一语言且未取得用户选择或明确正当说明,属于自然语言层面的政策问题。

Skill Enumeration

Medium
Category
Agent Snooping
Content
cp -r /path/to/ai-fa/* skills/fa-advisor/

   # 确保包含 SKILL.md
   ls skills/fa-advisor/SKILL.md
   ```

4. 提交并创建 PR:
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The entire publishing guide is written in Chinese and does not indicate that other languages are supported or that Chinese is required for a region-specific reason. This is a natural-language locale policy concern because it imposes a language choice on users without opt-in.

Skill Enumeration

Medium
Category
Agent Snooping
Content
/home/justin/ai-fa/ skills/fa-advisor-python/

# 确保SKILL.md存在
ls skills/fa-advisor-python/SKILL.md
```

### Step 3: 提交 Pull Request
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The document encourages OCR/PDF parsing of financial statements and scanned documents, which are likely to contain sensitive business and personal data, but it provides no warning about confidentiality, retention, consent, or secure handling. In a fundraising advisory skill, this omission is more dangerous because users are specifically likely to upload highly sensitive financial records and investor materials.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The report-generation example states that PDF reports are saved to an output directory but does not warn that potentially sensitive assessment, valuation, and financial data will be written to disk. In this skill context, that creates avoidable exposure through local file leakage, backups, shared workstations, or insecure permissions.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The markdown includes operational shell commands that create files, install dependencies, and execute code directly on the user's system without prominent safety warnings, sandboxing guidance, or least-privilege recommendations. In a skill context, users may copy-paste these commands verbatim, which can lead to unintended system modification, package installation, or execution of unreviewed code.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install ghostscript tcl-tk

# Ubuntu
sudo apt-get install ghostscript python3-tk

# 然后重试
pip install "camelot-py[cv]"
Confidence
95% confidence
Finding
The document instructs users to run a sudo apt-get install command, which requires elevated privileges and changes the host system. Even though the command itself is a common package installation step, including privileged execution guidance in a skill increases risk because users may execute it without understanding system-wide effects or verifying necessity.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The entire skill quickstart, including headings, instructions, examples, and trigger phrases, is written in Chinese with no indication that users may choose another language. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation unless the locale constraint is explicitly justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install tesseract-ocr poppler-utils ghostscript
sudo apt-get install tesseract-ocr-chi-sim  # 中文支持
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.