T08 · Insecure Dependencies
- 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: MediumComplete Code Snippets
From
templates/open-source/requirements.txt:text Pillow>=9 python-docx>=0.8From
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
@latestTwemoji 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 nativersvg-convertparser.The code does not download an ...[truncated 1355 chars]
- Remediation
View remediation
Remediation Suggestions
- Pin exact, reviewed Python package versions instead of using unrestricted lower bounds.
- Generate and commit a lock file containing hashes, or install with a hash-enforcing requirements file using
pip --require-hashes. - Use a trusted dependency update process that reviews release notes and security advisories before changing pins.
- Replace the Twemoji
@latestreference with an immutable commit SHA or reviewed release tag. - Maintain an allowlist manifest of expected asset paths and SHA-256 digests, then reject downloads whose digest does not match.
- Run SVG conversion in a restricted environment with minimal filesystem access, no unnecessary network access, resource limits, and an unprivileged account.
- Check the return code from
rsvg-convertand reject malformed output rather than relying only on file size.
