Back to skill

Security audit

project-factory

Security checks for vulnerabilities and agentic risk

Overview

This project scaffold skill is broadly coherent, but it grants persistent OpenClaw authority and handles credentials too broadly for automatic installation.

Install only after reviewing the scripts. Use --plan first, do not pass real bot tokens on the command line, remove or patch main auth-profile copying, add strict project_key validation, and avoid --cron-remove unless you intentionally want admin cron control.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/bootstrap_project.py:1230
Finding

Arbitrary Filesystem Writes Through an Unvalidated Project Key

Content
View full analysis

Vulnerability Details

File Location: scripts/bootstrap_project.py:1230-1233
Additional Location: scripts/bootstrap_project.py:859-863, scripts/bootstrap_project.py:141-143
Vulnerability Type: Path traversal and unrestricted destination path
Risk Level: High

Vulnerable Code

python
key = args.project_key
root = WORKSPACE / key
threads = parse_thread_ids(args.thread_ids) if args.thread_ids else {}

if root.exists():
    print(f"ERROR: {root} already exists")
    sys.exit(1)

The interactive bootstrap path uses the same unsafe construction:

python
def _do_fix_and_continue(args):
    from pathlib import Path
    project_dir = Path(WORKSPACE) / args.project_key
    project_dir.mkdir(parents=True, exist_ok=True)
    # Build minimal ctx and write files

Files subsequently created under the selected destination are also made executable:

python
for script in (root / "scripts").glob("*.sh"):
    script.chmod(0o755)
for script in (root / "scripts").glob("*.py"):
    script.chmod(0o755)

Technical Analysis

args.project_key is joined directly to WORKSPACE without centralized validation or a containment check. Although interactive collection applies a limited slug validator, the documented direct command-line interface does not apply that validator.

pathlib.Path permits both traversal components and absolute operands. Values such as ../../attacker-selected-directory can escape the projects directory after path resolution. If args.project_key is absolute, WORKSPACE / args.project_key resolves to the supplied absolute path and discards the intended workspace prefix.

The script then creates directories, writes the generated scaffold, and marks generated Python and shell files executable. The root.exists() check prevents the normal direct-bootstrap branch from targeting an already-existing root directory, but it does not prevent creation in a ...[truncated 1465 chars]

Remediation
View remediation

Remediation Suggestions

  1. Implement one centralized validator and invoke it in every operating mode, including direct CLI, interactive, fix, and continue modes.

  2. Restrict project keys to a strict pattern such as:

    python
    import re
    
    PROJECT_KEY_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
    
    def validate_project_key(value: str) -> str:
        if not PROJECT_KEY_PATTERN.fullmatch(value):
            raise ValueError(
                "project_key must contain only lowercase letters, digits, and internal hyphens"
            )
        return value
    
  3. Resolve and verify destination containment before creating anything:

    python
    workspace = WORKSPACE.resolve()
    project_key = validate_project_key(args.project_key)
    destination = (workspace / project_key).resolve()
    
    if destination.parent != workspace:
        raise ValueError("Project destination escapes WORKSPACE")
    
  4. Reject absolute paths, path separators, . and .. components explicitly.

  5. Do not use exist_ok=True for new project creation unless the operation is explicitly an update and the destination has been positively identified as an existing scaffold.

  6. Create the project in a temporary directory under WORKSPACE, validate it, and atomically rename it to the final destination.

  7. Avoid assigning executable permissions until generated content has passed validation.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/bootstrap_project.py:235
Finding

Main-Agent Authentication Profiles Are Cloned Into Every New Project Agent

Content
View full analysis

Vulnerability Details

File Location: scripts/bootstrap_project.py:235-248
Additional Location: scripts/upgrade_project.py:222-233
Vulnerability Type: Excessive credential propagation and violation of least privilege
Risk Level: High

Vulnerable Code

python
agent_id = f"{args.project_key}-chat"
agent_dir = Path.home() / ".openclaw" / "agents" / agent_id / "agent"
sessions_dir = Path.home() / ".openclaw" / "agents" / agent_id / "sessions"
agent_dir.mkdir(parents=True, exist_ok=True)
sessions_dir.mkdir(parents=True, exist_ok=True)

main_agent_dir = Path.home() / ".openclaw" / "agents" / "main" / "agent"
for filename in ["auth-profiles.json", "models.json"]:
    src = main_agent_dir / filename
    dst = agent_dir / filename
    if src.exists() and not dst.exists():
        shutil.copy2(src, dst)

The upgrade path repeats the same behavior:

python
for filename in ['auth-profiles.json', 'models.json']:
    dst = agent_dir / filename
    if not dst.exists() and (main_agent_dir / filename).exists():
        changes.append(f'create assistant runtime file {dst}')
        if not dry_run:
            agent_dir.mkdir(parents=True, exist_ok=True)
            shutil.copy2(main_agent_dir / filename, dst)

Technical Analysis

The bootstrap and upgrade operations copy the entire auth-profiles.json file from the privileged main agent into each project-specific agent. A project agent may need access to a selected model provider, but duplicating the main agent's complete authentication profile grants all credentials represented by that file rather than only the capability required by the configured workflow.

This expands the credential trust boundary from one main agent to every generated project agent. Project agents process project-local conversations, workflow documents, logs, and potentially externally sourced content. Any compromise, unsafe generated node implemen ...[truncated 1750 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove automatic copying of auth-profiles.json from both bootstrap and upgrade paths.
  2. Provision a dedicated project authentication profile containing only the model provider and permissions selected for that project.
  3. Prefer references to a credential broker or secret manager over duplicated credential files.
  4. Require explicit, informed confirmation before granting a project agent access to any shared authentication profile.
  5. Maintain an allowlist mapping workflow node types to required providers and reject unnecessary credentials.
  6. Ensure project-agent credential files use restrictive permissions such as 0600, with parent directories restricted to the owning account.
  7. Document credential provenance and support revocation on a per-project basis.
  8. Add an upgrade migration that identifies previously cloned profiles and replaces them with scoped profiles without exposing their contents in logs.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/bootstrap_project.py:1622
Finding

Telegram Bot Token Is Exposed in Process Arguments, Terminal Output, and Plaintext Files

Content
View full analysis

Vulnerability Details

File Location: scripts/bootstrap_project.py:1622-1639
Additional Location: scripts/bootstrap_project.py:832-838, SKILL.md:238-248
Vulnerability Type: Plaintext secret exposure and insecure credential handling
Risk Level: High

Vulnerable Code

The token is duplicated into two generated plaintext files:

python
def RUNTIME_ENV(c): return f"""\
BOT_TOKEN={c['BOT_TOKEN']}
TELEGRAM_BOT_TOKEN={c['BOT_TOKEN']}
TELEGRAM_CHAT_ID={c['CHAT_ID']}
CRON_SCHEDULE={c['CRON_SCHEDULE']}
CRON_TIMEOUT={c['CRON_TIMEOUT']}
REPORT_THREAD_ID={c['REPORT_THREAD_ID']}
CHAT_THREAD_ID={c['CHAT_THREAD_ID']}
GENERAL_THREAD_ID={c['GENERAL_THREAD_ID']}
WECHAT_PUBLISH={c['WECHAT_PUBLISH']}
WECHAT_CLASH_SELECTOR={c['WECHAT_CLASH_SELECTOR']}
WECHAT_CLASH_CONTROLLER_UNIX={c['WECHAT_CLASH_UNIX']}
"""

def ENV_FILE(c): return f"""\
TELEGRAM_BOT_TOKEN={c['BOT_TOKEN']}
TELEGRAM_CHAT_ID={c['CHAT_ID']}
REPORT_THREAD_ID={c['REPORT_THREAD_ID']}
CHAT_THREAD_ID={c['CHAT_THREAD_ID']}
GENERAL_THREAD_ID={c['GENERAL_THREAD_ID']}
WECHAT_PUBLISH={c['WECHAT_PUBLISH']}
WECHAT_CLASH_SELECTOR={c['WECHAT_CLASH_SELECTOR']}
WECHAT_CLASH_CONTROLLER_UNIX={c['WECHAT_CLASH_UNIX']}
"""

Interactive setup prints all collected values, including the complete bot token:

python
print()
print("✅ Parameter validation passed")
print()
for k, v in params.items():
    print(f"  {k}: {v}")
print(f"  threads: {threads}")
print()

The documented invocation also directs users to pass the token as a command-line argument:

bash
python3 skills/project-scaffold/scripts/bootstrap_project.py \
  --project-key "<project_key>" \
  --project-name "<project_name>" \
  --description "<description>" \
  --flowchart "<mermaid_flowchart_as_single_line>" \
  --bot-token "<bot_token>" \
  --chat-id "<chat_id>" \
  --thread-ids "<thread_ids>" 
...[truncated 2680 chars]
Remediation
View remediation

Remediation Suggestions

  1. Do not accept secrets through ordinary command-line arguments. Read the token from a secret manager, protected file descriptor, or non-echoing prompt such as getpass.getpass().

  2. Redact secret-bearing parameters in all output:

    python
    for key, value in params.items():
        rendered = "<redacted>" if key == "bot_token" and value else value
        print(f"  {key}: {rendered}")
    
  3. Store the token in one protected location rather than duplicating it. Generated configuration should contain a secret reference, not the secret value.

  4. If a local secret file is unavoidable, create it atomically with mode 0600 and verify ownership and permissions after creation.

  5. Add .env and other secret-bearing files to generated .gitignore and backup/artifact exclusion rules.

  6. Prevent secret values from appearing in validation errors, dry-run output, exception traces, or generated documentation.

  7. Provide token rotation instructions and revoke tokens that may already have appeared in command histories or logs.

  8. Add automated tests that assert the token is absent from stdout, stderr, generated Markdown, process command examples, and non-secret configuration files.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (50)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill presents itself as creating a new project but also describes modifying global user configuration, project routing, agent runtime files, and Telegram bindings for existing assistants. This is dangerous because it crosses from local scaffolding into system administration, enabling persistent changes outside the target project with little indication from the name or description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill presents itself as creating a new project but also describes modifying global user configuration, project routing, agent runtime files, and Telegram bindings for existing assistants. This is dangerous because it crosses from local scaffolding into system administration, enabling persistent changes outside the target project with little indication from the name or description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill presents itself as creating a new project but also describes modifying global user configuration, project routing, agent runtime files, and Telegram bindings for existing assistants. This is dangerous because it crosses from local scaffolding into system administration, enabling persistent changes outside the target project with little indication from the name or description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill presents itself as creating a new project but also describes modifying global user configuration, project routing, agent runtime files, and Telegram bindings for existing assistants. This is dangerous because it crosses from local scaffolding into system administration, enabling persistent changes outside the target project with little indication from the name or description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill presents itself as creating a new project but also describes modifying global user configuration, project routing, agent runtime files, and Telegram bindings for existing assistants. This is dangerous because it crosses from local scaffolding into system administration, enabling persistent changes outside the target project with little indication from the name or description.

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

md
After all nodes are configured, serialize the final node list as a JSON array. This becomes `--nodes-json` for `bootstrap_project.py`:

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documented ability to remove arbitrary cron jobs is a powerful destructive capability unrelated to project initialization. If invoked improperly, it could disable automation, monitoring, or scheduled reporting for other projects, causing denial of service or operational disruption across the environment.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The bootstrap persists bot credentials into generated environment files inside the project tree, increasing the risk of credential exposure through source control, artifact sharing, or local disclosure. Because the skill's purpose is project generation, embedding secrets directly into scaffolded files creates long-lived sensitive material with broad accidental exposure paths.

Content

Scanner excerpt · scripts/bootstrap_project.py (reported line 129)May include surrounding context.

python
)

    (root / "config" / "runtime.env").write_text(RUNTIME_ENV(ctx), encoding="utf-8")
    (root / ".env").write_text(ENV_FILE(ctx), encoding="utf-8")

    (root / "scripts" / "project_routing.py").write_text(PROJECT_ROUTING_PY(ctx), encoding="utf-8")
    (root / "scripts" / "broadcast_event.py").write_text(BROADCAST_EVENT_PY(ctx), encoding="utf-8")

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script can remove arbitrary OpenClaw cron jobs by ID, which is a destructive capability unrelated to normal project bootstrap. If an agent or user invokes this mode incorrectly, it can disable scheduled automations across the environment and cause service disruption.

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 · scripts/bootstrap_project.py (reported line 1066)May include surrounding context.

python
fake_args.cron_schedule = env.get("CRON_SCHEDULE", "0 9 * * *")
        fake_args.cron_timeout = int(env.get("CRON_TIMEOUT", "1200"))

        env_file = project_dir / ".env"
        if env_file.exists():
            for ln in env_file.read_text().splitlines():
                if 'BOT_TOKEN' in ln and '=' in ln:

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/bootstrap_project.py (reported line 1125)May include surrounding context.

python
fake_args.cron_schedule = env.get("CRON_SCHEDULE", "0 9 * * *")
        fake_args.cron_timeout = int(env.get("CRON_TIMEOUT", "1200"))

        env_file = project_dir / ".env"
        if env_file.exists():
            for ln in env_file.read_text().splitlines():
                if 'BOT_TOKEN' in ln and '=' in ln:

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/bootstrap_project.py (reported line 1148)May include surrounding context.

python
fake_args.cron_schedule = env.get("CRON_SCHEDULE", "0 9 * * *")
        fake_args.cron_timeout = int(env.get("CRON_TIMEOUT", "1200"))

        env_file = project_dir / ".env"
        if env_file.exists():
            for ln in env_file.read_text().splitlines():
                if 'BOT_TOKEN' in ln and '=' in ln:

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/bootstrap_project.py (reported line 1302)May include surrounding context.

python
fake_args.cron_schedule = env.get("CRON_SCHEDULE", "0 9 * * *")
        fake_args.cron_timeout = int(env.get("CRON_TIMEOUT", "1200"))

        env_file = project_dir / ".env"
        if env_file.exists():
            for ln in env_file.read_text().splitlines():
                if 'BOT_TOKEN' in ln and '=' in ln:

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/bootstrap_project.py (reported line 1174)May include surrounding context.

python
fake_args.cron_schedule = env.get("CRON_SCHEDULE", "0 9 * * *")
        fake_args.cron_timeout = int(env.get("CRON_TIMEOUT", "1200"))

        # Parse .env for bot token
        for ln in env_file.read_text().splitlines():
            if 'BOT_TOKEN' in ln and '=' in ln:
                fake_args.bot_token = ln.split('=', 1)[1].strip().strip('"')

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README advertises full scaffold generation, directory creation, runtime env output, and routing/cron registration, but does not warn that invoking the skill can modify the local workspace and operational configuration. This can mislead users into triggering a skill with side effects they did not anticipate, especially when combined with broad activation phrases and one-pass generation behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases are broad enough to match ordinary conversation about starting or designing projects, which can cause the skill to activate in contexts where the user did not intend to invoke automated scaffolding. In this skill, unintended activation is more dangerous because later phases create project directories, config files, and routing/cron-related artifacts, increasing the chance of unwanted workspace modification.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill documentation describes operations that imply file reads/writes and shell execution, including running bootstrap scripts and modifying project/config files, but it declares no explicit tool scope or permission boundaries. This creates an authorization gap where an agent may execute powerful filesystem and shell actions without clear least-privilege constraints, increasing the risk of unintended or abusive changes.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The activation criteria are extremely broad, triggering on general discussion of new projects, automation, or content pipelines. For a skill with filesystem, config, and cron side effects, loose activation significantly increases the chance of accidental invocation and unintended state changes from routine conversation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

Automatic Phase 0 activation on any plain-language project description is ambiguous and can cause the skill to begin processing or steering workflow without clear consent. In the context of later configuration and generation steps, this ambiguity can funnel users into a high-impact path they did not intend to trigger.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

L044-L046 state that Phase 0 is 'not an external API call' and 'does not call another model or external API.' However, the surrounding documentation defines Phase 0 in terms of selecting and upgrading among specific hosted models such as MiniMax-M2.5 and GPT-4.1, which contradicts the claim that no other model/API is involved.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

The required parameter prompt specifies the project name as a Chinese name, and many interaction examples and prompts are written as mandatory Chinese phrasing without stating that other languages are supported. This can violate language/locale policy when the skill is used by users who have not opted into Chinese-only interaction.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Including project-wide maintenance and administrative cron management inside a bootstrap skill broadens its authority beyond the stated purpose. This makes accidental overreach more likely and weakens least-privilege assumptions for a skill that may be auto-triggered during ordinary project discussions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The fetch section lists multiple third-party sources and tools, some with authentication, without warning that requests, metadata, and potentially secrets or private content may be transmitted to external services. Because this skill is meant to auto-design workflows, users may unknowingly create pipelines that pull from external systems in ways that expose sensitive data or violate expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The document defines publishing to external platforms, including an immediate mode, but does not warn that content may be sent off-system and publicly posted right away. In a project-bootstrap skill, this omission can cause users to generate automations that unintentionally exfiltrate data or publish unreviewed content to live accounts.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The naming convention states that each non-START/END node should have a short Chinese label, which is a natural-language policy constraint. Because the document does not offer a language choice or justify the locale restriction as region-specific, this is a language policy violation.

Content

No source excerpt is available for this finding.