Back to skill

Security audit

Coaching

Security checks for vulnerabilities and agentic risk

Overview

This coaching skill is mostly coherent, but it handles confidential client records with weak path scoping that can expose other local JSON data despite strong privacy claims.

Install only after reviewing the local-data risks. This skill should validate client identifiers, enforce path containment under the coaching directory, add explicit least-privilege file scope, and make its confidentiality and retention limits concrete before being used with real client information.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prep_session.py:14
Finding

Path Traversal Permits Unauthorized Local JSON Data Access

Content
View full analysis

Vulnerability Details

File Location: scripts/prep_session.py, lines 14-19
Vulnerability Type: Path traversal and insufficient file-access validation
Risk Level: High

Vulnerable Code

python
def load_client_data(client_name):
    """Load client data from isolated storage."""
    client_file = os.path.join(COACHING_DIR, f"{client_name.lower().replace(' ', '_')}.json")
    if os.path.exists(client_file):
        with open(client_file, 'r') as f:
            return json.load(f)
    return {"sessions": [], "goals": [], "commitments": []}

The client_name value originates from the required --client command-line argument and is incorporated into a filesystem path. Replacing spaces and converting input to lowercase does not remove path separators, absolute path prefixes, or .. traversal components.

os.path.join() also discards COACHING_DIR when its subsequent argument is an absolute path. Consequently, a caller can direct the script to another readable JSON file instead of a record contained in the intended coaching directory.

The loaded object is subsequently processed and selected values from its sessions and commitments fields are printed. This directly contradicts the documented guarantees of client isolation and local coaching-data confidentiality.

Technical Analysis

The vulnerability is caused by treating a human-readable client name as a trusted filename without canonicalization or containment validation. There is no check that the resolved file:

  • Remains beneath COACHING_DIR.
  • Has a filename from an approved character set.
  • Does not contain .. components or path separators.
  • Is not an absolute path.
  • Is not reached through a symbolic link that escapes the storage directory.

Exploitation is constrained to files readable by the operating-system account running the skill, whose names can be addressed with the automatically appended .json suffix, and whose ...[truncated 1889 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not derive storage paths directly from untrusted client names. Assign each client an opaque, application-generated identifier such as a UUID and maintain a validated name-to-identifier mapping.

  2. If filename derivation is unavoidable, enforce a strict allowlist such as letters, digits, underscores, and hyphens. Reject absolute paths, path separators, empty names, . components, and .. components.

  3. Resolve and verify path containment before opening the file:

    python
    from pathlib import Path
    import re
    
    COACHING_PATH = Path(COACHING_DIR).resolve()
    
    def load_client_data(client_name):
        if not re.fullmatch(r"[A-Za-z0-9_-]+", client_name):
            raise ValueError("Invalid client identifier")
    
        candidate = (COACHING_PATH / f"{client_name}.json").resolve()
    
        if candidate.parent != COACHING_PATH:
            raise ValueError("Client path escapes coaching storage")
    
        if candidate.is_file():
            with candidate.open("r", encoding="utf-8") as file:
                data = json.load(file)
            if not isinstance(data, dict):
                raise ValueError("Invalid client record")
            return data
    
        return {"sessions": [], "goals": [], "commitments": []}
    
  4. Protect against symbolic-link escapes. Where supported, reject symlinks and open files using platform facilities equivalent to O_NOFOLLOW; additionally verify the opened file's identity and location.

  5. Apply restrictive permissions to the coaching directory and records, limiting access to the owning user.

  6. Validate the complete JSON schema before processing or displaying values, and handle parsing errors without exposing unrelated file contents or implementation details.

  7. Add tests covering absolute paths, traversal sequences, nested separators, encoded or alternate separators, symbolic links, malformed JSON, and attempts to select another client's record.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Context Leakage

High
Category
Data Exfiltration
Confidence
85% confidence
Finding

Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Content

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

md
| `track_progress.py` | Track client progress |
| `set_goal.py` | Set structured goals |
| `between_sessions.py` | Between-session support |
| `log_session.py` | Log session notes |
| `track_commitment.py` | Track client commitments |
| `prepare_review.py` | Prepare progress review |

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill describes file-backed local storage under memory/coaching/ and references scripts that would need file access, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization ambiguity where the runtime may grant broader-than-intended file access or operators may assume tighter controls than actually exist, which is risky for highly sensitive client data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description says to use the skill whenever the user mentions broad topics like coaching sessions, coaching questions, client progress, or coaching goals. Such wide triggers can cause accidental activation in unrelated contexts, increasing the chance that sensitive conversational content is processed or stored under this skill without clear user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill repeatedly promises 'STRICT CONFIDENTIALITY' and states that no client data is ever shared, mixed, or exposed, but the file provides policy statements rather than enforceable technical guarantees. Overstated privacy claims are dangerous because users may disclose highly sensitive client information based on assurances the system may not be able to verify or enforce in all environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The script reads sensitive coaching records from local storage based solely on a provided client name and then displays that data, but it provides no user-facing notice or consent flow about accessing stored confidential client information. In a coaching skill with 'STRICT client confidentiality,' undisclosed retrieval of prior sessions and commitments increases privacy risk, especially if invoked in a shared environment or by a user unaware that persisted client data will be surfaced.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.