Back to skill

Security audit

2134

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent RAGFlow management skill, but it needs review because it can persist a live API key in plaintext and run destructive delete actions with limited built-in safeguards.

Install only if you trust the configured RAGFlow server and are comfortable giving the skill a bearer token that may be able to read, upload, modify, parse, search, and delete datasets or documents. Prefer a minimally scoped token, avoid --save-to-memory unless you can protect the memory file, use HTTPS for non-local servers, and manually confirm exact dataset or document IDs before any delete or stop operation.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/common.py:91
Finding

Plaintext API credential file is created without restrictive permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/common.py:91-96
Vulnerability Type: Insecure storage of bearer credentials
Risk Level: Medium

Vulnerable Code

python
def save_memory_config(memory_config: dict[str, Any], memory_file: str | Path | None = None) -> Path:
    path = resolve_memory_file(str(memory_file) if memory_file is not None else None)
    path.parent.mkdir(parents=True, exist_ok=True)
    try:
        path.write_text(json.dumps(memory_config, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    except OSError as exc:
        raise ConfigError(f"Failed to write memory file {path}: {exc}") from exc
    return path

The credential is added to this configuration before it is saved:

python
if getattr(args, "save_to_memory", False):
    updated_config = dict(memory_config)
    updated_config["base_url"] = base_url
    updated_config["api_key"] = api_key
    save_memory_config(updated_config, memory_file)
    memory_config = updated_config

Technical Analysis

The documented --save-to-memory workflow stores the RAGFlow bearer API key in plaintext. The code uses Path.write_text() without explicitly creating the file with owner-only permissions or subsequently enforcing a mode such as 0600.

Consequently, the effective permissions depend on the process umask and the permissions of any pre-existing user-selected memory file. In an environment with a permissive umask or unsafe existing file permissions, other local users or processes may be able to read the bearer token.

The default location under ~/.codex/memories/ reduces accidental exposure but does not guarantee that the file itself is owner-only. The --memory-file option can also select another location with weaker directory protections.

Attack Path

  1. The user invokes a documented command with --save-to-memory.
  2. resolve_runtime_config() adds the resolved bearer token to the ...[truncated 938 chars]
Remediation
View remediation

Remediation Suggestions

  1. Create new credential files atomically with owner-only permissions, for example by using os.open() with O_CREAT | O_EXCL and mode 0o600.
  2. Enforce 0600 after writing and verify that the resulting file is owned by the current user.
  3. Reject symbolic links and existing files with group or world permissions before storing credentials.
  4. Create the parent credential directory with mode 0700.
  5. Write through a securely created temporary file in the same directory, then atomically replace the destination.
  6. Prefer an operating-system credential manager or keyring instead of plaintext JSON where available.
  7. Clearly warn users that --save-to-memory persists the bearer token and provide a command to remove the stored credential.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/common.py:149
Finding

Bearer credentials and uploaded documents can be transmitted over insecure HTTP

Content
View full analysis

Vulnerability Details

File Location: scripts/common.py:149-155, scripts/common.py:299-317, scripts/upload.py:118-130, scripts/list_models.py:63-74
Vulnerability Type: Insecure authenticated transport and insufficient destination validation
Risk Level: Medium

Vulnerable Code

The shared URL validation accepts any absolute URL with a scheme and network location, including cleartext HTTP:

python
def resolve_base_url(cli_base_url: str | None = None, *, memory_config: dict[str, Any] | None = None) -> str:
    raw_value = cli_base_url
    if raw_value is None and memory_config:
        memory_base_url = memory_config.get("base_url")
        if memory_base_url is not None:
            raw_value = str(memory_base_url)

    base_url = (raw_value or "").strip()
    if not base_url:
        base_url = _prompt_non_empty("RAGFlow base URL: ")

    parsed = urllib.parse.urlsplit(base_url)
    if not parsed.scheme or not parsed.netloc:
        raise ConfigError("Invalid base URL. Use an absolute URL such as http://127.0.0.1:9380.")
    return base_url.rstrip("/")

Authenticated requests then attach the bearer token without requiring transport encryption:

python
def request_json(
    url: str,
    api_key: str,
    *,
    method: str = "GET",
    body: bytes | None = None,
    content_type: str | None = None,
    accept: str = "application/json",
) -> dict[str, Any]:
    headers = {"Authorization": f"Bearer {api_key}"}
    if accept:
        headers["Accept"] = accept
    if content_type:
        headers["Content-Type"] = content_type

    request_obj = urllib.request.Request(url, headers=headers, data=body, method=method)

    try:
        with urllib.request.urlopen(request_obj, timeout=HTTP_TIMEOUT) as response:
            return decode_json_response(response.read())

Upload requests transmit both the token and complete contents of user-selected fi ...[truncated 3201 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require https:// for all non-loopback RAGFlow endpoints.
  2. Permit plain HTTP only for explicit loopback hosts such as 127.0.0.1, ::1, or localhost, ideally behind a clearly named development-only override.
  3. Restrict accepted schemes to https and the narrowly defined loopback HTTP exception.
  4. Reject URLs containing embedded user information, fragments, or unexpected components.
  5. Disable automatic redirects for authenticated requests or implement a redirect handler that permits only same-scheme, same-host, and same-port redirects.
  6. Validate the final destination before sending or forwarding an Authorization header.
  7. Never downgrade an authenticated request from HTTPS to HTTP.
  8. Apply the same centralized transport policy to request_json(), upload_documents(), and list_models.py, rather than maintaining separate request implementations.
  9. Continue recommending minimally scoped bearer tokens so that credential compromise has limited impact.
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as dataset ingestion and retrieval, but it also includes model inventory functionality against a different endpoint (/v1/llm/my_llms). This scope expansion can expose additional sensitive metadata about providers, available models, and deployment configuration that a user may not expect from a dataset-focused skill.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as dataset ingestion and retrieval, but it also includes model inventory functionality against a different endpoint (/v1/llm/my_llms). This scope expansion can expose additional sensitive metadata about providers, available models, and deployment configuration that a user may not expect from a dataset-focused skill.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/upload.py list DATASET_ID --json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/upload.py list DATASET_ID --json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/upload.py list DATASET_ID --json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/upload.py list DATASET_ID --json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/upload.py list DATASET_ID --json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/upload.py list DATASET_ID --json

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The skill instructs saving a live API key in a default local memory file (~/.codex/memories/ragflow_credentials.json) and even shows the raw api_key field in an example. Persisting bearer credentials in a predictable path increases risk of credential theft through other tools, local compromise, overbroad file access, backups, or accidental disclosure.

Content

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

md
Pass `--base-url` explicitly when needed. For the API key, prefer `--api-key-file /path/to/key.txt`, or let the script prompt securely.

All scripts also support `--memory-file` and `--save-to-memory`. The default memory file is `~/.codex/memories/ragflow_credentials.json`.

Example memory file:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
92% confidence
Finding

The skill exposes destructive dataset deletion capability through API and script commands. Although the document includes a confirmation workflow, there is no enforceable technical guardrail in the manifest itself, so an agent or wrapper that ignores the procedure could issue irreversible delete requests against arbitrary dataset IDs.

Content

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

md
- `GET /api/v1/datasets`
- `POST /api/v1/datasets`
- `PUT /api/v1/datasets/<dataset_id>`
- `DELETE /api/v1/datasets`
- `GET /api/v1/datasets/<dataset_id>/documents`
- `POST /api/v1/datasets/<dataset_id>/documents`
- `PUT /api/v1/datasets/<dataset_id>/documents/<document_id>`

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
92% confidence
Finding

The skill provides document deletion operations that can remove files from datasets, potentially causing data loss or operational disruption. The text requires confirmation, but because this is policy-only and not an enforced control, parameter abuse remains possible if a caller supplies explicit document IDs.

Content

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

md
- `GET /api/v1/datasets/<dataset_id>/documents`
- `POST /api/v1/datasets/<dataset_id>/documents`
- `PUT /api/v1/datasets/<dataset_id>/documents/<document_id>`
- `DELETE /api/v1/datasets/<dataset_id>/documents`
- `POST /api/v1/datasets/<dataset_id>/chunks`
- `DELETE /api/v1/datasets/<dataset_id>/chunks`
- `POST /api/v1/retrieval`

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · SKILL.md (reported line 245)May include surrounding context.

md
- `PUT /api/v1/datasets/<dataset_id>/documents/<document_id>`
- `DELETE /api/v1/datasets/<dataset_id>/documents`
- `POST /api/v1/datasets/<dataset_id>/chunks`
- `DELETE /api/v1/datasets/<dataset_id>/chunks`
- `POST /api/v1/retrieval`
- `POST /api/v1/chunk/retrieval_test`
- `GET /v1/llm/my_llms`

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/common.py (reported line 16)May include surrounding context.

python
from typing import Any

HTTP_TIMEOUT = 30
DEFAULT_MEMORY_FILE = Path.home() / ".codex" / "memories" / "ragflow_credentials.json"


class ScriptError(Exception):

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill describes operations that require file reads, file writes, and network access, but it does not declare an explicit tool scope or permission boundary. In an agent environment, that omission increases the chance the skill can be invoked with broader capabilities than intended, including access to local files and remote endpoints without clear restriction.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instruction says to use the skill when user intent matches trigger actions "in either Chinese or English," which imposes a language restriction in the activation policy. There is no accompanying user choice, opt-in, or justification that the skill is limited to those languages for a region-specific purpose.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 25)May include surrounding context.

md
"dataset details", "show dataset info", "inspect dataset", "describe this dataset"
  "数据集详情", "查看数据集信息", "检查数据集", "显示这个数据集的信息"

- Create dataset
  Trigger phrases:
  "create dataset", "new dataset", "add a dataset"
  "创建数据集", "新建数据集", "添加数据集"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

When --save-to-memory is used, the script persists the resolved API key into a JSON file in the user's home directory without enforcing restrictive file permissions or clearly warning that the secret is stored in plaintext. In an agent/automation context, persistent plaintext credentials increase the blast radius of local compromise, accidental disclosure, backups, or overly broad filesystem access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script performs irreversible dataset deletion immediately when invoked with --ids, without any interactive confirmation, dry-run mode, or secondary safeguard. In an agent-driven or automation context, malformed input, prompt-induced mistakes, or accidental ID selection can cause unintended data loss across one or more datasets.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest scopes this skill to RAGFlow dataset and retrieval tasks, but this file targets the separate /v1/llm/my_llms endpoint and its CLI description says it will list available LLMs. Enumerating model/provider configuration is a distinct capability not described as part of dataset ingestion, parser control, status checking, or chunk retrieval.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest says this skill can retrieve relevant chunks with search.py, but the implementation calls json.dumps at L199 without any json import in the file. In practice this causes a runtime NameError before the network request is sent, so the script does not perform the described retrieval operation.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/common.py (reported line 59)May include surrounding context.

python
return

    for stream_name in ("stdout", "stderr"):
        stream = getattr(sys, stream_name, None)
        if stream is None or not hasattr(stream, "reconfigure"):
            continue
        try:

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code performs an API call to retrieve document data using a base URL and API key, which is a network operation that may transmit dataset identifiers and authentication context. Within this file there is no confirmation prompt, user-facing log/print before the request, or comment/docstring warning that remote data will be accessed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code performs an HTTP POST to a remote API containing the user's search query and optional dataset/document identifiers. While the script's purpose is search, this file does not include any confirmation, privacy notice, or user-facing disclosure that input data will be sent to an external service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This code performs an outbound HTTP PUT request containing user-supplied dataset fields such as name, description, avatar, and parser configuration. While the script purpose is to update a dataset, the file itself provides no user-facing disclosure that local input will be transmitted to a remote API endpoint.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.