Back to skill

Security audit

Langfuse Backup

Security checks for vulnerabilities and agentic risk

Overview

The skill is for local Langfuse backups and restores, but its destructive restore workflow uses weakly validated inputs and mutable Docker images, so it should be reviewed before installation.

Install only if you are comfortable reviewing and hardening the scripts first. Pin or pre-vet the alpine image, restrict backup directory permissions, validate backup dates and database names, require and verify manifests or hashes, and test restores on disposable data before using it on a real Langfuse deployment.

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 (4)

T08 · Insecure Dependencies

Warning
Location
scripts/backup_langfuse.sh:58
Finding

Unpinned Docker Image May Retrieve and Execute Unreviewed Code

Content
View full analysis
/dev/null ``` ```bash # scripts/restore_langfuse.sh:125-128 docker run --rm \ -v "$MINIO_VOL":/data \ -v "$BACKUP_DIR":/backup:ro \ alpine sh -c "rm -rf /data/* && tar xzf /backup/minio-data.tar.gz -C /data" ``` ```yaml # SKILL.md:8-9 network: outbound: false reason: "Backs up local Docker volumes only. No data is sent to remote servers." ``` ### Technical Analysis Both scripts execute the mutable `alpine` image without specifying an immutable digest. If the image is not present locally, Docker can retrieve it from the configured registry. Consequently, the code that runs is not fully fixed by the reviewed project and may change when the registry tag is updated or if the registry configuration is compromised. This behavior also conflicts with the metadata assertion that outbound network access is not required. During backup, the container receives read access to the MinIO volume and write access to the backup directory. During restoration, it receives write access to the MinIO volume and read access to the selected backup directory. ### Attack Path 1. An attacker compromises or controls the Docker registry, registry mirror, DNS path, or local Docker image associated with the mutable `alpine` tag. 2. The expected image is absent locally, or the local mutable tag is replaced. 3. An operator executes the backup or restore script. 4. Docker retrieves or starts the attacker-controlled image. 5. The image reads sensitive MinIO content, modifies the backup, or corrupts the writable MinIO volum ...[truncated 478 chars]
Remediation
View remediation
`. - Preload and verify the image before executing either script. - Use `docker run --pull=never` so the operation fails rather than silently retrieving an image. - Document any required image retrieval accurately in `SKILL.md`. - Consider replacing the container dependency with trusted host utilities when practical. - Retain read-only mounts wherever possible and limit writable mounts to the narrowest required directory. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/restore_langfuse.sh:99
Finding

SQL Injection Through Unvalidated Database Name

Content
View full analysis
/dev/null || true docker exec "$DB_CONTAINER" psql -U "$DB_USER" postgres \ -c "CREATE DATABASE $DB_NAME;" 2>/dev/null || true ``` ### Technical Analysis `DB_NAME` is populated from the `LANGFUSE_DB_NAME` environment variable and interpolated directly into SQL statements. Shell quoting protects the shell command structure but does not quote or validate the value as a PostgreSQL identifier. A value containing SQL delimiters can terminate the intended statement and append additional SQL. The injected statements execute through `psql` using `DB_USER` against the `postgres` database. The two commands also suppress errors and continue because of `|| true`, potentially concealing failed or partially malicious operations before restoration proceeds. ### Attack Path 1. An attacker gains influence over the restore process environment, a service definition, a scheduled execution configuration, or an operator-provided shell configuration. 2. The attacker assigns a crafted value to `LANGFUSE_DB_NAME`, such as a value containing a semicolon followed by an additional SQL statement. 3. The operator starts the restore script and confirms the destructive restoration. 4. The script inserts the value into the `DROP DATABASE` and `CREATE DATABASE` command strings. 5. PostgreSQL parses and executes any appended statements with the privileges of `LANGFUSE_DB_USER`. ### Impact Assessment The attacker can execute SQL with the database privileges assigned to the configured PostgreSQL user. Depending on that role's permissions, impact may include dropping or modifying other databases, deleting or altering application data, ch ...[truncated 215 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/restore_langfuse.sh:31
Finding

Untrusted Backup Selection and Restore Without Integrity Validation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/backup_langfuse.sh:29
Finding

Sensitive Backup Files and Predictable Temporary Log Use Inherited Permissions

Content
View full analysis
/dev/null \ | gzip > "$BACKUP_DIR/postgres-langfuse.sql.gz" SIZE=$(du -sh "$BACKUP_DIR/postgres-langfuse.sql.gz" | cut -f1) log "✅ Postgres: $SIZE" ``` ### Technical Analysis The script does not set a restrictive `umask` or explicitly assign secure modes to backup directories and files. As a result, the permissions of database dumps, MinIO archives, and manifests depend on the invoking process's environment. With a permissive umask, other local users may be able to read sensitive Langfuse traces, scores, evaluations, or uploaded blobs. The log uses a fixed path in the shared `/tmp` directory and appends through `tee`. The script does not securely create the file or check whether it is a symbolic link. A local attacker may pre-create the path or point it to another file. The practical write target remains constrained by the invoking user's filesystem permissions, but elevated executions increase the impact. ### Attack Path 1. The backup runs under an account with a permissive umask, causing generated backup artifacts to be readable by other local users; or a local attacker pre-creates `/tmp/langfuse-backup.log` as a file or symbolic link. 2. The operator executes the backup script. 3. Sensitive backup artifacts are created with inherited permissions, allowing unauthorized local reads. 4. In the log-path scenario, `tee -a` follows the attacker-controlled path and appends log content to a target writable by the backu ...[truncated 468 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code clearly aligns with part of the description: it performs backups for a self-hosted Langfuse deployment, specifically Postgres and MinIO, and it includes 14-day retention pruning by default. However, the declared purpose claims broader functionality than the code chunk actually provides. The chunk contains only a backup script; there is no restore logic or validation workflow. It also states optional ClickHouse and Redis coverage, but this script does not back up either service—it only comments that they are optional/transient. This is a description-to-behavior mismatch due to missing declared capabilities in the provided code chunk, though there is no evidence of unrelated or malicious behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The description overstates the supplied code chunk's scope. The code clearly implements Langfuse restore operations only, specifically for Postgres and MinIO. It includes validation and destructive restore behavior, but there is no backup logic, no cron-oriented retention pruning, and no optional clickhouse or redis support in this chunk. Additionally, Postgres handling is database-level SQL restore rather than direct Docker volume restore. While some parts align with the declared purpose (self-hosted Langfuse restore, validation, Postgres/MinIO resources), the overall declared description does not accurately represent what this specific code chunk actually does.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

Cron setup (macOS LaunchAgent)

xml
<!-- ~/Library/LaunchAgents/com.yourname.langfuse-backup.plist -->
<key>StartCalendarInterval</key>
<dict>
  <key>Hour</key><integer>2</integer>

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding

The script runs an unpinned container image with docker run --rm alpine ..., relying on whatever alpine:latest resolves to locally or from a registry. If the image is replaced, poisoned in a registry, or changes unexpectedly over time, the backup process will execute untrusted code with mounted access to the MinIO volume contents and the host backup directory, which is especially sensitive in a backup/restore skill.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The script runs the unpinned image alpine for a privileged restore step that mounts the MinIO data volume and executes destructive commands inside it. Because no tag or digest is specified, behavior depends on whatever image is current at execution time, which creates supply-chain risk and can lead to unexpected or malicious image changes affecting a highly sensitive restore operation.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

Low
Category
Tool Misuse
Confidence
15% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/restore_langfuse.sh (reported line 124)May include surrounding context.

sh
if [ -n "$MINIO_VOL" ]; then
        # Clear and restore
        docker run --rm \
            -v "$MINIO_VOL":/data \
            -v "$BACKUP_DIR":/backup:ro \
            alpine sh -c "rm -rf /data/* && tar xzf /backup/minio-data.tar.gz -C /data"

Tool Parameter Abuse

Low
Category
Tool Misuse
Confidence
15% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/restore_langfuse.sh (reported line 127)May include surrounding context.

sh
docker run --rm \
            -v "$MINIO_VOL":/data \
            -v "$BACKUP_DIR":/backup:ro \
            alpine sh -c "rm -rf /data/* && tar xzf /backup/minio-data.tar.gz -C /data"
        log "✅ MinIO restored from volume $MINIO_VOL"
    else
        log "⚠️  Could not find MinIO volume — skipping"

Static analysis

No suspicious patterns detected.