Back to skill

Security audit

flashcards-word

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed local flashcard generator with expected network artwork downloads, but users should keep its output directory dedicated because one helper deletes existing PNG files there.

Install only in a normal project or temporary work directory, review the scripts before running them, and leave EMOJI_OUT unset unless it points to a dedicated generated-emoji folder with no valuable PNG files. Prefer pinned dependency versions and a fixed Twemoji release if reproducibility or supply-chain control matters.

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
templates/open-source/requirements.txt:1
Finding

Mutable and Unverified Third-Party Dependencies and Remote Artwork

Content
View full analysis

Vulnerability Details

File Location: templates/open-source/requirements.txt:1-2; scripts/download_emoji.py:20, 31-43
Vulnerability Type: Supply-chain exposure through unpinned dependencies and mutable, unverified remote assets
Risk Level: Medium

Complete Code Snippets

From templates/open-source/requirements.txt:

text
Pillow>=9
python-docx>=0.8

From scripts/download_emoji.py:

python
BASE = "https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets"

def curl(out, url):
    return subprocess.run(
        ["curl", "-sL", "-o", out, "-w", "%{http_code}",
         "--max-time", "25", url],
        capture_output=True,
        text=True
    ).stdout.strip()

def get_png(cp):
    final, svg = f"{OUT}/{cp}.png", f"{OUT}/{cp}.svg"
    if curl(svg, f"{BASE}/svg/{cp}.svg") == "200" and os.path.getsize(svg) > 100:
        subprocess.run(
            ["rsvg-convert", "-w", "256", "-h", "256", "-o", final, svg],
            capture_output=True
        )
        os.remove(svg)
        if os.path.exists(final) and os.path.getsize(final) > 150:
            return "svg256"
    if os.path.exists(svg):
        os.remove(svg)
    if curl(final, f"{BASE}/72x72/{cp}.png") == "200" and os.path.getsize(final) > 100:
        return "png72"
    if os.path.exists(final):
        os.remove(final)
    return "FAIL"

Technical Analysis

The Python requirements specify only minimum versions, with no upper bounds, exact versions, lock file, or package hashes. Consequently, identical installation commands can resolve to different code over time.

The downloader also obtains artwork through the mutable @latest Twemoji reference. It validates only the HTTP status and minimum file size; it does not authenticate the expected asset using a digest or signature. The downloaded SVG is then passed directly to the native rsvg-convert parser.

The code does not download an ...[truncated 1355 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin exact, reviewed Python package versions instead of using unrestricted lower bounds.
  2. Generate and commit a lock file containing hashes, or install with a hash-enforcing requirements file using pip --require-hashes.
  3. Use a trusted dependency update process that reviews release notes and security advisories before changing pins.
  4. Replace the Twemoji @latest reference with an immutable commit SHA or reviewed release tag.
  5. Maintain an allowlist manifest of expected asset paths and SHA-256 digests, then reject downloads whose digest does not match.
  6. Run SVG conversion in a restricted environment with minimal filesystem access, no unnecessary network access, resource limits, and an unprivileged account.
  7. Check the return code from rsvg-convert and reject malformed output rather than relying only on file size.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/download_emoji.py:19
Finding

Environment-Controlled Output Directory Can Cause Deletion of Unrelated PNG Files

Content
View full analysis

Vulnerability Details

File Location: scripts/download_emoji.py:19-25
Vulnerability Type: Unrestricted destructive cleanup of a user-selected directory
Risk Level: Medium

Complete Code Snippet

python
OUT = os.environ.get("EMOJI_OUT", os.path.join(HERE, "emoji"))
BASE = "https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets"
os.makedirs(OUT, exist_ok=True)
for f in os.listdir(OUT):
    p = os.path.join(OUT, f)
    if f.endswith(".png") and os.path.isfile(p):
        os.remove(p)

Technical Analysis

The EMOJI_OUT environment variable can select any filesystem directory accessible to the invoking user. At startup, the downloader enumerates that directory and deletes every top-level filename ending in .png.

The cleanup operation does not verify that the directory was created by this project, require a marker file, consult the generated manifest, or restrict deletion to known Twemoji filenames. Therefore, a mistaken, inherited, or attacker-influenced environment value can turn normal pipeline execution into destructive deletion of unrelated images.

The deletion is limited to regular files at the selected directory's top level and does not recursively traverse subdirectories. Symbolic links are not deleted because os.path.isfile() follows the link and os.remove() removes the link itself, but unrelated regular PNG files remain exposed.

Attack Path

  1. EMOJI_OUT is set to an existing shared directory containing valuable PNG files, whether by user error, shell configuration, CI configuration, or an attacker who can influence the process environment.
  2. The user runs python3 scripts/download_emoji.py as documented.
  3. The script lists the selected directory before downloading any assets.
  4. Every top-level regular file whose name ends with .png is removed.
  5. The original files may be unrecoverable unless backups or filesystem recovery are available.

Impact Asse

...[truncated 479 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require the output directory to be dedicated to generated artifacts.
  2. Create a project-specific marker file when initializing the directory and refuse cleanup if the marker is absent.
  3. Record generated files in _final.json or a separate manifest and delete only files listed in the previous manifest.
  4. Validate the canonical output path and reject dangerous or overly broad locations such as the filesystem root, the user's home directory, or pre-existing unmarked directories.
  5. Prefer generating into a new temporary staging directory and atomically replacing the previous generated directory after successful completion.
  6. Add an explicit cleanup confirmation or opt-in flag when a custom EMOJI_OUT points to a nonempty directory.
  7. Document that EMOJI_OUT must not reference a shared asset directory.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is presented as a printable flashcard generator, but the documented behavior also includes downloading remote assets and relying on external tools and network access that are not clearly surfaced as primary behaviors. Description-behavior mismatch can mislead agents or users into approving execution they would not otherwise allow, especially when network and shell operations are involved.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 194)May include surrounding context.

md
**copy `templates/open-source/*` to the repo root** when publishing, alongside `SKILL.md`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 222)May include surrounding context.

md
**copy `templates/open-source/*` to the repo root** when publishing, alongside `SKILL.md`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 206)May include surrounding context.

md
├── requirements.txt # <- templates/open-source/requirements.txt

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 220)May include surrounding context.

md
├── requirements.txt # <- templates/open-source/requirements.txt

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill instructs use of shell commands, environment variables, file reads/writes, and external binaries, yet the manifest declares no tool restrictions or permissions. This increases the chance an agent will run filesystem and shell operations without explicit scoping, expanding blast radius if the skill is invoked in an unsafe context.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest description says the skill 'works for BOTH Chinese ... and English', but the documentation later states the bundled scripts ship only the English path and directs users to a separate skill for Chinese support. This is a direct scope mismatch between the advertised capability and the actual packaged implementation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The overview first says the skill works for Chinese with a pinyin-aware variant, but immediately clarifies that the bundled scripts only provide the English path and that users should use another skill for the Chinese variant. That creates active documentation-level contradiction about whether this skill itself supports Chinese generation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The helper script performs external downloads and purges existing PNG files in the target directory, behavior that goes beyond simple local document generation and can surprise users or agents invoking the skill. In a skill ecosystem, undisclosed network access and destructive cache cleanup increase the blast radius if the script is run in a shared workspace.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script deletes every PNG file in the output directory on startup without confirmation and with OUT partially controlled by an environment variable. If OUT points to a broader or shared directory, this can cause unintended data loss unrelated to emoji assets.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

For a flashcard-generation skill, spawning subprocesses to run curl and rsvg-convert is not an obvious or justified capability from the manifest alone. This expands the operational surface from document/image processing in Python to shell-level tooling with network access and host dependencies.

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

python
if ord(c) not in (0xfe0f, 0x200d, 0x20e3))

def curl(out, url):
    return subprocess.run(["curl","-sL","-o",out,"-w","%{http_code}","--max-time","25",url],
                          capture_output=True, text=True).stdout.strip()

def get_png(cp):

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

python
def get_png(cp):
    final, svg = f"{OUT}/{cp}.png", f"{OUT}/{cp}.svg"
    if curl(svg, f"{BASE}/svg/{cp}.svg") == "200" and os.path.getsize(svg) > 100:
        subprocess.run(["rsvg-convert","-w","256","-h","256","-o",final,svg], capture_output=True)
        os.remove(svg)
        if os.path.exists(final) and os.path.getsize(final) > 150:
            return "svg256"

Tainted flow: 'final' from os.environ.get (line 36, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/download_emoji.py (reported line 38)May include surrounding context.

python
def get_png(cp):
    final, svg = f"{OUT}/{cp}.png", f"{OUT}/{cp}.svg"
    if curl(svg, f"{BASE}/svg/{cp}.svg") == "200" and os.path.getsize(svg) > 100:
        subprocess.run(["rsvg-convert","-w","256","-h","256","-o",final,svg], capture_output=True)
        os.remove(svg)
        if os.path.exists(final) and os.path.getsize(final) > 150:
            return "svg256"

Tainted flow: 'OUT' from os.environ.get (line 19, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
90% confidence
Finding

The script trusts EMOJI_OUT from the environment and writes files into that directory without validating that it is a safe, expected workspace. In agent or automation contexts, an attacker who can influence environment variables could cause file creation or overwrite in unintended locations, especially because the script also deletes PNGs in the same directory earlier.

Content

Scanner excerpt · scripts/download_emoji.py (reported line 74)May include surrounding context.

python
report[w] = 1
if report: print("WARN unresolved:", list(report))

json.dump(final_map, open(f"{OUT}/_final.json", "w"), ensure_ascii=False, indent=1)

def md5(p): return hashlib.md5(open(p, "rb").read()).hexdigest()
seen, dups = {}, []

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest describes the skill as a self-contained, open-source script set for generating printable flashcards. The Makefile explicitly runs a download step, indicating the build depends on fetching external assets rather than operating entirely from local resources.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The skill's purpose is to create Word flashcards for children, which is primarily a local document-generation task. Triggering a download step introduces network-dependent behavior that is not explicitly justified in the manifest, especially since the description emphasizes a self-contained script set.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.md (reported line 39)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.md (reported line 58)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.md (reported line 209)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.zh.md (reported line 36)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.zh.md (reported line 53)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.zh.md (reported line 193)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · templates/open-source/README.md (reported line 39)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · templates/open-source/README.md (reported line 58)May include surrounding context.

md
- **Python** 3.9+
- **Python 依赖:** `pip install -r requirements.txt` → 安装 `Pillow`、`python-docx`
- **`rsvg-convert`**(把 emoji 的 SVG 转成清晰 PNG)。安装:
  - Debian/Ubuntu:`sudo apt-get install -y librsvg2-bin`
  - macOS:`brew install librsvg`
- **`curl`**(多数系统自带)
- **一个能读粗体无衬线字体的字体**(默认 DejaVu Sans Bold;Liberation / Noto Sans 亦可)。用 `fc-list | grep -i sans` 查找。

Static analysis

No suspicious patterns detected.