Back to skill

Security audit

SkillScout

Security checks for vulnerabilities and agentic risk

Overview

SkillScout has a coherent safety-catalog purpose, but its recommendations rely on mutable remote or registry content and its review/publishing code has validation gaps that could affect what users or agents trust.

Treat SkillScout as a useful catalog, not as an authority for automatic installation. Do not let an agent install recommended skills without user confirmation and independent source review. Prefer pinned MCP/package versions, and be cautious because catalog entries and trust scores can be changed or poisoned if the publishing or review pipeline is compromised.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (8)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/harden-skill.sh:13
Finding
Python Code Injection in the Hardening Workflow<![CDATA[ ## Vulnerability Details **File Location**: `scripts/harden-skill.sh:13-20` **Vulnerability Type**: Python code injection through unsafe string interpolation **Risk Level**: High ### Vulnerable Code ```bash SKILL_NAME="${1:?Usage: harden-skill.sh <skill-name>}" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" # Find skill in catalog SKILL_INFO=$(python3 -c " import json with open('$PROJECT_DIR/data/skills.json') as f: catalog = json.load(f) for s in catalog['skills']: if s['name'] == '$SKILL_NAME': print(f'{s[\"author\"]}/{s[\"name\"]}') break else: print('NOT_FOUND') ") ``` ### Technical Analysis The shell argument `SKILL_NAME` is interpolated directly into a Python program passed to `python3 -c`. Shell quoting does not make this value safe inside Python source code. An attacker can include quote characters and additional Python syntax in the skill name, terminate the intended string literal, and cause arbitrary Python statements or expressions to run. This exceeds the minimum privileges required to look up a skill name. The script only needs to compare an input string with catalog entries; it does not need to generate executable Python source from that input. ### Attack Path 1. An attacker persuades an operator or automation system to invoke `harden-skill.sh` with a crafted skill name. 2. The crafted value closes the Python string literal in: ```python if s['name'] == '$SKILL_NAME': ``` 3. Additional attacker-controlled Python syntax is interpreted by `python3 -c`. 4. The payload executes with the operating-system privileges of the user running the review workflow. ### Impact Assessment Successful exploitation provides arbitrary local code execution as the review operator. The attacker could read or modify files accessible to that account, alter catalog data, tamper with reviews, access environment variables, or invoke available local commands. No privilege escalation be ...[truncated 150 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pass the skill name as a positional argument rather than embedding it into Python source: ```bash SKILL_INFO=$(python3 - "$PROJECT_DIR/data/skills.json" "$SKILL_NAME" <<'PY' import json import sys catalog_path = sys.argv[1] skill_name = sys.argv[2] with open(catalog_path, encoding="utf-8") as f: catalog = json.load(f) for skill in catalog.get("skills", []): if skill.get("name") == skill_name: print(f"{skill['author']}/{skill['name']}") break else: print("NOT_FOUND") PY ) ``` - Validate skill names against a conservative allowlist, such as letters, digits, periods, underscores, and hyphens. - Reject names containing control characters, path separators, quotes, or newlines. - Add regression tests using quotes, backslashes, newlines, and Python metacharacters. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/scan-blocklists.sh:17
Finding
Python Code Injection in Blocklist Scanning<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scan-blocklists.sh:17-24` **Vulnerability Type**: Python code injection through unsafe string interpolation **Risk Level**: High ### Vulnerable Code ```bash BLOCKED=$(python3 -c " import json with open('$PROJECT_DIR/$BLOCKLIST') as f: data = json.load(f) blocked = [s['name'] for s in data.get('blocked', [])] print('BLOCKED' if '$SKILL' in blocked else 'CLEAN') " 2>/dev/null) ``` ### Technical Analysis The user-controlled `SKILL` value is embedded directly into executable Python source. A crafted value can escape the quoted Python string and inject additional Python operations. Redirecting standard error does not prevent execution; it only conceals diagnostic output. This is particularly security-sensitive because the vulnerable code is part of a blocklist enforcement stage. An attacker-controlled skill identifier can attack the scanner that is intended to evaluate it. ### Attack Path 1. The scanner is invoked with an attacker-controlled or maliciously supplied skill name. 2. The supplied value terminates the Python string surrounding `$SKILL`. 3. Injected Python syntax runs while the blocklist check is being evaluated. 4. The payload executes with the scanner operator's local privileges and can also interfere with the reported `BLOCKED` or `CLEAN` result. ### Impact Assessment The vulnerability permits arbitrary code execution under the invoking account. It can compromise the integrity of the review pipeline, modify blocklist or catalog files, falsify scan outcomes, and access any files or environment data available to the review process. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Supply the blocklist path and skill name through `sys.argv`: ```bash BLOCKED=$(python3 - "$PROJECT_DIR/$BLOCKLIST" "$SKILL" <<'PY' import json import sys with open(sys.argv[1], encoding="utf-8") as f: data = json.load(f) blocked = {entry.get("name") for entry in data.get("blocked", [])} print("BLOCKED" if sys.argv[2] in blocked else "CLEAN") PY ) ``` - Validate skill names before processing. - Do not suppress all Python errors. Treat parser or scanner failures as a failed closed security check rather than implicitly allowing the skill. - Add tests proving that metacharacters cannot alter the Python program or scanner result. ]]>

T01 · Skill Instruction Hijacking

Error
Location
scripts/review-skill.sh:43
Finding
Untrusted Skill Source Is Embedded Directly into the Review Agent Prompt<![CDATA[ ## Vulnerability Details **File Location**: `scripts/review-skill.sh:43-57` **Vulnerability Type**: Indirect prompt injection into a security-review agent **Risk Level**: High ### Vulnerable Code ```bash cat > "/tmp/skillscout-review-input-$SKILL.md" << HEREDOC # SkillScout Security & Quality Review You are a security-focused code reviewer. Analyze the following OpenClaw skill source code and complete the review template below. ## CRITICAL CONSTRAINTS: - You are READ-ONLY. You cannot and must not execute any code. - Analyze the source text only. Look for security risks, quality issues, and verify claims. - Be skeptical. Flag anything suspicious. - Write for a non-technical audience where possible. ## SKILL SOURCE CODE: $SKILL_SOURCE ## REVIEW TEMPLATE (complete this): $(cat "$PROJECT_DIR/REVIEW_TEMPLATE.md") HEREDOC ``` ### Technical Analysis `SKILL_SOURCE` is downloaded from a third-party repository and inserted directly into the instruction body sent to an AI review agent. The generated task does not create a trustworthy separation between reviewer instructions and attacker-controlled source text. Although the prompt tells the reviewer to look for prompt injection, it does not explicitly require that every instruction found inside the source be treated solely as quoted data. A malicious Skill can therefore contain instructions telling the reviewer to ignore the review policy, assign a safe rating, omit findings, or emit attacker-selected JSON. The read-only constraint reduces direct host impact but does not protect the integrity of the review decision. ### Attack Path 1. An attacker publishes a Skill containing adversarial instructions in its source or documentation. 2. `fetch-skill.sh` retrieves that content as text. 3. `review-skill.sh` interpolates the content directly into the agent task. 4. The operator submits the generated task to the isolated review agent as instructed by the workflow. 5. The agent follows the embedded adversari ...[truncated 593 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Place the non-overridable review policy in a trusted system message, not in the same content block as the source. - Explicitly state that all text inside the source attachment is untrusted data and that instructions found there must never be followed. - Pass source files through a typed attachment or structured data channel where supported. - Use clear, randomized or length-prefixed boundaries and avoid interpolating source into an instruction template. - Require deterministic schema validation and independent security checks on the output. - Require a human reviewer to compare the result against the exact source commit before publication. - Treat any review output containing unexpected fields, markup, URLs, or instructions as invalid. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/merge-reviews.py:40
Finding
Agent-Generated Reviews Are Merged Without Schema or Security Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/merge-reviews.py:40-59` **Vulnerability Type**: Catalog poisoning through unvalidated AI-generated objects **Risk Level**: High ### Vulnerable Code ```python for filepath in review_files: print(f"Processing {filepath}...") with open(filepath) as f: text = f.read() reviews = extract_json_array(text) if reviews is None: print(f" WARNING: Could not extract JSON array from {filepath}") continue for review in reviews: name = review.get('name', '') if name and name not in existing: new_skills.append(review) existing.add(name) print(f" + {name} ({review.get('trustScore', '?')})") elif name in existing: print(f" ~ {name} (already exists, skipping)") catalog['skills'].extend(new_skills) catalog['totalReviewed'] = len(catalog['skills']) catalog['lastUpdated'] = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') with open(CATALOG_PATH, 'w') as f: json.dump(catalog, f, indent=2) ``` ### Technical Analysis The merger only checks whether a parsed object has a nonempty and previously unused `name`. It does not enforce a JSON Schema, reject unknown fields, validate trust scores, constrain text, verify the source author, verify URLs, or bind the review to a source commit. Because the preceding review stage processes attacker-controlled source through an AI agent, its output cannot safely be treated as trusted structured data. Unvalidated objects later flow into MCP responses and HTML rendering. ### Attack Path 1. A malicious source manipulates the review agent, or an attacker supplies a crafted review file directly. 2. The file contains a syntactically valid JSON array with an object having a new `name`. 3. `merge-reviews.py` accepts the entire object without validating its other fields. 4. The object is written to `data/skills.json`. 5. After synchronization or publication, MCP clients ...[truncated 399 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate every review against a strict JSON Schema before merging. - Reject unknown properties and enforce exact types, length limits, enumerations, and character sets. - Restrict `trustScore` and `permissions` to predefined values. - Validate skill and author identifiers against conservative patterns. - Reject HTML, control characters, instruction-like text, and unsafe URL schemes from display fields. - Verify that the author, skill name, repository URL, and source commit match the reviewed artifact. - Make human approval an enforced workflow step rather than documentation only. - Sign approved catalog records or maintain an auditable approval manifest bound to source hashes. - Write catalog updates atomically and retain a reviewable diff before replacement. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
docs/index.html:293
Finding
Stored Cross-Site Scripting Through Unescaped Catalog Fields<![CDATA[ ## Vulnerability Details **File Location**: `docs/index.html:293-337` **Vulnerability Type**: Stored cross-site scripting through `innerHTML` **Risk Level**: High ### Vulnerable Code ```javascript catsEl.innerHTML = categories.categories.map(c => { const count = (skills?.skills || []).filter(s => s.category === c.slug).length; return `<a class="cat-card" href="#${c.slug}"> <div class="emoji">${c.emoji}</div> <h3>${c.name}</h3> <p>${c.description}</p> <div class="count">${count} skill${count !== 1 ? 's' : ''} reviewed</div> </a>`; }).join(''); ``` ```javascript listEl.innerHTML = skills.skills.map(s => { const trustClass = s.trustScore === 'safe' ? 'trust-safe' : s.trustScore === 'caution' ? 'trust-caution' : 'trust-avoid'; const trustLabel = s.trustScore === 'safe' ? '🟢 Safe' : s.trustScore === 'caution' ? '🟡 Caution' : '🔴 Avoid'; const hardenBadge = s.hardening ? `<span class="harden-badge harden-${s.hardening.verdict?.toLowerCase()}">${ s.hardening.verdict === 'HARDENED' ? '🛡️ Hardened' : s.hardening.verdict === 'CONDITIONAL' ? '⚠️ Conditional' : '🚫 Rejected' } ${s.hardening.hardeningScore?.overall || '?'}/10</span>` : ''; return `<div class="skill-card" data-name="${s.name}" data-desc="${(s.plainDescription || s.description || '').toLowerCase()}" data-cat="${s.category}"> <div class="skill-header"> <span class="skill-name">${s.name}${hardenBadge}</span> <span class="trust-badge ${trustClass}">${trustLabel}</span> </div> <div class="skill-desc">${s.plainDescription || s.description}</div> <div class="skill-meta"> <span>👤 ${s.author}</span> <span>📁 ${s.category}</span> <span>⭐ ${s.rating || 'N/A'}</span> </div> </div>`; }).join(''); ``` ### Technical Analysis Multiple catalog-controlled values are concatenated into HTML markup and attribut ...[truncated 1288 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace string templates and `innerHTML` with DOM construction using `document.createElement`. - Assign all catalog text through `textContent`. - Validate category slugs and CSS class fragments against strict allowlists. - If HTML rendering is unavoidable, use a well-maintained sanitizer configured to forbid scripts, event attributes, embedded objects, and unsafe URLs. - Add a restrictive Content Security Policy that disallows inline scripts and limits script sources. - Apply length and character restrictions during catalog ingestion. - Add automated browser tests using payloads in every displayed catalog field and HTML attribute context. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/review-skill.sh:43
Finding
Predictable Temporary Review File Permits Symlink Overwrite and Tampering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/review-skill.sh:43-62` and `scripts/harden-skill.sh:29-33` **Vulnerability Type**: Unsafe temporary-file creation **Risk Level**: Medium ### Vulnerable Code From `scripts/review-skill.sh`: ```bash cat > "/tmp/skillscout-review-input-$SKILL.md" << HEREDOC # SkillScout Security & Quality Review ... HEREDOC echo "━━━ Stage 2: Ready for Isolated Agent Review ━━━" echo " Review input saved to: /tmp/skillscout-review-input-$SKILL.md" echo " $(wc -c < "/tmp/skillscout-review-input-$SKILL.md") bytes" ``` From `scripts/harden-skill.sh`: ```bash echo "Fetching source for $SKILL_INFO..." bash "$SCRIPT_DIR/fetch-skill.sh" "$SKILL_INFO" > "/tmp/skillscout-harden-$SKILL_NAME.md" 2>/dev/null LINES=$(wc -l < "/tmp/skillscout-harden-$SKILL_NAME.md") echo "Fetched $LINES lines. Ready for hardening review." echo "Input file: /tmp/skillscout-harden-$SKILL_NAME.md" ``` ### Technical Analysis Both scripts create predictable files in the shared `/tmp` directory using names derived from skill identifiers. The files are opened through ordinary shell redirection without exclusive creation, ownership verification, restrictive permissions, or cleanup. On a multi-user system, another local user can predict the filename and pre-create it as a symbolic link or regular file. The review process can consequently overwrite another file accessible to the invoking account or consume attacker-modified review input. ### Attack Path 1. A local attacker predicts the temporary filename from the skill name. 2. The attacker creates a symbolic link at that path pointing to a file writable by the review operator, or creates a file they can later modify. 3. The operator runs the review or hardening script. 4. Shell redirection follows the symbolic link or overwrites the pre-created path. 5. The target file is overwritten, or the generated review input is tampered with before it is submitted to the review agent. ### Impact Assessm ...[truncated 337 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a private temporary directory: ```bash umask 077 TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/skillscout.XXXXXXXX") trap 'rm -rf -- "$TMP_DIR"' EXIT HUP INT TERM REVIEW_INPUT="$TMP_DIR/review-input.md" ``` - Write only inside the private directory and do not derive the filesystem path directly from an untrusted skill name. - Use exclusive creation where possible. - Verify that temporary paths are regular files owned by the current user before reading them. - Remove all temporary material on success and failure. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:36
Finding
Unpinned npx Command Executes Mutable Registry Code<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:36-40` **Vulnerability Type**: Unpinned third-party package execution **Risk Level**: Medium ### Vulnerable Code ```markdown ### MCP Server (for agent-to-agent queries) ```bash npx @skillscout/mcp ``` ``` The package also declares a floating dependency range in `mcp-server/package.json:16-18`: ```json "dependencies": { "@modelcontextprotocol/sdk": "^1.12.0" } ``` ### Technical Analysis The documented `npx @skillscout/mcp` command resolves and executes the package version currently selected by the npm registry instead of an explicitly audited release. The effective executed code can therefore change after this repository's review. The included lockfile pins resolved transitive artifacts with integrity hashes for local locked installation, but that protection does not guarantee that an unversioned top-level `npx` invocation will execute the audited package release. ### Attack Path 1. A user follows the documented MCP command. 2. `npx` resolves the package from the npm registry. 3. A future compromised, malicious, or otherwise unsafe package release is selected. 4. The downloaded package executes locally with the user's privileges. ### Impact Assessment A compromised top-level package can run arbitrary code as the invoking user and access files, environment variables, network connectivity, and other resources available to that account. No malicious dependency is demonstrated in the audited lockfile; the risk arises from mutable future resolution. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin the exact audited package version in user instructions: ```bash npx --yes @skillscout/mcp@0.1.0 ``` - Prefer installation with `npm ci` from a committed lockfile where practical. - Pin direct dependency versions exactly rather than using caret ranges for security-sensitive releases. - Publish package provenance and verify registry integrity metadata. - Document the expected package hash, version, and publisher identity. - Require explicit review before updating the pinned MCP or SDK versions. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
mcp/server.js:17
Finding
Unsigned Remote Catalog Is Returned Directly as Agent Recommendations<![CDATA[ ## Vulnerability Details **File Location**: `mcp/server.js:17-40` and `mcp/server.js:86-133` **Vulnerability Type**: Untrusted remote content injected into agent-visible tool output **Risk Level**: Medium ### Vulnerable Code ```javascript const DATA_URL = 'https://nashbot67.github.io/skillscout/data/skills.json'; const CATEGORIES_URL = 'https://nashbot67.github.io/skillscout/data/categories.json'; let skillsCache = null; let categoriesCache = null; let cacheTime = 0; const CACHE_TTL = 300_000; async function fetchData() { if (skillsCache && Date.now() - cacheTime < CACHE_TTL) return; try { const [skillsRes, catsRes] = await Promise.all([ fetch(DATA_URL), fetch(CATEGORIES_URL), ]); skillsCache = await skillsRes.json(); categoriesCache = await catsRes.json(); cacheTime = Date.now(); } catch (e) { if (!skillsCache) throw new Error('Failed to fetch SkillScout data: ' + e.message); } } ``` ```javascript return results.map(s => ({ name: s.name, author: s.author, category: s.category, description: s.plainDescription || s.description, trustScore: s.trustScore, rating: s.rating, permissions: s.permissions, install: `npx clawhub@latest install ${s.author}/${s.name}`, })); ``` ### Technical Analysis The MCP server fetches a mutable remote catalog and returns its fields directly to an AI agent. It does not verify a digital signature or expected digest, enforce a response-size limit, validate the response status, or apply a strict schema before using the content. HTTPS protects the transport connection but does not protect against compromise of the publishing account, repository, deployment workflow, or hosting content. Catalog descriptions and identifiers can contain adversarial instructions, and identifiers are used to form installation guidance. The network access is relevant to live catalog lookup, but accepting mutable content without integrity controls exceeds the minimum trust necessary for ...[truncated 904 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer the bundled, reviewed catalog used by `mcp-server/index.js`, or require signed remote catalogs. - Pin a trusted public key in the server and verify catalog signatures before parsing. - Check `response.ok`, content type, content length, and final response origin. - Enforce strict JSON Schema validation and reject unknown fields. - Apply response and field-size limits. - Treat all catalog descriptions as untrusted data in MCP output and clearly label them as such. - Validate author and skill identifiers before constructing installation commands. - Require explicit user confirmation and independent source verification before any installation. - Retain the last verified catalog and fail closed when signature or schema validation fails. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (161)

Credential Access

High
Category
Privilege Escalation
Content
- ❌ Install any packages
- ❌ Access the internet
- ❌ Read workspace files outside the skill being reviewed
- ❌ Access credentials, API keys, or secrets
- ❌ Modify any files
- ❌ Spawn sub-agents
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

External Script Fetching

High
Category
Supply Chain
Content
### Quick Search (Static API)
```bash
curl -s https://nashbot67.github.io/skillscout/data/skills.json | python3 -c "
import json, sys
q = sys.argv[1].lower()
data = json.load(sys.stdin)
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
### Quick Search (Static API)
```bash
curl -s https://nashbot67.github.io/skillscout/data/skills.json | python3 -c "
import json, sys
q = sys.argv[1].lower()
data = json.load(sys.stdin)
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Credential Access

High
Category
Privilege Escalation
Content
},
          "informationDisclosure": {
            "applicable": true,
            "finding": "API key stored in plaintext in config files (~/.config/4claw/credentials.json). Thread content fetched includes full post text by default. No encryption in transit mentioned."
          },
          "denialOfService": {
            "applicable": true,
Confidence
93% confidence
Finding
The `4claw` entry states that API keys are stored in plaintext in `~/.config/4claw/credentials.json`. Plaintext local credential storage materially increases theft risk from other local processes, backups, or accidental disclosure, especially for a network-connected posting service.

Credential Access

High
Category
Privilege Escalation
Content
"curl -X POST https://www.4claw.org/api/v1/boards/milady/threads -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{...}'"
        ],
        "exfiltrationVectors": [
          "~/.config/4claw/credentials.json (API key in plaintext)",
          "Thread replies posted to 4claw servers (content sent to external API)",
          "Agent identity (name, description) exposed on public imageboard",
          "SVG media embedded in posts (can contain exfiltration URLs if validation bypassed)",
Confidence
91% confidence
Finding
The exfiltration vector explicitly names `~/.config/4claw/credentials.json` as plaintext API-key storage. Because the skill also sends data to an external API, compromise of that file enables account abuse and impersonation on the remote service.

Credential Access

High
Category
Privilege Escalation
Content
"id": "CWE-312",
            "title": "Cleartext Storage of Sensitive Information",
            "severity": "high",
            "details": "API key stored in plaintext at ~/.config/4claw/credentials.json with no encryption or access control enforcement mentioned."
          },
          {
            "id": "CWE-295",
Confidence
94% confidence
Finding
This CWE-312 finding is a direct and credible credential-handling weakness: plaintext bearer token storage without enforced access controls. The context makes it more dangerous because the token authorizes external posting actions and identity claims.

Credential Access

High
Category
Privilege Escalation
Content
"id": "CWE-312",
            "title": "Cleartext Storage of Sensitive Information",
            "severity": "critical",
            "details": "OAuth token stored in plaintext in ~/.openclaw/agents/main/agent/auth-profiles.json. No encryption, no keychain integration."
          },
          {
            "id": "CWE-276",
Confidence
97% confidence
Finding
The `ag-model-usage` entry states that an OAuth token is stored in plaintext in `auth-profiles.json` with no keychain integration. Because the token carries cloud-platform related scopes, theft could enable broader unauthorized API access beyond simple quota checks.

Credential Access

High
Category
Privilege Escalation
Content
],
      "risks": [
        "External API calls",
        "Credentials storage in ~/.config/agentarcade/credentials.json",
        "Network communication with third-party service"
      ],
      "tags": [
Confidence
71% confidence
Finding
The `agentarcade` entry flags credential storage in `~/.config/agentarcade/credentials.json`, implying plaintext or filesystem-based secrets for a third-party multiplayer API. Although less detail is provided than other entries, storing external-service credentials in a local JSON file is a real credential exposure pattern.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The `second-brain` entry contains contradictory security metadata: it claims to be documentation-only and fully hardened while also declaring scripts, network access, credentials, and read/write permissions. This kind of inconsistency can mislead reviewers or automated allowlisting systems into treating a data-exfiltrating skill as low risk.

Exfiltration Commands

High
Category
Prompt Injection
Content
"file-search",
        "cost-tracking"
      ],
      "verdict": "This skill makes extensive network API calls to Google Gemini services and can upload files to external stores. It includes cost estimation and billing features. While the code appears well-structured, it requires network access and external API keys. Users should review the cost implications before use.",
      "hasScripts": true,
      "hasDependencies": true,
      "linesReviewed": 1360
Confidence
90% confidence
Finding
The skill explicitly supports uploading local files to external Gemini services, which is a real exfiltration channel. While expected for the feature, it remains security-relevant because sensitive local documents could leave the environment if file selection is too broad or automated.

Credential Access

High
Category
Privilege Escalation
Content
"stride": {
          "spoofing": {
            "applicable": true,
            "finding": "API keys for OpenAI/LiteLLM stored in .env file; no authentication between local API server and clients (port 8000, localhost only but no auth token required)."
          },
          "tampering": {
            "applicable": true,
Confidence
88% confidence
Finding
The `agentic-paper-digest` skill stores API keys in `.env` and lacks auth on the local API server. Plaintext secrets in environment/config files are routinely exposed through process inspection, logs, backups, or accidental commits, and the nearby unauthenticated API worsens the blast radius.

Credential Access

High
Category
Privilege Escalation
Content
],
        "exfiltrationVectors": [
          "OpenAI API key (OPENAI_API_KEY env var; also sent to LiteLLM if LITELLM_API_KEY set)",
          ".env file (credentials)",
          "SQLite database at data/papers.sqlite3 (paper summaries, topics, settings)",
          "GET /api/papers endpoint (returns all papers in database as JSON)",
          "GET /api/topics endpoint (returns all topics)",
Confidence
85% confidence
Finding
This exfiltration section explicitly lists `.env` credentials and API keys as exposed artifacts. In a service that processes external content and runs a local API, plaintext credential files materially increase the chance of unauthorized key access.

Credential Access

High
Category
Privilege Escalation
Content
"id": "CWE-327",
            "title": "Use of a Broken or Risky Cryptographic Algorithm",
            "severity": "low",
            "details": "No encryption for SQLite database or .env file. Credentials are stored in plaintext."
          }
        ],
        "hardeningScore": {
Confidence
84% confidence
Finding
The JSON notes that credentials are stored in plaintext due to no encryption for `.env`. While this line is phrased under a weak CWE mapping, the underlying concern is valid: plaintext secret storage is an actual credential-protection issue.

Credential Access

High
Category
Privilege Escalation
Content
],
      "risks": [
        "Network access to external API",
        "Credential storage in .env file",
        "Cookie storage in /tmp (insecure)",
        "External API dependency"
      ],
Confidence
86% confidence
Finding
The `alexandrie` skill declares credential storage in `.env` and insecure cookie handling in `/tmp`. This is especially dangerous for a note-management API because stolen credentials or session cookies expose potentially sensitive personal data and allow remote account actions.

Credential Access

High
Category
Privilege Escalation
Content
"stride": {
          "spoofing": {
            "applicable": true,
            "finding": "Password stored in /home/eth3rnit3/clawd/.env (plaintext); API authentication via JWT cookie. Risk: credential exposure in .env file or cookies."
          },
          "tampering": {
            "applicable": true,
Confidence
94% confidence
Finding
This line directly states a plaintext password in `.env` with JWT-cookie authentication. Password and session material stored this way are low-effort targets for local compromise, accidental disclosure, or multi-user system abuse.

Credential Access

High
Category
Privilege Escalation
Content
},
          "informationDisclosure": {
            "applicable": true,
            "finding": "User ID and password in .env file (world-readable depending on umask). JWT tokens stored in /tmp/alexandrie_cookies.txt (world-readable, /tmp is not secure). API responses include user ID and personal information."
          },
          "denialOfService": {
            "applicable": true,
Confidence
95% confidence
Finding
The information-disclosure section says user ID/password are in `.env` and JWTs are in world-readable `/tmp`. This is a strong, direct credential-protection failure that can enable account takeover on the remote notes service.

Credential Access

High
Category
Privilege Escalation
Content
"curl -s -c /tmp/alexandrie_cookies.txt -X POST",
          "curl -s -b /tmp/alexandrie_cookies.txt -c /tmp/alexandrie_cookies.txt",
          "jq",
          "source /home/eth3rnit3/clawd/.env"
        ],
        "exfiltrationVectors": [
          "/home/eth3rnit3/clawd/.env (password in plaintext)",
Confidence
92% confidence
Finding
The shell command `source /home/eth3rnit3/clawd/.env` operationalizes reading plaintext credentials from disk into the process environment. In a reusable skill, hardcoded secret-file sourcing also increases the chance of accidental exposure and brittle deployments.

Credential Access

High
Category
Privilege Escalation
Content
"source /home/eth3rnit3/clawd/.env"
        ],
        "exfiltrationVectors": [
          "/home/eth3rnit3/clawd/.env (password in plaintext)",
          "/tmp/alexandrie_cookies.txt (JWT tokens, world-readable)",
          "https://api-notes.eth3rnit3.org/api (remote API endpoint)"
        ],
Confidence
93% confidence
Finding
This exfiltration vector again confirms plaintext password storage in `.env`. The repeated documentation across risk, shell command, and exfiltration sections makes this a well-supported true positive.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"id": "CWE-78",
            "title": "Improper Neutralization of Special Elements used in an OS Command (OS Command Injection)",
            "severity": "critical",
            "details": "cooldown.sh uses eval \"$COMMAND\" with unquoted variable. Attacker can inject shell commands via COMMAND parameter. e.g., COMMAND='true; rm -rf /' would execute."
          },
          {
            "id": "CWE-312",
Confidence
99% confidence
Finding
Again, this duplicate match captures the same injection sink and example payload. In the context of an orchestration skill, this is especially severe because task definitions may flow from higher-level automation and appear trustworthy.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"id": "CWE-78",
            "title": "Improper Neutralization of Special Elements used in an OS Command (OS Command Injection)",
            "severity": "critical",
            "details": "cooldown.sh uses eval \"$COMMAND\" with unquoted variable. Attacker can inject shell commands via COMMAND parameter. e.g., COMMAND='true; rm -rf /' would execute."
          },
          {
            "id": "CWE-312",
Confidence
99% confidence
Finding
Again, this duplicate match captures the same injection sink and example payload. In the context of an orchestration skill, this is especially severe because task definitions may flow from higher-level automation and appear trustworthy.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"id": "CWE-78",
            "title": "Improper Neutralization of Special Elements used in an OS Command (OS Command Injection)",
            "severity": "critical",
            "details": "cooldown.sh uses eval \"$COMMAND\" with unquoted variable. Attacker can inject shell commands via COMMAND parameter. e.g., COMMAND='true; rm -rf /' would execute."
          },
          {
            "id": "CWE-312",
Confidence
99% confidence
Finding
Again, this duplicate match captures the same injection sink and example payload. In the context of an orchestration skill, this is especially severe because task definitions may flow from higher-level automation and appear trustworthy.

Chaining Abuse

High
Category
Tool Misuse
Content
"id": "CWE-78",
            "title": "Improper Neutralization of Special Elements used in an OS Command (OS Command Injection)",
            "severity": "critical",
            "details": "cooldown.sh uses eval \"$COMMAND\" with unquoted variable. Attacker can inject shell commands via COMMAND parameter. e.g., COMMAND='true; rm -rf /' would execute."
          },
          {
            "id": "CWE-312",
Confidence
99% confidence
Finding
The semicolon in `COMMAND='true; rm -rf /'` demonstrates chaining abuse through command injection. Because the workflow manager may execute task-provided commands automatically, attackers can chain benign-looking operations with destructive payloads without additional privileges.

Credential Access

High
Category
Privilege Escalation
Content
"id": "CWE-22",
            "title": "Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)",
            "severity": "high",
            "details": "scripts/bun-fs.sh read/write accept user-provided paths without validation. Could read/write to /etc/passwd, private keys, etc."
          },
          {
            "id": "CWE-78",
Confidence
90% confidence
Finding
The `bun-runtime` path traversal description explicitly says user-provided paths can read or write arbitrary filesystem locations such as `/etc/passwd` or private keys. In a tool exposing generic file operations, this means credential or secret access is a realistic consequence of insufficient path restriction.

Credential Access

High
Category
Privilege Escalation
Content
},
          "informationDisclosure": {
            "applicable": true,
            "finding": "API key stored in plaintext in config files (~/.config/4claw/credentials.json). Thread content fetched includes full post text by default. No encryption in transit mentioned."
          },
          "denialOfService": {
            "applicable": true,
Confidence
97% confidence
Finding
The `4claw` entry documents plaintext API key storage in `~/.config/4claw/credentials.json`. Storing credentials unencrypted in predictable locations raises theft risk from other local processes, backups, or accidental disclosure.

Credential Access

High
Category
Privilege Escalation
Content
"curl -X POST https://www.4claw.org/api/v1/boards/milady/threads -H 'Authorization: Bearer YOUR_API_KEY' -H 'Content-Type: application/json' -d '{...}'"
        ],
        "exfiltrationVectors": [
          "~/.config/4claw/credentials.json (API key in plaintext)",
          "Thread replies posted to 4claw servers (content sent to external API)",
          "Agent identity (name, description) exposed on public imageboard",
          "SVG media embedded in posts (can contain exfiltration URLs if validation bypassed)",
Confidence
96% confidence
Finding
The exfiltration vectors explicitly include a plaintext credential file. In context this is not just a theoretical note: the skill also performs network actions, so stolen credentials can be immediately used to impersonate the user to the external service.

Static analysis

No suspicious patterns detected.