Back to skill

Security audit

Lineage Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill’s course-processing behavior is mostly coherent, but its installer handles existing API-key .env files through an unsafe predictable /tmp backup path, so it needs review before installation.

Install only if you are comfortable sending selected course materials to the transcription, vision, text, and OCR providers you configure. Before installing, patch or avoid the installer’s /tmp .env backup behavior, keep API keys in agent-managed secrets or a private 0600 file, use trusted ffmpeg/ffprobe binaries, and review/pin dependencies in a virtual environment.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tainted flow: 'cmd' from os.getenv (line 268, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
def get_video_duration(video_path: str) -> float:
    cmd = [FFPROBE, "-v", "error", "-show_entries", "format=duration",
           "-of", "default=noprint_wrappers=1:nokey=1", video_path]
    r = subprocess.run(cmd, capture_output=True, text=True)
    return float(r.stdout.strip()) if r.returncode == 0 else 0
Confidence
87% confidence
Finding
The executable path for FFPROBE is sourced from environment variables or PATH and then executed. In environments where attackers can influence .env, process environment, or PATH resolution, this can lead to execution of a malicious binary with the script's privileges.

Tainted flow: 'cmd' from os.getenv (line 126, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
"-y", output_path,
    ]
    try:
        subprocess.run(cmd, capture_output=True, text=True, timeout=1800)
        return os.path.exists(output_path) and os.path.getsize(output_path) > 0
    except Exception as e:
        print(f"  压缩失败: {e}")
Confidence
88% confidence
Finding
This code executes FFmpeg using a binary path derived from environment variables or PATH lookup. If an attacker can control deployment environment variables or the searched PATH, they can cause arbitrary program execution when compression runs.

Tainted flow: 'cmd' from os.getenv (line 126, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
"-y", output_path,
    ]
    try:
        subprocess.run(cmd, capture_output=True, text=True, timeout=1800)
        return os.path.exists(output_path) and os.path.getsize(output_path) > 0
    except Exception as e:
        print(f"  480p 压缩失败: {e}")
Confidence
88% confidence
Finding
The 480p compression path has the same trust-boundary issue: the script executes whatever binary FFMPEG resolves to from environment or PATH. In a shared agent/runtime context, that can become arbitrary code execution rather than mere media processing.

Tainted flow: 'cmd' from os.getenv (line 126, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
cmd = [FFMPEG, "-y", "-ss", str(time_sec), "-i", video_path,
           "-vframes", "1", "-q:v", "2", output_path]
    try:
        subprocess.run(cmd, capture_output=True, text=True, timeout=60)
        return os.path.exists(output_path) and os.path.getsize(output_path) > 0
    except Exception:
        return False
Confidence
88% confidence
Finding
Frame extraction invokes the same attacker-influenceable FFMPEG binary source. Because this path may be reached repeatedly for many screenshots, exploitation could yield reliable repeated arbitrary code execution if environment control is available.

Tainted flow: 'cmd' from os.getenv (line 126, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
"-t", str(dur), "-c", "copy",
               "-avoid_negative_ts", "make_zero", cp]
        try:
            subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            if os.path.exists(cp) and os.path.getsize(cp) > 0:
                chunks.append((cp, start))
                print(f"  片段 {i+1}/{num}: offset={start/60:.0f}min, {os.path.getsize(cp)/(1024*1024):.0f}MB")
Confidence
88% confidence
Finding
Video splitting relies on the same untrusted executable resolution path and therefore inherits the arbitrary-executable risk. The skill context increases concern because batch processing workflows are often automated and may run on large trusted datasets or privileged hosts.

Tainted flow: 'url' from os.getenv (line 71, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
last_error: Exception | None = None
    for model in models:
        try:
            response = requests.post(
                url,
                headers={
                    "Authorization": f"Bearer {api_key}",
Confidence
95% confidence
Finding
The request destination is derived from an environment-controlled base URL and the code sends the Authorization bearer token to that URL. If an attacker can influence environment variables or deployment configuration, they can redirect requests to an attacker-controlled endpoint and exfiltrate the API key and all submitted prompts/data, which is a real SSRF-style secret exfiltration risk rather than a mere external call.

Tainted flow: 'url' from os.getenv (line 71, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
for model in models:
        try:
            print(f"  🚀 图像模型: {model}, images={len(image_paths)}", flush=True)
            response = requests.post(
                url,
                headers={
                    "Authorization": f"Bearer {api_key}",
Confidence
95% confidence
Finding
This image-analysis path also builds the POST target from an environment-supplied base URL and includes the bearer token in the outbound request. A malicious or compromised configuration can cause sensitive images, prompts, and credentials to be transmitted to an attacker-controlled service.

Tainted flow: 'cmd' from os.getenv (line 86, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
"-q:a", "2", "-y", audio_path,
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
        return result.returncode == 0 and os.path.exists(audio_path)
    except Exception as e:
        print(f"  音频提取异常: {e}")
Confidence
81% confidence
Finding
The executable name used in the subprocess command comes from the FFMPEG environment variable, so a user who can influence environment configuration can cause the script to execute an arbitrary binary. In a skill or automation context where environment values may come from external setup or untrusted deployment state, this becomes a real arbitrary code execution risk.

Tainted flow: 'cmd' from os.getenv (line 86, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
audio_path,
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        return float(result.stdout.strip()) if result.returncode == 0 else 0
    except Exception:
        return 0
Confidence
81% confidence
Finding
The FFPROBE executable path is environment-controlled, and subprocess.run will execute whatever binary that variable points to. If an attacker can modify the environment or deployment configuration, they can replace ffprobe with a malicious program and gain code execution under the script's privileges.

Tainted flow: 'cmd' from os.getenv (line 86, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
output_path,
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
        return result.returncode == 0 and os.path.exists(output_path) and os.path.getsize(output_path) > 0
    except Exception as e:
        print(f"  分段音频提取异常: {e}")
Confidence
81% confidence
Finding
This segment extraction path again executes the environment-controlled FFMPEG binary. Repeated use of an untrusted executable path increases exposure because every processed media file triggers execution of that binary.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill instructs the agent to use shell commands, access environment variables for API keys, read and write files, and potentially use network-backed providers, but it does not declare permissions or capability boundaries. This creates a trust and containment problem: a host may expose more capability than the user expects, and the skill can drive sensitive operations such as reading local materials, writing generated artifacts, or invoking external services with secret-backed credentials.

External Transmission

Medium
Category
Data Exfiltration
Content
BASE_DIR = str(Path(__file__).resolve().parents[1])

AUDIO_TRANSCRIBE_API_KEY = os.getenv("AUDIO_TRANSCRIBE_API_KEY", "")
AUDIO_TRANSCRIBE_BASE_URL = os.getenv("AUDIO_TRANSCRIBE_BASE_URL", "https://api.openai.com/v1")
AUDIO_TRANSCRIBE_MODEL = os.getenv("AUDIO_TRANSCRIBE_MODEL", "whisper-1")
FFMPEG = os.getenv("FFMPEG") or shutil.which("ffmpeg") or "ffmpeg"
FFPROBE = os.getenv("FFPROBE") or shutil.which("ffprobe") or "ffprobe"
Confidence
98% confidence
Finding
This script transmits local audio content to an external transcription service, which is a genuine data exfiltration/privacy risk if the media contains sensitive course, learner, or proprietary information. In the context of a learning-content processing skill, this is especially relevant because source materials may include private educational data, copyrighted content, or internal recordings.

Credential Access

High
Category
Privilege Escalation
Content
print_info "Cancelled"
            exit 0
        fi
        if [ -f "$SKILL_DIR/.env" ]; then
            cp "$SKILL_DIR/.env" "/tmp/lineage-skill.env.bak"
            print_info "Backed up existing .env to /tmp/lineage-skill.env.bak"
        fi
Confidence
92% confidence
Finding
The installer copies an existing .env file to a predictable world-accessible temporary path under /tmp before deleting and reinstalling the skill. On multi-user systems this can expose secrets via insecure temporary-file handling, symlink attacks, or unintended reads by other local users/processes.

Credential Access

High
Category
Privilege Escalation
Content
exit 0
        fi
        if [ -f "$SKILL_DIR/.env" ]; then
            cp "$SKILL_DIR/.env" "/tmp/lineage-skill.env.bak"
            print_info "Backed up existing .env to /tmp/lineage-skill.env.bak"
        fi
        rm -rf "$SKILL_DIR"
Confidence
90% confidence
Finding
Logging that the .env was backed up to /tmp/lineage-skill.env.bak confirms the exact location of sensitive material and normalizes storing credentials in an insecure shared temp directory. The main issue is not the log itself but that it documents and supports the unsafe backup pattern.

Credential Access

High
Category
Privilege Escalation
Content
print_success "Files copied"

    if [ -f "/tmp/lineage-skill.env.bak" ]; then
        mv "/tmp/lineage-skill.env.bak" "$SKILL_DIR/.env"
        print_success "Restored existing .env"
    fi
Confidence
89% confidence
Finding
Restoring the .env from a predictable /tmp backup path continues the insecure secret-handling flow. An attacker able to replace or tamper with that temp file could inject malicious configuration or redirect API keys, while local disclosure remains possible if the file was readable.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
python-dotenv>=1.0.0
requests>=2.31.0
pillow>=10.0.0
Confidence
96% confidence
Finding
The dependency uses a lower-bound version specifier instead of an exact pinned version, which makes builds non-reproducible and can unexpectedly pull in newer releases with breaking changes or newly introduced vulnerabilities. In a skill that processes diverse external content, dependency drift increases supply-chain risk even if this line alone does not prove active exploitation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
python-dotenv>=1.0.0
requests>=2.31.0
pillow>=10.0.0
Confidence
97% confidence
Finding
This dependency is not pinned, so installations may resolve to different versions over time, undermining reproducibility and potentially introducing vulnerable releases. The risk is heightened here because python-dotenv is also separately flagged with a known vulnerable version, showing that lax version control can directly expose the environment to known issues.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
python-dotenv>=1.0.0
requests>=2.31.0
pillow>=10.0.0
Confidence
97% confidence
Finding
Using an unpinned requests version allows dependency resolution to vary between environments and over time, which can silently introduce insecure or incompatible behavior. Because this package is used for network access, version drift can materially affect transport security, credential handling, and request validation behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
openai>=1.0.0
python-dotenv>=1.0.0
requests>=2.31.0
pillow>=10.0.0
Confidence
99% confidence
Finding
The package is unpinned and the current minimum version corresponds to a release with multiple serious advisories, including critical issues. Given this skill's likely handling of user-supplied images, PDFs, OCR assets, and other media, uncontrolled Pillow version selection creates meaningful exposure to parser-level vulnerabilities.

Known Vulnerable Dependency: python-dotenv==1.0.0 — 2 advisory(ies): CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)

Medium
Category
Supply Chain
Confidence
88% confidence
Finding
The finding identifies python-dotenv 1.0.0 as having known advisories, including unsafe symlink following during set_key operations that could enable arbitrary file overwrite in affected usage patterns. This becomes more relevant if the skill writes or modifies .env files in workspaces influenced by untrusted content or attacker-controlled paths.

Known Vulnerable Dependency: requests==2.31.0 — 6 advisory(ies): CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi); CVE-2026-25645 (Requests has Insecure Temp File Reuse in its extract_zipped_paths() utility func) +3 more

Medium
Category
Supply Chain
Confidence
90% confidence
Finding
Requests 2.31.0 is flagged with multiple known advisories affecting areas such as credential leakage and request/session security behavior. Since this skill may fetch remote course materials or external resources, a vulnerable HTTP client can expose secrets or weaken transport security when interacting with attacker-controlled URLs.

Known Vulnerable Dependency: pillow==10.0.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
98% confidence
Finding
Pillow 10.0.0 is associated with numerous advisories, including critical image parsing issues such as potential code execution and memory corruption. The skill description explicitly mentions PDFs, OCR, slides, images, and other rich media workflows, so vulnerable image-processing code is especially dangerous because attackers could deliver malicious files through normal skill inputs.

Static analysis

Detected: suspicious.dynamic_code_execution, suspicious.potential_exfiltration

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_select_video_keyframes.py:14

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_transcribe_video_segmenting.py:10

Python code base64-encodes a local file and sends it over the network.

Critical
Code
suspicious.potential_exfiltration
Location
scripts/llm_client.py:173