Back to skill

Security audit

Foxcode Openclaw

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed Foxcode/OpenClaw setup helper, but its validator can send API tokens to arbitrary configured URLs and its wizard can overwrite existing OpenClaw configuration without creating its own backup.

Install only if you are comfortable reviewing local OpenClaw config changes first. Back up ~/.openclaw/openclaw.json and auth-profiles.json yourself before running the wizard, and do not run validate_config.py on a config whose baseUrl you do not fully trust because it may send your Foxcode API token to that URL.

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

Error
Location
scripts/validate_config.py:173
Finding
Bearer Credential Disclosure and SSRF Through an Untrusted Custom Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/validate_config.py`, lines 173–180 and 240–259 **Vulnerability Type**: Credential disclosure and server-side request forgery through insufficient URL validation **Risk Level**: High ### Vulnerable Code ```python def validate_base_url(config: Dict) -> Tuple[bool, str]: """Validate the base URL from foxcode provider.""" providers = config.get("models", {}).get("providers", {}) foxcode = providers.get("foxcode", {}) base_url = foxcode.get("baseUrl", "") if not base_url: return False, "baseUrl is empty" if base_url not in VALID_ENDPOINTS: # Check if it's a valid URL format if not re.match(r'^https?://[^\s/]+', base_url): return False, f"Invalid URL format: {base_url}" # It's a custom URL, just warn return True, f"Custom endpoint (not in known list): {base_url}" return True, f"Valid endpoint: {base_url}" ``` ```python def test_endpoint_connection(config: Dict) -> Tuple[bool, str]: """Test connection to the foxcode endpoint.""" providers = config.get("models", {}).get("providers", {}) foxcode = providers.get("foxcode", {}) base_url = foxcode.get("baseUrl", "") api_key = foxcode.get("apiKey", "") # Resolve environment variable if needed if api_key.startswith("${") and api_key.endswith("}"): env_var = api_key[2:-1] api_key = os.environ.get(env_var, "") if not api_key: return False, f"Cannot test: environment variable {env_var} not set" try: req = Request(base_url, method="HEAD") req.add_header("Authorization", f"Bearer {api_key}") req.add_header("User-Agent", "Foxcode-Validator/1.0") with urlopen(req, timeout=15) as response: return True, f"Connection successful (status: {response.getcode()})" ``` ### Technical Analysis The validator treats any string matching a basic HTTP or HTTPS URL expression as an accep ...[truncated 2435 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce an exact allowlist of documented HTTPS endpoint URLs before attaching credentials. 2. Parse URLs with `urllib.parse.urlsplit()` rather than relying on a permissive regular expression. 3. Reject: - Plain HTTP URLs - URLs containing user-information components - Unexpected ports - Loopback, link-local, private, reserved, multicast, and unspecified IP addresses - Hostnames that resolve to those address classes 4. Separate reachability checks from authentication checks. A generic reachability test should not include the API key. 5. If custom providers must be supported, require explicit informed confirmation before transmitting a credential and clearly display the normalized destination. 6. Disable redirects for authenticated validation requests, or independently validate every redirect target and remove the `Authorization` header when the origin changes. 7. Pin the expected hostname and consider verifying the final connected address to reduce DNS-rebinding exposure. 8. Add regression tests proving that arbitrary domains, private addresses, HTTP URLs, and cross-origin redirects cannot receive credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/configure_foxcode.py:328
Finding
Destructive Replacement of Existing OpenClaw Configuration Without Backup or Atomic Write<![CDATA[ ## Vulnerability Details **File Location**: `scripts/configure_foxcode.py`, lines 328–338, 356–367, and 540–561 **Vulnerability Type**: Unsafe configuration overwrite and non-atomic file update **Risk Level**: Medium ### Vulnerable Code ```python # OpenClaw uses camelCase and nested providers structure config = { "models": { "providers": providers } } # Set default agent model to the default endpoint's primary model default_provider = f"foxcode-{default_endpoint}" if default_endpoint != "official" else "foxcode" config["agents"] = { "defaults": { "model": f"{default_provider}/{primary_model}" } } ``` ```python def save_config(config: Dict, config_path: Path) -> bool: """Save configuration to file.""" try: # Create directory if it doesn't exist config_path.parent.mkdir(parents=True, exist_ok=True) # Write config with open(config_path, 'w') as f: json.dump(config, f, indent=2) # Set restrictive permissions os.chmod(config_path, 0o600) return True except Exception as e: print(f"❌ Error saving config: {e}") return False ``` ```python config_path = get_config_path() config = create_config(endpoint_keys, api_token, primary_model, fallback_models, default_endpoint) print(f"\nConfiguration to save:") print(f" Config file: {config_path}") print(f" Auth file: {get_auth_profiles_path()}") print(f" Endpoints: {', '.join(endpoint_keys)}") print(f" Default Endpoint: {default_endpoint}") print(f" Primary Model: {primary_model}") print(f" All Models: {', '.join(MODELS.keys())}") # Show config (no API key - stored separately) print(f"\nConfig contents (openclaw.json):") print(json.dumps(config, indent=2)) print() confirm = input("Save this configuration? (y/n): ").strip().lower() if confirm == 'y': # Sav ...[truncated 2348 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read and validate the existing configuration before generating changes. 2. Merge only Skill-owned entries, such as the selected `foxcode*` provider records and the explicitly approved default model. 3. Preserve all unrelated providers, agents, plugins, and top-level settings. 4. Show the user a structured diff rather than only showing the replacement object. 5. Create a timestamped backup with restrictive permissions before any modification. 6. Perform an atomic update: - Create a temporary file in the destination directory. - Set mode `0600` when creating the temporary file. - Serialize and flush the complete JSON document. - Call `fsync()` on the file. - Atomically replace the destination with `os.replace()`. - Where appropriate, synchronize the parent directory. 7. If updating `openclaw.json` succeeds but updating `auth-profiles.json` fails, restore the prior state or report a transactional failure with an automated rollback option. 8. Add tests verifying preservation of unknown top-level fields, unrelated providers, multiple agents, file permissions, backup creation, and recovery from interrupted writes. ]]>
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 (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a broader configuration and management skill for Foxcode AI models within OpenClaw, emphasizing setup guidance, endpoint selection, model configuration, and monitoring. The supplied code only implements the monitoring portion, and even that is limited to a CLI endpoint health checker. It sends HTTP requests to hardcoded Foxcode URLs, measures latency, outputs status, and provides lightweight recommendations. There is no code for API setup, storing configuration, selecting models in OpenClaw, managing primary/fallback assignments, or any psychology-backed instructional behavior. Because the actual primary purpose is endpoint status checking rather than configuration/management in OpenClaw, this is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The declared purpose implies an interactive setup/management skill that helps users configure Foxcode models in OpenClaw, choose endpoints, set primary and fallback models, and monitor status. The supplied code does not perform configuration or management; it only validates an already-existing configuration file and reports issues. While some checks align indirectly with setup topics (API key, endpoint, models), the primary behavior is validation rather than guided configuration. Additionally, the code performs a live network connection test and inspects local file permissions, which are not reflected in the description. Therefore the description does not accurately represent the actual behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises and instructs use of scripts that read environment data, write configuration files, perform network access, and invoke shell commands, but it declares no explicit tool scope or permission boundaries. This increases the chance that an agent platform grants broader capabilities than users expect, especially because the skill handles API tokens and modifies critical OpenClaw files.

Session Persistence

Medium
Category
Rogue Agent
Content
# macOS/Linux
export FOXCODE_API_TOKEN="sk-foxcode-your-token"

# Add to ~/.zshrc or ~/.bashrc for persistence
echo 'export FOXCODE_API_TOKEN="sk-foxcode-your-token"' >> ~/.zshrc
```
Confidence
90% 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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### DO

- [ ] Use environment variables for API keys
- [ ] Set restrictive file permissions: `chmod 600 ~/.openclaw/openclaw.json`
- [ ] Rotate API tokens regularly
- [ ] Use separate tokens for different environments
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# Set permissions
chmod 600 ~/.openclaw/openclaw.json

# Use env var for API key
export FOXCODE_API_TOKEN="sk-foxcode-your-token"
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

File System Enumeration

Medium
Category
Data Exfiltration
Content
### Config Not Loading

1. Verify file path: `ls -la ~/.openclaw/openclaw.json`
2. Check JSON syntax: `python3 -m json.tool ~/.openclaw/openclaw.json`
3. Check permissions: `ls -l ~/.openclaw/openclaw.json`
4. Restart OpenClaw after changes
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

File System Enumeration

Medium
Category
Data Exfiltration
Content
1. Verify file path: `ls -la ~/.openclaw/openclaw.json`
2. Check JSON syntax: `python3 -m json.tool ~/.openclaw/openclaw.json`
3. Check permissions: `ls -l ~/.openclaw/openclaw.json`
4. Restart OpenClaw after changes

### API Key Not Working
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The wizard claims to guide users through primary and fallback model configuration, and it collects fallback selections in select_fallback_models(). However, create_config() ignores the fallback_models argument entirely and instead writes all models in a fixed order derived from MODELS, so the saved configuration does not reflect the user's fallback choices.

Session Persistence

Medium
Category
Rogue Agent
Content
fallback_models: List[str],
    default_endpoint: str
) -> Dict:
    """Create the configuration dictionary with multiple endpoint providers.
    
    Note: OpenClaw uses auth-profiles.json for API keys, NOT openclaw.json.
    The apiKey is stored separately in ~/.openclaw/agents/main/agent/auth-profiles.json
Confidence
78% confidence
Finding
The script persistently stores a user-supplied API token in ~/.openclaw/agents/main/agent/auth-profiles.json, creating long-lived credential material on disk. Although permissions are restricted to 0600, persistent secret storage increases exposure if the local account is compromised, the file is backed up/synced insecurely, or other tooling later reads/logs the file.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The connection test performs an outbound request to whatever baseUrl is configured and includes the API key in the Authorization header. If the configuration points to a malicious, mistyped, or attacker-controlled endpoint, running the validator leaks the credential during validation, and the script does not give a clear warning that validation transmits the secret over the network.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
stat = config_path.stat()
        mode = stat.st_mode

        # Check if world-readable
        if mode & 0o044:
            return False, "File is world-readable (should be 600)"
Confidence
70% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
stat = config_path.stat()
        mode = stat.st_mode

        # Check if world-readable
        if mode & 0o044:
            return False, "File is world-readable (should be 600)"
Confidence
70% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The document presents navigation and content only in English, Chinese, and Japanese, which imposes a locale/language limitation by design. There is no wording that asks for user preference or clarifies that these languages are optional rather than mandatory, so this can be read as a language policy constraint without opt-in.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The documentation shows authentication patterns using bearer tokens and an environment variable but does not warn users to keep tokens secret, avoid pasting real values into shared files, or rotate exposed credentials. In a beginner-focused setup guide, this omission increases the chance that users copy real tokens into notes, screenshots, shell history, or version-controlled config, leading to credential exposure.

Missing User Warnings

Low
Confidence
96% confidence
Finding
The config example includes an `api_key` field in plaintext without warning against storing real credentials directly in configuration files. Because this skill is specifically for configuring OpenClaw and targets beginners, users are likely to copy the example verbatim into local config or repositories, creating a realistic path to accidental secret leakage and unauthorized API use.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
This code embeds endpoint names in Chinese within user-facing output, and the script does not offer any language or locale choice. That creates a natural-language policy issue because users are forced into a specific language presentation regardless of preference.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
This code presents several user-facing option names in Chinese string literals while the surrounding wizard is otherwise in English. Because the skill does not offer a language/locale choice or document that it is intended only for Chinese-speaking users, it creates a language-policy issue under the locale-choice rule.

Static analysis

No suspicious patterns detected.