Back to skill

Security audit

ragflow-runbook

Security checks for vulnerabilities and agentic risk

Overview

This looks like a real RAGFlow operations runbook, but it deserves review because it can deploy mutable upstream Docker content and use API tokens in weakly constrained network checks.

Review this before installing on a production or sensitive host. Use a pinned RAGFlow release or commit, inspect Compose files before starting containers, prefer HTTPS or loopback-only HTTP for bearer-token checks, use a low-privilege monitoring API key, avoid putting secrets directly in cron or launchd files, and require explicit confirmation before restore or scheduled-task setup.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ragflow_ping.py:33
Finding

Bearer token can be transmitted over plaintext HTTP or forwarded during redirects

Content
View full analysis

Vulnerability Details

File Location: scripts/ragflow_ping.py:33-39, 47-58; scripts/ragflow_smoke.py:34-41, 48-56; scripts/ragflow_status.py:31-38, 45-48; examples/api-examples.sh:19, 34-48
Vulnerability Type: Unprotected transmission of credentials and insufficient destination validation
Risk Level: High

Vulnerable Code

scripts/ragflow_ping.py:33-39:

python
def http_get(url: str, api_key: str | None = None, timeout: int = 10) -> tuple[int, bytes]:
    headers = {"Accept": "application/json"}
    if api_key:
        headers["Authorization"] = f"Bearer {api_key}"
    req = urllib.request.Request(url, headers=headers, method="GET")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return resp.status, resp.read()

scripts/ragflow_ping.py:47-58:

python
base_url = get_env("RAGFLOW_BASE_URL").rstrip("/")
api_key = os.environ.get("RAGFLOW_API_KEY", "").strip() or None

st, _ = http_get(f"{base_url}/openapi.json", api_key=None)
if st != 200:
    print(f"LIVENESS_FAIL openapi.json status={st}")
    return 2

if not api_key:
    print("OK_LIVE (no api key set)")
    return 0

st2, body2 = http_get(f"{base_url}/v1/system/status", api_key=api_key)

scripts/ragflow_smoke.py:34-41:

python
def http_get(url: str, api_key: str, timeout: int = 15) -> tuple[int, bytes]:
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}",
    }
    req = urllib.request.Request(url, headers=headers, method="GET")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return resp.status, resp.read()

scripts/ragflow_status.py:31-38:

python
def http_get(url: str, api_key: str, timeout: int = 15) -> tuple[int, bytes]:
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}",
    }
    re
...[truncated 3183 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse RAGFLOW_BASE_URL before making requests and permit only https by default.
  2. Allow plaintext HTTP only for explicit loopback destinations such as 127.0.0.1, ::1, or localhost, or behind a clearly named opt-in such as RAGFLOW_ALLOW_INSECURE_HTTP=1.
  3. Reject URLs containing embedded user information, fragments, unexpected schemes, or malformed hostnames.
  4. Disable redirects for authenticated requests, or implement a redirect handler that permits only same-scheme, same-host, and same-port redirects.
  5. Never downgrade an authenticated request from HTTPS to HTTP.
  6. Configure certificate verification with the system trust store or an explicitly supplied private CA. Do not introduce a global TLS-verification bypass.
  7. Apply the same validation to all Python helpers, shell examples, and documented curl commands.
  8. Use a narrowly scoped monitoring credential with short lifetime and straightforward revocation.
  9. Add automated tests covering plaintext remote URLs, cross-origin redirects, HTTPS-to-HTTP redirects, URLs with user information, and loopback exceptions.

T03 · Remote Payload Retrieval and Execution

Warning
Location
scripts/deploy.sh:110
Finding

Mutable upstream deployment code is retrieved and executed without integrity verification

Content
View full analysis

Vulnerability Details

File Location: scripts/deploy.sh:68-91, 110-145, 171-178
Vulnerability Type: Remote payload retrieval and execution without immutable pinning or integrity validation
Risk Level: Medium

Vulnerable Code

scripts/deploy.sh:68-91:

bash
UPSTREAM_REPO_URL="https://github.com/infiniflow/ragflow.git"
UPSTREAM_DIR="$DEPLOY_ROOT/ragflow"
DOCKER_DIR="$UPSTREAM_DIR/docker"

have_git=0
if command -v git >/dev/null 2>&1; then
  have_git=1
fi

if [[ $have_git -eq 1 ]]; then
  echo "3) Fetch upstream via git clone (preferred)"

  if [[ -d "$UPSTREAM_DIR/.git" ]]; then
    echo "  - Found existing repo: $UPSTREAM_DIR"
    echo "  - Updating..."
    (cd "$UPSTREAM_DIR" && git fetch --all --prune)
  elif [[ -e "$UPSTREAM_DIR" ]]; then
    echo "  ERROR: $UPSTREAM_DIR exists but is not a git repo"
    echo "         Move it aside or choose a different deploy_root"
    exit 2
  else
    echo "  - Cloning: $UPSTREAM_REPO_URL"
    git clone "$UPSTREAM_REPO_URL" "$UPSTREAM_DIR"
  fi

scripts/deploy.sh:110-145:

bash
echo "  - Download docker files"
FILES=(
  "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/docker-compose.yml"
  "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/docker-compose-base.yml"
  "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/.env"
  "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/service_conf.yaml.template"
  "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/entrypoint.sh"
  "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/README.md"
)

dl() {
  local url="$1"
  local name
  name=$(basename "$url")
  echo "    - $name"
  wget -q "$url" || curl -fsSLO "$url"
}

for url in "${FILES[@]}"; do
  dl "$url"
done

echo "  - Download nginx configuration"
mkdir -p nginx
cd nginx

NGINX_FILES=(
  "https://r
...[truncated 3447 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin deployment content to a reviewed release tag and full commit SHA instead of main or an unqualified default branch.
  2. After cloning, explicitly check out the expected commit and verify that git rev-parse HEAD exactly matches the configured SHA.
  3. Prefer signed upstream releases or signed commits and fail closed when signature verification fails.
  4. For fallback downloads, use immutable commit-based raw URLs and verify every file against a checksum manifest embedded in or securely distributed with the Skill.
  5. Pin container images by digest rather than mutable tags.
  6. Separate retrieval, verification, review, and execution into distinct commands. Do not permit startup until verification succeeds.
  7. Display the selected commit, image digests, and verification result before requesting start authorization.
  8. Validate Compose configuration for dangerous options, including privileged mode, host PID/network namespaces, Docker socket mounts, broad host filesystem mounts, added capabilities, and untrusted build contexts.
  9. Run deployment through a least-privileged or rootless Docker environment where operationally possible.
  10. Maintain an approved-version manifest so updates require an explicit review and manifest change rather than silently consuming new upstream content.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The declared description promises a broad end-to-end runtime operations runbook covering deployment, operation, troubleshooting, and monitoring. The supplied code chunk is much narrower: it is only an example shell script for hitting a few system endpoints and printing HTTP status codes. It does not deploy anything, does not provide substantive troubleshooting logic, and does not implement monitoring beyond minimal one-off checks. This is a material difference in primary purpose and scope, so the description does not accurately represent the code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description says this skill is an end-to-end runbook for RAGFlow runtime operations, which implies documentation or operational procedures for deployment, operation, troubleshooting, and monitoring. The supplied code instead implements a concrete alert-sending utility that performs outbound messaging to Telegram via OpenClaw. That is a materially different primary purpose from a runbook. It also introduces undeclared capabilities: sending external notifications, invoking an external command, and using environment-based routing configuration. While alerting could support monitoring/operations, this code chunk is not itself a runbook and its active behavior is not accurately represented by the declared description.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

docker compose up -d

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

docker compose up -d

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

docker compose up -d

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deploy.sh (reported line 198)May include surrounding context.

sh
# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

docker compose up -d

Chaining Abuse

High
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · examples/troubleshooting.md (reported line 50)May include surrounding context.

sudo sysctl -w vm.max_map_count=262144

Permanent (Linux)

echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf sudo sysctl -p

text

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deploy.sh (reported line 114)May include surrounding context.

sh
FILES=(
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/docker-compose.yml"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/docker-compose-base.yml"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/.env"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/service_conf.yaml.template"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/entrypoint.sh"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/README.md"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deploy.sh (reported line 152)May include surrounding context.

sh
FILES=(
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/docker-compose.yml"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/docker-compose-base.yml"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/.env"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/service_conf.yaml.template"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/entrypoint.sh"
    "https://raw.githubusercontent.com/infiniflow/ragflow/main/docker/README.md"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill explicitly instructs agents to use shell commands, network access, and environment variables, but it does not declare any tool scope or allowed-tools boundary. That creates an avoidable over-privilege condition where an agent may execute powerful actions without an explicit permission contract, increasing the chance of unintended command execution or network access in a runtime-ops context.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
cd ragflow/docker

# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
cd ragflow/docker

# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/deploy.sh (reported line 167)May include surrounding context.

sh
cd ragflow/docker

# Common requirement for some document engine profiles
sudo sysctl -w vm.max_map_count=262144 || true

# Default .env = elasticsearch + cpu
# To change ports/passwords/image versions: edit docker/.env

Rp1

Medium
Category
MCP Rug Pull
Confidence
83% confidence
Finding

The backup example uses docker run --rm ... alpine without pinning an image tag or digest, which can pull a mutable image at execution time. This creates supply-chain risk because future image changes or a compromised upstream image could alter behavior unexpectedly during sensitive backup operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The restore procedure instructs users to extract backup data directly into a target volume after docker compose down without an explicit warning that existing data may be overwritten or destroyed. In an agent-executed context, this can lead to irreversible data loss if the wrong volume, environment, or backup archive is used.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
83% confidence
Finding

The restore example also invokes an unpinned alpine container via docker run --rm, again relying on a mutable external image during a sensitive data operation. Because restore actions touch persistent volumes, compromised or changed container content could corrupt, overwrite, or exfiltrate backed-up data.

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 619)May include surrounding context.

md
#### 12.5.2 launchd (macOS)

Create two plist files (one for ping, one for smoke) and load them with `launchctl`.

Ping (every 10 minutes):

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 626)May include surrounding context.

md
#### 12.5.2 launchd (macOS)

Create two plist files (one for ping, one for smoke) and load them with `launchctl`.

Ping (every 10 minutes):

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 656)May include surrounding context.

md
#### 12.5.2 launchd (macOS)

Create two plist files (one for ping, one for smoke) and load them with `launchctl`.

Ping (every 10 minutes):

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 664)May include surrounding context.

md
#### 12.5.2 launchd (macOS)

Create two plist files (one for ping, one for smoke) and load them with `launchctl`.

Ping (every 10 minutes):

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 699)May include surrounding context.

md
#### 12.5.2 launchd (macOS)

Create two plist files (one for ping, one for smoke) and load them with `launchctl`.

Ping (every 10 minutes):

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 704)May include surrounding context.

md
#### 12.5.2 launchd (macOS)

Create two plist files (one for ping, one for smoke) and load them with `launchctl`.

Ping (every 10 minutes):

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This finding points to the same plist block, where long-lived persistence is combined with inline secret material. Even though the example is for benign monitoring, storing authentication data in a persistent launch configuration broadens the blast radius if the host or user account is compromised.

Content

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

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This finding points to the same plist block, where long-lived persistence is combined with inline secret material. Even though the example is for benign monitoring, storing authentication data in a persistent launch configuration broadens the blast radius if the host or user account is compromised.

Content

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

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This duplicate finding maps to the same smoke-check plist and carries the same risk: persistent scheduled execution plus inline bearer token storage. Because bearer tokens are sufficient for API access, disclosure can enable unauthorized health/status queries or broader actions depending on server-side privileges.

Content

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

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>

Static analysis

No suspicious patterns detected.