Back to skill

Security audit

A-Level Physics CIE 9702

Security checks for vulnerabilities and agentic risk

Overview

The skill's normal exam-answer function is coherent, but its bundled optional data-building tools have review-worthy risks, especially an unsafe scraper and unpinned dependencies.

Install only in an isolated environment and treat the normal local inference path separately from the optional data pipeline. Do not run the scraper or full retraining pipeline against untrusted sources until filename validation and path-containment checks are fixed, and be aware that DeepSeek teacher mode sends question and mark-scheme content to an external API. Pin dependencies or use a reviewed lock file before serious use.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scraper/scrape_fraft.py:119
Finding

Server-Controlled Filename Enables Arbitrary PDF File Write

Content
View full analysis
Optional[Path]: output_dir.mkdir(parents=True, exist_ok=True) path = output_dir / item["filename"] if path.exists() and path.stat().st_size > 1024: return path backoff = max(config["request_delay_sec"], 1.0) for attempt in range(1, config["max_retries"] + 1): response = session.get(item["download_url"], timeout=60) if response.status_code == 429: wait_s = min(60, backoff * (2 ** (attempt - 1))) print(f"[WARN] 429 for {item['filename']}, retrying in {wait_s:.1f}s (attempt {attempt})") time.sleep(wait_s) continue response.raise_for_status() if "application/pdf" not in response.headers.get("Content-Type", "") and len(response.content) < 1024: return None with open(path, "wb") as f: f.write(response.content) return path ``` ### Technical Analysis The filename is obtained from the remote API through `row.get("file")`. The only enforced validation is that its lowercase representation ends with `.pdf`. Although `parse_filename()` applies a strict regular expression internally, a failed match does not reject the filename. It merely returns metadata containing null fields. The origi ...[truncated 2023 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:4
Finding

Unpinned Executable Dependencies Create Supply-Chain Exposure

Content
View full analysis
=2.28.0 beautifulsoup4>=4.12.0 lxml>=4.9.0 pdfplumber>=0.10.0 pypdf>=3.0.0 playwright>=1.40.0 # for JS-rendered cie.fraft.org # Data & training pyyaml>=6.0 tqdm>=4.65.0 # MLX fine-tuning (Apple Silicon) mlx>=0.19.0 mlx-lm>=0.19.0 # Optional: teacher model for SFT data generation openai>=1.0.0 ``` ### Technical Analysis Every dependency uses an open-ended minimum-version constraint. No upper bounds, lock file, exact versions, or package hashes are provided. Consequently, the same installation instruction can resolve to different package and transitive-dependency versions over time. Those versions may not be the ones reviewed or tested with this project. Packages such as ML frameworks, browser automation libraries, parsers, and API clients execute substantial third-party code in the user's environment. Playwright also requires a separately downloaded browser distribution, further expanding the mutable supply-chain surface. The project does not separate the minimal local-inference dependency set from optional scraping and teacher-training dependencies, so users may install network-capable components that are unnecessary for normal inference. This finding does not establish that any currently named dependency is malicious. The risk arises from non-reproducible resolution and the absence of integrity verification. ### Attack Path 1. A user follows the documented instruction to install `requirements.txt`. 2. The package resolver selects the newest releases satisfying the open-ended lower bounds, together with their current transitive dependencies. 3. A compromised, malicious, or unexpectedly incompatible future release is selected instead of the version originally reviewed. 4. Package installation, import-time initialization, brows ...[truncated 876 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (31)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill manifest does not declare an explicit tool/permission scope, while the repository description references components that can use network, shell, file access, environment variables, and MCP-like capabilities. Without a restrictive declaration, an orchestrator may grant broader capabilities than the primary inference path actually needs, increasing the chance that optional maintainer scripts or future prompt paths invoke higher-risk actions unintentionally.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The scraper accepts a configurable base URL and then fetches arbitrary content from that destination, following redirects and downloading linked HTML/PDF files. In a broader agent or automated environment, this can be abused for unintended outbound requests, internal network probing, or pulling untrusted content to disk, which is more capability than the skill's normal local-inference function requires.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest centers the skill on producing structured answer templates with a local MLX inference path and says scraping is only an optional maintainer-only workflow. This file implements a full network scraper that fetches arbitrary pages, follows discovered links, downloads PDFs, and writes a manifest of collected data, which is materially different from the end-user generation behavior described in the manifest.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The module advertises a full scrape/extract/train pipeline that is materially different from the manifest's stated primary behavior of local inference. This mismatch can mislead operators about what the skill is capable of doing, increasing the chance that higher-risk maintenance functions are executed in a context that assumed offline, low-risk behavior.

Content

No source excerpt is available for this finding.

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/run_full_pipeline.py (reported line 24)May include surrounding context.

python
def run(cmd: list[str], cwd: Path = None) -> bool:
    r = subprocess.run(cmd, cwd=cwd or PROJECT_ROOT)
    return r.returncode == 0

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This script expands the skill from local answer-template generation into a broader orchestration pipeline that performs external scraping, optional API-backed dataset generation, and shell-based training. That increased capability widens the attack surface and can cause unexpected network access, data provenance issues, and execution of maintenance-only workflows in environments that may expect a local-only educational skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This markdown file documents sending extracted question and mark scheme context to an external service using DEEPSEEK_API_KEY, but it does not warn users that content will be transmitted to a third-party API. For markdown files, omission of privacy or data-handling warnings for external network behavior is in scope for missing user warnings.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

These triggers are broad educational phrases like generic physics-answering and study-help requests, so the skill may activate when a user did not specifically intend to invoke this exam-template tool. Unintended routing can cause policy bypass at the orchestrator level, mis-handle user intent, or overshadow safer/more appropriate general tutoring skills.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger at this line is especially broad and resembles an ordinary tutoring request rather than a skill-specific invocation. A single generic phrase can create frequent accidental activation, reducing routing precision and increasing the chance of inappropriate outputs in mixed-skill environments.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The Chinese trigger block includes generic tutoring and study-framework phrases that are common in ordinary educational conversations, so they can collide with unrelated Chinese-language physics help requests. This creates the same unintended invocation risk as the English list, but across another locale, increasing total attack surface for misrouting.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The requirements file uses a lower-bound specifier for requests instead of pinning an exact version or constrained range. This makes builds non-reproducible and can silently pull in a vulnerable or breaking release, which is especially relevant here because the skill includes scraping/network functionality.

Content

Scanner excerpt · requirements.txt (reported line 5)May include surrounding context.

text
# pip install -r requirements.txt

# Scraping
requests>=2.28.0
beautifulsoup4>=4.12.0
lxml>=4.9.0
pdfplumber>=0.10.0

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
96% confidence
Finding

requests has multiple published advisories, and because the manifest does not pin a version there is no way to verify that installed environments avoid affected releases. Given that this skill includes scraping/network access in optional paths, an unresolved requests version leaves real exposure uncertainty.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

beautifulsoup4 is specified as >=4.12.0, so dependency resolution may select different versions over time. While this is mainly a supply-chain and reproducibility weakness rather than an immediate exploit, it increases uncertainty in deployed environments.

Content

Scanner excerpt · requirements.txt (reported line 6)May include surrounding context.

text
# Scraping
requests>=2.28.0
beautifulsoup4>=4.12.0
lxml>=4.9.0
pdfplumber>=0.10.0
pypdf>=3.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

lxml is unpinned and is a security-sensitive parser dependency with a history of advisories. Allowing any version above a minimum increases the risk of installing an affected release or introducing parser-related security regressions.

Content

Scanner excerpt · requirements.txt (reported line 7)May include surrounding context.

text
# Scraping
requests>=2.28.0
beautifulsoup4>=4.12.0
lxml>=4.9.0
pdfplumber>=0.10.0
pypdf>=3.0.0
playwright>=1.40.0  # for JS-rendered cie.fraft.org

Unverifiable Dependency: lxml has 14 known advisory(ies) (CVE-2021-43818 (lxml's HTML Cleaner allows crafted and SVG embedded scripts to pass through); CVE-2014-3146 (lxml Cross-site Scripting Via Control Characters); CVE-2021-28957 (lxml vulnerable to Cross-Site Scripting ) +11 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding

lxml is a historically security-sensitive parsing library, and the unpinned manifest prevents confirming whether a patched version is installed. Because the skill may parse remote HTML/XML-like content during scraping, this uncertainty is materially relevant.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

pdfplumber is not pinned, so installations are not deterministic and may drift to unreviewed versions. Because this package processes external PDF content, version uncertainty can matter if a vulnerable parser or dependency chain is introduced.

Content

Scanner excerpt · requirements.txt (reported line 8)May include surrounding context.

text
requests>=2.28.0
beautifulsoup4>=4.12.0
lxml>=4.9.0
pdfplumber>=0.10.0
pypdf>=3.0.0
playwright>=1.40.0  # for JS-rendered cie.fraft.org

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

pypdf is specified with only a minimum version, which can permit installation of versions with denial-of-service or parser flaws. In this skill's optional data-processing path, handling untrusted PDFs makes this more relevant than a purely internal library.

Content

Scanner excerpt · requirements.txt (reported line 9)May include surrounding context.

text
beautifulsoup4>=4.12.0
lxml>=4.9.0
pdfplumber>=0.10.0
pypdf>=3.0.0
playwright>=1.40.0  # for JS-rendered cie.fraft.org

# Data & training

Unverifiable Dependency: pypdf has 16 known advisory(ies) (CVE-2026-84310 (pypdf: Possible long runtimes/large memory usage when retrieving outlines); CVE-2026-48156 (pypdf: Possible long runtimes for zero-only width values in cross-reference stre); CVE-2026-24688 (pypdf has possible Infinite Loop when processing outlines/bookmarks) +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
97% confidence
Finding

pypdf has known advisories, including denial-of-service style parsing issues, and the requirements file does not guarantee a safe release. Since the project processes PDFs in data-building workflows, leaving this unverifiable increases risk from malformed or malicious documents.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

pyyaml is unpinned, creating uncertainty about which release is installed and whether known unsafe deserialization-related issues are present. Even though the file alone does not prove unsafe loader usage, the dependency should still be tightly controlled.

Content

Scanner excerpt · requirements.txt (reported line 13)May include surrounding context.

text
playwright>=1.40.0  # for JS-rendered cie.fraft.org

# Data & training
pyyaml>=6.0
tqdm>=4.65.0

# MLX fine-tuning (Apple Silicon)

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
96% confidence
Finding

pyyaml has had multiple security issues, and without a fixed version the installed package may still be affected. The requirements file alone does not show exploitation, but the unresolved advisory state is a legitimate dependency risk.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

tqdm is not pinned, so builds may resolve to different releases over time. This is a weaker direct security issue than parser/network libraries, but it still represents avoidable supply-chain and reproducibility risk.

Content

Scanner excerpt · requirements.txt (reported line 14)May include surrounding context.

text
# Data & training
pyyaml>=6.0
tqdm>=4.65.0

# MLX fine-tuning (Apple Silicon)
mlx>=0.19.0

Unverifiable Dependency: tqdm has 4 known advisory(ies) (CVE-2024-34062 (tqdm CLI arguments injection attack); CVE-2016-10075 (TDQM Arbitrary Code Execution); CVE-2016-10075 (The tqdm._version module in tqdm versions 4.4.1 and 4.10 allows local users to e) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

tqdm has known advisories and the current manifest cannot prove a safe version will be installed. The practical risk is lower than for core parsing/network packages, but the dependency state is still unverifiable and avoidable.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

mlx is unpinned despite being a native/ML framework where memory-safety issues can have significant consequences. The skill's primary local inference path depends on MLX, so version drift in this package is more operationally and security relevant than a purely optional tool.

Content

Scanner excerpt · requirements.txt (reported line 17)May include surrounding context.

text
tqdm>=4.65.0

# MLX fine-tuning (Apple Silicon)
mlx>=0.19.0
mlx-lm>=0.19.0

# Optional: teacher model for SFT data generation

Unverifiable Dependency: mlx has 4 known advisory(ies) (CVE-2025-62609 (MLX has Wild Pointer Dereference in load_gguf()); CVE-2025-62608 (MLX has heap-buffer-overflow in load()); CVE-2025-62608 (MLX is an array framework for machine learning on Apple silicon. Prior to versio) +1 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

mlx has published advisories, including memory-safety issues, and the unpinned requirement means an affected build could be installed unknowingly. This is somewhat more significant in context because MLX is part of the skill's primary local inference path and may load model artifacts locally.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

mlx-lm is specified only with a minimum version, which allows non-deterministic installs and unreviewed transitive dependency changes. In a local model-loading workflow, this can expose users to regressions or vulnerable releases without notice.

Content

Scanner excerpt · requirements.txt (reported line 18)May include surrounding context.

text
# MLX fine-tuning (Apple Silicon)
mlx>=0.19.0
mlx-lm>=0.19.0

# Optional: teacher model for SFT data generation
openai>=1.0.0

Static analysis

No suspicious patterns detected.