Back to skill

Security audit

persian-pdf-studyguide-forge

Security checks across malware telemetry and agentic risk

Overview

The skill appears to do its advertised PDF-to-study-guide work, but it can send document text through automatically discovered or custom AI providers using ambient API keys and cache prompt content outside the workspace.

Install only in a dedicated workspace. Use FORGE_MOCK=1 or a trusted local model for private PDFs, or explicitly review the provider list, base URLs, headers, and API-key environment variables before enabling remote AI. Set FORGE_CACHE_DIR to a controlled location or purge the default cache after sensitive runs, and prefer pinned dependencies or a locked environment.

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
scripts/model_adapters.py:303
Finding
Custom provider endpoints can expose API credentials and source-document content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/model_adapters.py`, lines 303–315 and 733–766 **Vulnerability Type**: Unrestricted provider endpoint and insecure transport configuration **Risk Level**: High ### Complete Code Snippet ```python base = p.get("base_url") or _DEFAULT_BASES.get(p.get("name", ""), "") info = ProviderInfo( name=p.get("name") or dialect, dialect=dialect, model=p.get("model", ""), base_url=base, api_key_env=p.get("api_key_env", ""), alt_models=list(p.get("alt_models", []) or []), headers=dict(p.get("headers", {}) or {}), supports_system=p.get("supports_system"), supports_temperature=p.get("supports_temperature"), supports_seed=p.get("supports_seed"), supports_json_mode=p.get("supports_json_mode"), max_tokens_field=p.get("max_tokens_field"), context=int(p.get("context", 0) or 0), weight=int(p.get("weight", 60) or 60), notes=p.get("notes", ""), ) ``` ```python def _b_openai(p, model, prompt, system, max_tokens, json_mode, seed, caps): field_name = caps.get("max_tokens_field") or p.max_tokens_field or "max_tokens" msgs = [] if system and caps.get("supports_system", p.supports_system) is not False: msgs.append({"role": "system", "content": system}) user = prompt else: user = (system + "\n\n" + prompt) if system else prompt msgs.append({"role": "user", "content": user}) body: Dict[str, Any] = {"model": model, "messages": msgs, field_name: max_tokens} if caps.get("supports_stream_field", True) is not False: body["stream"] = False if caps.get("supports_temperature", p.supports_temperature) is not False: body["temperature"] = 0 if caps.get("supports_top_p", True) is not False: body["top_p"] = 1 if seed is not None and caps.get("supports_seed", p.supports_seed) is not False: body["seed"] = seed if json_mode and caps.get("supports_json_mode", p.supports_json_mode) i ...[truncated 3373 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for all non-loopback model endpoints. 2. Permit plain HTTP only for verified loopback destinations such as `127.0.0.1`, `[::1]`, or a deliberately approved local Unix-socket bridge. 3. Maintain an allowlist of expected origins for built-in providers. 4. Require an explicit, prominently logged confirmation before using a custom provider origin. 5. Validate URLs and reject user-info components, fragments, unsupported schemes, malformed hosts, and unexpected ports. 6. Disable redirects for authenticated requests, or revalidate every redirect destination and strip authorization headers whenever the origin changes. 7. Block link-local, metadata-service, and private-network destinations by default for non-local provider configurations. 8. Restrict `api_key_env` to recognized variables unless the operator explicitly approves a custom variable. 9. Prevent arbitrary custom headers from overriding security-sensitive headers such as `Authorization`, `Host`, and provider authentication headers. 10. Before the first request, display the destination origin and a clear warning that source-document content will leave the local machine. 11. Add automated tests covering HTTP rejection, cross-origin redirects, authorization-header stripping, and malicious custom provider configurations. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Open-ended dependency constraints make installations non-reproducible<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, lines 1–3 **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Complete Code Snippet ```text beautifulsoup4>=4.12 PyMuPDF>=1.24 Pillow>=10.0 ``` ### Technical Analysis All Python dependencies use open-ended lower-bound constraints. A future installation may therefore resolve to any newer release, including versions that were not reviewed or tested with this Skill. The project documentation recommends installing these dependencies through `pip install -r requirements.txt`. Because no exact versions or package hashes are supplied, identical source packages can produce materially different environments over time. This is a supply-chain hardening weakness rather than evidence that any currently named package is malicious. The package names correspond to expected libraries for HTML processing, PDF handling, and image processing, but the constraints do not provide reproducible or integrity-verified resolution. ### Attack Path 1. An operator follows the documented dependency installation procedure. 2. Pip queries the configured package index and selects the newest versions satisfying the lower bounds. 3. A future compromised, malicious, or incompatible release satisfies those constraints. 4. Pip downloads and installs that release without comparing it against a project-approved hash. 5. Package installation or later import executes or exposes the unreviewed dependency code within the Skill's process context. Exploitation depends on compromise of the configured package source, a malicious future release, or an unintended incompatible release; no such compromise was established during this audit. ### Impact Assessment A compromised dependency would execute with the same operating-system privileges as the user installing or running the Skill. Depending on that user's access, it could read source PDFs and generated artifacts, access environment variables con ...[truncated 295 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin each runtime dependency to an exact, reviewed version. 2. Generate a lock file using a controlled dependency-resolution process. 3. Record hashes for all direct and transitive packages and install with `pip --require-hashes`. 4. Use a trusted package index and explicitly configure the expected index origin in deployment guidance. 5. Separate required and optional dependencies so installations receive only the minimum components needed. 6. Add automated dependency vulnerability scanning and license checks to the release process. 7. Review and deliberately update pinned versions on a scheduled basis instead of accepting all future releases automatically. 8. Test locked dependency sets against the supported Python versions before publishing a new Skill release. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (18)

External Transmission

Medium
Category
Data Exfiltration
Content
# Default base URLs per well-known provider name.
_DEFAULT_BASES = {
    "openai": "https://api.openai.com/v1",
    "openrouter": "https://openrouter.ai/api/v1",
    "groq": "https://api.groq.com/openai/v1",
    "mistral": "https://api.mistral.ai/v1",
Confidence
90% confidence
Finding
This module is explicitly designed to send prompts to external model providers, and the surrounding code auto-discovers credentials from environment variables and posts prompt content over the network. In an agent-skill context, that creates a real exfiltration risk because host-provided documents or prompts may be transmitted to third-party services without strict operator pinning or egress controls.

External Transmission

Medium
Category
Data Exfiltration
Content
_DEFAULT_BASES = {
    "openai": "https://api.openai.com/v1",
    "openrouter": "https://openrouter.ai/api/v1",
    "groq": "https://api.groq.com/openai/v1",
    "mistral": "https://api.mistral.ai/v1",
    "deepseek": "https://api.deepseek.com/v1",
    "together": "https://api.together.xyz/v1",
Confidence
90% confidence
Finding
The presence of Groq as a built-in remote endpoint is part of a generalized outbound LLM transport layer. Because this skill can process lecture PDFs and prompts, sending that content to remote inference APIs can leak sensitive or proprietary material if the host environment exposes API keys and the operator did not intend external processing.

External Transmission

Medium
Category
Data Exfiltration
Content
"openai": "https://api.openai.com/v1",
    "openrouter": "https://openrouter.ai/api/v1",
    "groq": "https://api.groq.com/openai/v1",
    "mistral": "https://api.mistral.ai/v1",
    "deepseek": "https://api.deepseek.com/v1",
    "together": "https://api.together.xyz/v1",
    "fireworks": "https://api.fireworks.ai/inference/v1",
Confidence
90% confidence
Finding
Mistral is another built-in external transmission destination in a component whose job is to normalize remote model calls. In this skill context, that is a meaningful data exposure issue because the adapter can silently route study-guide source content to third-party APIs based on ambient environment configuration.

External Transmission

Medium
Category
Data Exfiltration
Content
"openrouter": "https://openrouter.ai/api/v1",
    "groq": "https://api.groq.com/openai/v1",
    "mistral": "https://api.mistral.ai/v1",
    "deepseek": "https://api.deepseek.com/v1",
    "together": "https://api.together.xyz/v1",
    "fireworks": "https://api.fireworks.ai/inference/v1",
    "xai": "https://api.x.ai/v1",
Confidence
90% confidence
Finding
DeepSeek is configured as a default outbound endpoint, contributing to the same real exfiltration surface as the other providers. Since this skill advertises agent-agnostic portability and environment auto-discovery, it may run in hosts where API keys are present for unrelated reasons, causing unplanned remote transmission.

External Transmission

Medium
Category
Data Exfiltration
Content
"groq": "https://api.groq.com/openai/v1",
    "mistral": "https://api.mistral.ai/v1",
    "deepseek": "https://api.deepseek.com/v1",
    "together": "https://api.together.xyz/v1",
    "fireworks": "https://api.fireworks.ai/inference/v1",
    "xai": "https://api.x.ai/v1",
    "zai": "https://api.z.ai/api/paas/v4",
Confidence
90% confidence
Finding
Together is a remote inference destination in a module that can send arbitrary prompts and extracted content. In a security review of an agent skill, that is a true vulnerability because it expands outbound data channels and may disclose confidential inputs to external infrastructure outside the user's intended processing boundary.

External Transmission

Medium
Category
Data Exfiltration
Content
"mistral": "https://api.mistral.ai/v1",
    "deepseek": "https://api.deepseek.com/v1",
    "together": "https://api.together.xyz/v1",
    "fireworks": "https://api.fireworks.ai/inference/v1",
    "xai": "https://api.x.ai/v1",
    "zai": "https://api.z.ai/api/paas/v4",
    "llm7": "https://api.llm7.io/v1",
Confidence
90% confidence
Finding
Fireworks is another hardcoded external API base in a module built for automatic model failover and provider discovery. That design increases the chance that sensitive prompt data will be transmitted externally as part of retries or fallback behavior, which is risky in agent environments handling user files.

External Transmission

Medium
Category
Data Exfiltration
Content
"deepseek": "https://api.deepseek.com/v1",
    "together": "https://api.together.xyz/v1",
    "fireworks": "https://api.fireworks.ai/inference/v1",
    "xai": "https://api.x.ai/v1",
    "zai": "https://api.z.ai/api/paas/v4",
    "llm7": "https://api.llm7.io/v1",
    "cerebras": "https://api.cerebras.ai/v1",
Confidence
90% confidence
Finding
xAI is included as a default remote endpoint, and the adapter layer can send prompts there using ambient keys. In the context of PDF study-guide generation, that makes data egress a real concern because the content being processed may be private educational or corporate material.

External Transmission

Medium
Category
Data Exfiltration
Content
"together": "https://api.together.xyz/v1",
    "fireworks": "https://api.fireworks.ai/inference/v1",
    "xai": "https://api.x.ai/v1",
    "zai": "https://api.z.ai/api/paas/v4",
    "llm7": "https://api.llm7.io/v1",
    "cerebras": "https://api.cerebras.ai/v1",
    "nvidia": "https://integrate.api.nvidia.com/v1",
Confidence
89% confidence
Finding
Z.AI is one of many predefined outbound targets in a flexible multi-provider transport layer. The vulnerability is not the URL literal itself, but the skill's ability to automatically use such endpoints and send content to them, potentially outside the operator's awareness.

External Transmission

Medium
Category
Data Exfiltration
Content
"fireworks": "https://api.fireworks.ai/inference/v1",
    "xai": "https://api.x.ai/v1",
    "zai": "https://api.z.ai/api/paas/v4",
    "llm7": "https://api.llm7.io/v1",
    "cerebras": "https://api.cerebras.ai/v1",
    "nvidia": "https://integrate.api.nvidia.com/v1",
    "perplexity": "https://api.perplexity.ai",
Confidence
88% confidence
Finding
llm7 is configured as an external service destination in the same generalized outbound adapter. Because the module supports retries, failover, and autodiscovery, it can broaden the exfiltration surface beyond a single intended vendor and increase the chance of unintended disclosure.

External Transmission

Medium
Category
Data Exfiltration
Content
"xai": "https://api.x.ai/v1",
    "zai": "https://api.z.ai/api/paas/v4",
    "llm7": "https://api.llm7.io/v1",
    "cerebras": "https://api.cerebras.ai/v1",
    "nvidia": "https://integrate.api.nvidia.com/v1",
    "perplexity": "https://api.perplexity.ai",
    "hf": "https://router.huggingface.co/v1",
Confidence
88% confidence
Finding
Cerebras is another remote endpoint that this skill may contact with prompt data. In an agent-skill context, any built-in mechanism that transmits potentially sensitive file-derived content to third parties is a true security concern even if the feature is intentional.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
@property
    def key(self) -> str:
        if self.dialect in ("mock", "ollama"):
            return os.environ.get(self.api_key_env, "") if self.api_key_env else ""
        return os.environ.get(self.api_key_env, "")

    @property
Confidence
96% confidence
Finding
This code reads API keys from environment variables dynamically based on provider metadata, which is a real credential-harvesting capability in an agent environment. Although intended for legitimate provider auth, it leverages ambient host secrets and combines with automatic provider discovery to use credentials that may belong to unrelated tooling or contexts.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def key(self) -> str:
        if self.dialect in ("mock", "ollama"):
            return os.environ.get(self.api_key_env, "") if self.api_key_env else ""
        return os.environ.get(self.api_key_env, "")

    @property
    def usable(self) -> bool:
Confidence
96% confidence
Finding
This second environment lookup is the normal path for fetching a provider key, but it still constitutes sensitive secret access. In a portable skill that advertises host-environment autodiscovery, this increases risk because merely running the skill can activate external accounts and authorize outbound transmission using secrets the host happened to expose.

Unpinned Dependencies

Low
Category
Supply Chain
Content
beautifulsoup4>=4.12
PyMuPDF>=1.24
Pillow>=10.0
Confidence
90% confidence
Finding
The dependency specification uses a lower-bound only constraint (`beautifulsoup4>=4.12`), which allows future unreviewed versions to be installed. This creates supply-chain and reproducibility risk because builds may silently pick versions with breaking changes or newly introduced vulnerabilities.

Unpinned Dependencies

Low
Category
Supply Chain
Content
beautifulsoup4>=4.12
PyMuPDF>=1.24
Pillow>=10.0
Confidence
92% confidence
Finding
`PyMuPDF>=1.24` is not pinned, so deployments may resolve to different versions over time. For a PDF-processing skill that handles complex document inputs, unpinned parser dependencies increase risk of consuming a later vulnerable or incompatible release without review.

Unpinned Dependencies

Low
Category
Supply Chain
Content
beautifulsoup4>=4.12
PyMuPDF>=1.24
Pillow>=10.0
Confidence
97% confidence
Finding
`Pillow>=10.0` is unpinned and also governs image parsing for OCR/document workflows, which commonly process attacker-controlled content. This makes the issue more dangerous than a generic version-drift problem because future or currently allowed vulnerable image-library releases could be installed automatically.

Known Vulnerable Dependency: Pillow==10.0 — 10 advisory(ies): CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2024-28219 (Pillow buffer overflow vulnerability); CVE-2026-55379 (Pillow `BdfFontFile`: `Image.new()` called without `_decompression_bomb_check()`) +7 more

Critical
Category
Supply Chain
Confidence
82% confidence
Finding
The finding indicates Pillow 10.0 is associated with multiple advisories, and this skill processes PDFs and rendered page images, meaning image/font parsing paths are plausibly reachable with untrusted document content. In this context, a vulnerable Pillow version could expose the host to denial of service, memory corruption, or potentially code execution depending on the affected decoder or font handler.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"--title",
        "--maximum",
        "--auto-sessions",
        "--no-verify",
        "--providers"
      ]
    }
Confidence
92% confidence
Finding
The manifest exposes a `run` flag `--no-verify` that disables the pipeline's independent answer-verification stage while still producing downstream study-guide artifacts. In a skill whose value proposition includes fidelity and verified educational outputs, letting callers bypass verification can materially reduce trust guarantees and enable unreviewed or hallucinated content to be packaged as if it were acceptable output.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
ap.add_argument("--maximum", action="store_true", help="maximum enrichment mode")
    ap.add_argument("--auto-sessions", action="store_true",
                    help="accept detected session boundaries without human review")
    ap.add_argument("--no-verify", action="store_true", help="skip flashcard verification")
    ap.add_argument("--mock", action="store_true", help="include the offline mock provider")
    ap.add_argument("--only", help="comma-separated provider/dialect filter")
    ap.add_argument("--limit", type=int, default=8, help="max providers for compat/reproduce")
Confidence
81% confidence
Finding
The --no-verify flag allows operators or integrating agents to skip the flashcard verification stage, weakening an integrity control in a pipeline that claims fidelity and QA guarantees. In hostile or careless integrations, this can enable unverified, hallucinated, or manipulated study content to be packaged as if it passed normal checks, especially because the tool also supports automatic end-to-end execution.

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

No suspicious patterns detected.