Skip to content

Security: AgriciDaniel/lipnardo

Security

SECURITY.md

Security Policy

Reporting a vulnerability

Do not open a public GitHub issue. Email agricidaniel@gmail.com with the subject lipnardo security: <short description>.

Include:

  • Affected version / commit SHA
  • Steps to reproduce
  • Impact (what an attacker can do)
  • Suggested remediation if you have one

Expected response: initial acknowledgement within 7 days. Full triage and patch target: 30 days for anything that leads to code execution, credential exfiltration, financial loss (unauthorized API charges), or privilege escalation on the user's machine.

Threat model

lipnardo is a Claude Code skill that calls the HeyGen REST API to generate avatar videos. It runs locally, holds an API key in either environment or ~/.heygen/config.json, and writes outputs to ~/Documents/lipnardo_videos/. It does not start network services, does not call external APIs other than api.heygen.com, and does not upload generated content elsewhere.

The realistic threat surfaces are:

Surface Example risk Mitigation in place
API key exfiltration Key leaked via env dump, logs, or world-readable config API key never logged. ~/.heygen/config.json permission warned on read. Cost ledger uses os.open() with O_CREAT mode 0600 to prevent race-window exposure.
SSRF via download URL Attacker MITMs HeyGen response and substitutes file:///etc/passwd to exfiltrate local files _validate_url() in heygen_client.py rejects non-HTTP(S) schemes and localhost/private hosts before urllib.request.urlopen.
Path traversal via API response Malicious video_id (../../etc/foo) in API response written as filename sanitize_id() regex ^[a-zA-Z0-9_.-]+$ applied to all 6 callers that use IDs as filenames.
Financial denial-of-wallet 5,000-row CSV silently spends $20K on first run --max-batch 500 default cap with explicit override. Cost warning logged before submission. --max-concurrent bounded to 1-10. --max-retries bounded to 0-10.
Negative-cost ledger fraud --duration -60 produces negative cost, hiding real spend _lookup_cost() rejects negative duration.
Multipart filename injection Filename with " or CRLF breaks Content-Disposition header in upload asset_manager.py strips ", \, CR, LF before building multipart body.
Unbounded retry credit burn max_retries=999 with exponential backoff = hours of API hammering Bounded 0-10.
Concurrent ledger corruption Two processes writing to costs.json simultaneously lose entries Atomic write via tmp + os.replace().

Out of scope

  • Vulnerabilities in HeyGen's API itself — report to HeyGen at security@heygen.com.
  • Vulnerabilities in Python's stdlib (urllib, ssl, json) — report upstream.
  • Vulnerabilities in Claude Code itself — report to Anthropic.
  • Generated video content licensing or commercial-use questions — consult HeyGen's terms.
  • Charges incurred from legitimate use of the API — Lipnardo does not refund usage fees.

Past issues (fixed in v1.0)

Documented in git history for transparency. All fixes verified with behavioral tests, not just code-existence checks.

ID Severity Issue Fix
VULN-001 HIGH SSRF via file:// scheme in download_file() URL parameter _validate_url() rejects non-HTTP(S) and localhost
VULN-002 HIGH No batch size cap — 5000-row CSV could spend $20K silently --max-batch 500 default + cost warning + bounds on --max-concurrent (1-10) and --max-retries (0-10)
VULN-003 MEDIUM API-returned video_id used unsanitized in local filenames (path traversal via MITM) sanitize_id() regex applied at 6 callsites
VULN-004 MEDIUM ~/.heygen/ directory + config.json created with default umask (world-readable) os.open() with O_CREAT mode 0600 for ledger; permission warning on config read
VULN-005 MEDIUM Negative --duration produced negative cost (ledger fraud loophole) Validation in _lookup_cost()
VULN-006 MEDIUM max_concurrent=0 caused silent batch no-op (data loss illusion) Bounds check 1-10
VULN-007 MEDIUM Multipart filename in Content-Disposition not escaped Strip ", \, CR, LF before header construction
VULN-008 LOW Cost ledger lacked atomic write (concurrent loss) tmp + os.replace() pattern
API-001 HIGH Translation --mode quality was silently ignored by v2 endpoint (users charged 2x for "speed" mode) Upgraded to /v3/video-translations with explicit speed/precision mapping

The 8-agent cybersecurity audit (OWASP Top 10:2025, CWE Top 25:2024, MITRE ATT&CK) scored v1.0 at 78/100 before fixes. All HIGH and MEDIUM findings remediated. The full audit report is in the repository commit history.

There aren't any published security advisories