T08 · Insecure Dependencies
- Location
scripts/generate.py:20- Finding
Unverified Mutable Font Dependency and Unsafe Shared Cache
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate.py, lines 20–29
Vulnerability Type: Unverified third-party dependency stored in an unsafe shared temporary location
Risk Level: Mediumpython FONT_URL = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/SimplifiedChinese/NotoSansCJKsc-Bold.otf" FONT_CACHE = Path(tempfile.gettempdir()) / "NotoSansCJKsc-Bold.otf" FONT_SHA256 = None # skip hash check for now def ensure_font(): if FONT_CACHE.exists() and FONT_CACHE.stat().st_size > 1_000_000: return str(FONT_CACHE) print(f"Downloading CJK font to {FONT_CACHE} ...") subprocess.run(["curl", "-sL", FONT_URL, "-o", str(FONT_CACHE), "--max-time", "120"], check=True) return str(FONT_CACHE)Technical Analysis
The font is downloaded from the mutable
mainbranch of an external repository and is not verified against a known cryptographic digest. AlthoughFONT_SHA256is declared, it is set toNoneand never used. Consequently, changes to the remote branch or compromise of the upstream delivery path could cause the program to process content that was not present during the audit.The cache is also stored under a predictable filename in the system-wide temporary directory. An existing cache entry is trusted solely because it exists and is larger than one megabyte. The code does not verify its digest, ownership, permissions, regular-file status, or whether it is a symbolic link. The returned file is subsequently parsed by Pillow through
ImageFont.truetype()in all three cover-generation paths.This creates a local cache-poisoning and race-condition opportunity. An attacker with write access to the shared temporary directory could place or replace the predictable file before the program runs. The download itself is written directly to the final cache path rather than securely downloaded to a private temporary file and atomically installed after verification.
Attack Path
- A local attacker identi ...[truncated 1798 chars]
- Remediation
View remediation
Remediation Suggestions
- Pin the font URL to an immutable release artifact or exact repository commit rather than the mutable
mainbranch. - Define an expected SHA-256 digest in source and verify every downloaded or cached font before use. Delete and reject any file whose digest does not match.
- Store the cache in a user-private cache directory with restrictive permissions instead of a shared system temporary directory.
- If temporary storage is required, create a private directory with mode
0700and use securely generated filenames. - Reject symbolic links and non-regular files. Where supported, open files with no-follow and exclusive-creation semantics.
- Download into a securely created temporary file, validate HTTP success, enforce reasonable size limits, calculate the digest, and atomically rename the verified file into the cache.
- Add
curl --failor use a Python HTTP client that explicitly validates status codes, TLS, response size, and timeouts. - Handle download and validation failures without passing partial or untrusted content to Pillow.
- Consider packaging the verified font with the skill if licensing and package-size constraints permit, eliminating the runtime dependency.
- Pin the font URL to an immutable release artifact or exact repository commit rather than the mutable
