Back to skill

Security audit

homework-grader

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its homework grading purpose, but its Excel/PDF report generation can unsafe­ly carry student-controlled data into files teachers may trust.

Install only in an isolated environment and treat all student names, answers, photos, JSON files, Excel files, and PDFs as sensitive. Do not use this for untrusted student-supplied names or answers unless the report generators are fixed to escape HTML, neutralize spreadsheet formulas, pin dependencies, and document where student data is stored and how it should be deleted.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_excel.py:91
Finding
Spreadsheet Formula Injection Through Student Names<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_excel.py`, lines 91 and 181–189 **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: High ### Vulnerable Code ```python for row, student in enumerate(stats['students'], 2): ws_students.cell(row=row, column=1, value=student['name']) ws_students.cell(row=row, column=2, value=student['correct_count']) ws_students.cell(row=row, column=3, value=student['incorrect_count']) ws_students.cell(row=row, column=4, value=f"{student['error_rate']}%") ws_students.cell(row=row, column=5, value=str(student['incorrect_questions'])) ``` The student records are loaded from an externally supplied JSON file without validation: ```python with open(student_file, 'r', encoding='utf-8') as f: students_data = json.load(f) with open(correct_file, 'r', encoding='utf-8') as f: correct_answers = json.load(f) generate_excel(students_data, correct_answers, output_path, threshold) ``` ### Technical Analysis The `name` field is controlled by the supplied student-data JSON and is passed directly to `openpyxl` as a cell value. A string beginning with `=` can be stored as a spreadsheet formula instead of inert text. Formula-like prefixes such as `+`, `-`, or `@` may also receive special treatment in some spreadsheet applications or workflows. No validation or neutralization is applied before the workbook is saved. An attacker able to influence a student name could therefore inject a formula such as a hyperlink, an external-data function, or another application-supported formula. Execution occurs when a teacher or administrator opens or interacts with the generated workbook, subject to the spreadsheet application's security policy. ### Attack Path 1. An attacker supplies or influences a student-data JSON record. 2. The attacker sets the `name` field to a formula-like value, for example one beginning with `=`. 3. `generate_excel.py` loads the JSON without validating the fie ...[truncated 1100 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat all values originating from OCR, JSON input, student names, and answer data as untrusted. - Before writing text to a workbook, neutralize values beginning with `=`, `+`, `-`, or `@`. - Prefix formula-like values with an apostrophe or explicitly force the cell to contain an inert string. - Centralize this protection in a helper and apply it to every user-controlled cell. Example: ```python def safe_excel_text(value): text = str(value) if text.startswith(("=", "+", "-", "@")): return "'" + text return text cell = ws_students.cell( row=row, column=1, value=safe_excel_text(student["name"]) ) cell.data_type = "s" ``` - Define maximum field lengths and allowed character rules for names. - Add tests covering formula prefixes, leading whitespace, tabs, carriage returns, and Unicode variants. - Warn users not to disable spreadsheet protected-view or external-content protections. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_pdf.py:146
Finding
Unescaped HTML Injection During PDF Generation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_pdf.py`, lines 146–150, 164–170, and 184 **Vulnerability Type**: HTML injection with server-side resource retrieval potential **Risk Level**: High ### Vulnerable Code The correct answer is inserted directly into generated HTML: ```python for kq in key_questions: html_content += f""" <div class="suggestion"> <div class="suggestion-title">第{kq['question']}题 (错误率: {kq['error_rate']}%)</div> <p><strong>正确答案:</strong> {kq['correct_answer']}</p> <p><strong>讲解建议:</strong> 该题错误人数较多,建议老师在课堂上详细讲解此题目。</p> </div> """ ``` Student names are also inserted without escaping: ```python for student in student_stats: html_content += f""" <tr> <td>{student['name']}</td> <td>{student['correct_count']}</td> <td>{student['incorrect_count']}</td> <td>{100 - student['error_rate']}%</td> </tr> """ ``` The resulting attacker-influenced HTML is parsed by WeasyPrint: ```python HTML(string=html_content).write_pdf(output_path) ``` ### Technical Analysis Student names and correct answers originate from supplied JSON or OCR-derived data and are interpolated directly into an HTML document. The code does not apply HTML escaping or use a template engine with automatic escaping. An attacker can terminate the expected text context and insert arbitrary HTML elements. Because WeasyPrint resolves resources referenced by elements and styles during rendering, injected markup can potentially cause the PDF-generation process to request attacker-selected URLs. Depending on the WeasyPrint version, operating environment, and URL-fetcher configuration, the injected content may also attempt to access local resources. Even when resource access is blocked, arbitrary markup can alter the report structure, conceal legitimate information, impersonate report sections, or place deceptive content in the generated PDF. ### Attack Path 1. An attacker influences a student name o ...[truncated 1350 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Escape every untrusted value before placing it in HTML: ```python from html import escape safe_name = escape(str(student["name"]), quote=True) safe_answer = escape(str(kq["correct_answer"]), quote=True) ``` - Prefer a maintained template engine configured with automatic escaping rather than constructing HTML through f-strings. - Apply strict type and range validation to numeric fields before interpolation. - Configure a restrictive custom WeasyPrint URL fetcher that denies: - HTTP and HTTPS resources unless explicitly required and allowlisted. - `file:` URLs outside an approved resource directory. - Loopback, link-local, private, and internal network destinations. - Redirects from approved locations to prohibited destinations. - Run PDF generation in a sandboxed process with no unnecessary network access and minimal filesystem permissions. - Set size and time limits for resource loading. - Add security tests using injected elements, attributes, CSS URLs, malformed markup, local-file URLs, and internal-network URLs. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/requirements.txt:1
Finding
Unpinned and Incomplete Third-Party Dependency Declaration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/requirements.txt`, lines 1–3; `SKILL.md`, line 21; `scripts/generate_pdf.py`, lines 11–14 **Vulnerability Type**: Insecure and non-reproducible dependency installation **Risk Level**: Medium ### Vulnerable Code The declared dependencies have no version or integrity constraints: ```text pillow pytesseract openpyxl ``` The documented installation command resolves those mutable package names from the configured package index: ```bash pip install -r scripts/requirements.txt ``` The PDF generator additionally imports an undeclared dependency and recommends an unconstrained installation: ```python try: from weasyprint import HTML except ImportError: print("请安装依赖: pip install weasyprint") exit(1) ``` ### Technical Analysis The requirements file lists package names without exact versions or cryptographic hashes. As a result, installations performed at different times can resolve to different package releases. Future incompatible or compromised releases could therefore enter the environment without any change to the audited project. Additionally, `weasyprint` is required by `generate_pdf.py` but is absent from `scripts/requirements.txt`. The fallback message instructs users to install the latest package version manually, bypassing a reviewed and reproducible dependency specification. The observed package names are legitimate and there is no evidence in the audited repository that they are intentionally malicious. The risk arises from mutable, unconstrained dependency resolution and the incomplete dependency manifest. ### Attack Path 1. A user follows the documented installation instructions. 2. `pip` queries the configured package index for the latest versions matching the unpinned names. 3. The environment installs releases that were not fixed or reviewed when the Skill was audited. 4. If a package release or configured package index is compromised, malicious package code may execu ...[truncated 797 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin every direct dependency to a reviewed version, including `weasyprint`. - Generate and commit a lock file that includes transitive dependencies. - Record cryptographic hashes and install with hash verification, for example through `pip --require-hashes`. - Use a controlled package index or approved internal mirror. - Remove manual unconstrained installation instructions from exception handlers. - Add WeasyPrint and all required runtime components to the documented dependency manifest. - Run dependency vulnerability and license scans in CI. - Establish a controlled update process that reviews release notes, vulnerabilities, integrity, and compatibility before changing pinned versions. - Install dependencies in an isolated virtual environment under a non-privileged account. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented behavior materially overstates the implemented functionality, including bulk grading, class-wide analytics, Excel/PDF generation, and direct grading from student photos. This mismatch can cause operators to trust outputs that are incomplete, fabricated, or based on different inputs than expected, which is especially risky in educational workflows involving student records and grading decisions.

Ae1

High
Category
analysis-evasion
Content
pip install -r scripts/requirements.txt
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
pip install -r scripts/requirements.txt
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill advertises file read/write-capable behavior but does not declare any explicit tool scope or permission boundaries. In an agent environment, this increases the chance of overbroad filesystem access, unintended reads of unrelated files, or writes to arbitrary locations during report generation and intermediate data handling.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### 系统依赖(必须安装)
```bash
# Ubuntu/Debian
sudo apt install tesseract-ocr

# macOS
brew install tesseract
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill explicitly handles student homework photos, names, grading results, and generated reports without any privacy notice, retention policy, or guidance on sensitive educational data handling. This creates a risk of exposing personally identifiable student information and assessment data through stored images, spreadsheets, or PDFs.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code file contains its title, docstrings, usage instructions, and runtime messages exclusively in Chinese, including the command help and dependency guidance. Under the policy rule, forcing a specific language without opt-in or an explicit justified locale constraint is a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The manifest description, headings, trigger phrases, and example commands are all presented in Chinese, including required user utterances like '生成统计表' and '设置阈值为 30%'. This can be a natural-language policy issue if the skill effectively forces a specific language without user opt-in or an explicitly documented regional justification.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The HTML template hard-codes major user-facing labels and the report title in Chinese, such as the page title and section headings, while only the footer includes limited English text. This can violate a language/locale policy when a skill imposes one language by default without explicit user opt-in or documented regional justification.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
This markdown file contains user-facing instructions exclusively in Chinese, which can constitute a language/locale policy issue when no opt-in, alternative language, or justification is provided. The policy specifically calls for flagging skills that force a specific language without user choice.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This markdown file presents the output format exclusively in Chinese, beginning with the title and continuing throughout the document. Under the policy rule, forcing a specific language without offering a choice or documenting a justified locale constraint can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The module docstring, dependency message, function-facing status output, and CLI usage text are written only in Chinese. This imposes a specific language on users without any opt-in or indication that the skill is intentionally limited to a Chinese-speaking context, which matches the language/locale policy concern.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pillow
pytesseract
openpyxl
Confidence
98% confidence
Finding
The dependency 'pillow' is unpinned, so installs are not reproducible and may unexpectedly pull a vulnerable or breaking release. In this skill, Pillow processes uploaded homework images, making dependency integrity and version control more important because image parsing libraries have a long history of memory-safety and denial-of-service issues.

Unverifiable Dependency: pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +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
Pillow has multiple historical advisories, including code execution and resource-consumption issues, and the manifest does not pin a version, so the deployed release could be vulnerable. This is more concerning here because the skill processes untrusted uploaded images from students and teachers, which directly exercises Pillow's attack surface.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pillow
pytesseract
openpyxl
Confidence
93% confidence
Finding
The dependency 'pytesseract' is unpinned, which allows different environments to resolve different versions with potentially unreviewed behavior or transitive dependency changes. In a grading workflow that handles teacher- and student-supplied files at scale, this weakens supply-chain assurance and can complicate patch validation and incident response.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pillow
pytesseract
openpyxl
Confidence
97% confidence
Finding
The dependency 'openpyxl' is unpinned, creating supply-chain uncertainty and making it impossible to guarantee that deployment will use a patched release. Because this skill generates Excel reports and may consume spreadsheet-related data, predictable and patched XML/office-file handling is important to reduce exposure to parser-related vulnerabilities.

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
Openpyxl has known XXE-related advisories, and because the dependency is unpinned, there is no assurance that the installed version includes the relevant fixes. In this skill, spreadsheet generation and possible spreadsheet handling increase the relevance of XML parser hardening, though the risk is somewhat lower if the application only writes XLSX files and never ingests attacker-controlled spreadsheets.

Static analysis

No suspicious patterns detected.