Back to skill

Security audit

Session Handoff

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its handoff purpose, but review is warranted because its documented installer uses mutable remote code and one resume check can inspect git metadata from a path supplied inside a handoff file.

Install only from a pinned, reviewed version or a trusted ClawHub distribution path. Treat handoff files as sensitive project records, review them before sharing or committing, and avoid running resume/staleness checks on handoffs from untrusted sources unless you verify the recorded project path first.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T08 · Insecure Dependencies

Error
Location
README.md:26
Finding
Unpinned Third-Party Installer and Mutable Repository Source<![CDATA[ ## Vulnerability Details **File Location**: `README.md:26-30` **Vulnerability Type**: Supply-chain exposure through an unpinned installer and mutable source **Risk Level**: High ### Complete Code Snippet ```markdown ## Installation ```bash npx add https://github.com/wpank/ai/tree/main/skills/tools/session-handoff ``` ``` ### Technical Analysis The documented installation command invokes the `add` package through `npx` without pinning its package version. It also installs content from the mutable `main` branch of a personal GitHub repository rather than from an immutable, reviewed commit. Consequently, the code executed or installed when a user follows these instructions can differ from the code reviewed during this audit. Compromise of the npm package, npm publisher account, GitHub repository, repository owner account, or upstream branch could substitute malicious content without requiring changes to this project. The audited Python scripts themselves do not retrieve or execute remote payloads. The risk is specifically introduced by the documented installation procedure. ### Attack Path 1. An attacker compromises the npm package or publisher account associated with the unpinned `add` command, or compromises the referenced GitHub repository. 2. The attacker publishes a malicious installer version or modifies content on the mutable `main` branch. 3. A user follows the installation command from the README. 4. `npx` retrieves and runs the currently available package, which may execute with the user's operating-system privileges. 5. The installer retrieves or installs the attacker-controlled repository content. 6. Malicious installation-time code can access files and credentials available to the invoking account or install a modified skill for later execution. ### Impact Assessment Successful exploitation could result in arbitrary code execution with the privileges of the user running the installation command. The reachable scope may include the ...[truncated 325 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the npm installer to a reviewed version, rather than allowing `npx` to select the latest release. 2. Reference an immutable Git commit or signed release tag instead of the mutable `main` branch. 3. Publish and verify cryptographic checksums or signatures for distributed skill artifacts. 4. Prefer a transparent installation process that copies reviewed files without executing a transient third-party package. 5. If `npx` remains necessary, use an exact package version, review its dependency tree and lifecycle scripts, and document the expected package integrity hash. 6. Add automated monitoring for unexpected changes to installation sources and release artifacts. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/validate_handoff.py:22
Finding
Secret Validation Can Be Bypassed by Common Unquoted Credential Formats<![CDATA[ ## Vulnerability Details **File Location**: `scripts/validate_handoff.py:22-36` **Vulnerability Type**: Incomplete secret detection leading to false-safe validation **Risk Level**: Medium ### Complete Code Snippet ```python # Secret detection patterns SECRET_PATTERNS = [ (r'["\']?[a-zA-Z_]*api[_-]?key["\']?\s*[:=]\s*["\'][^"\']{10,}["\']', "API key"), (r'["\']?[a-zA-Z_]*password["\']?\s*[:=]\s*["\'][^"\']+["\']', "Password"), (r'["\']?[a-zA-Z_]*secret["\']?\s*[:=]\s*["\'][^"\']{10,}["\']', "Secret"), (r'["\']?[a-zA-Z_]*token["\']?\s*[:=]\s*["\'][^"\']{20,}["\']', "Token"), (r'["\']?[a-zA-Z_]*private[_-]?key["\']?\s*[:=]', "Private key"), (r'-----BEGIN [A-Z]+ PRIVATE KEY-----', "PEM private key"), (r'mongodb(\+srv)?://[^/\s]+:[^@\s]+@', "MongoDB connection string with password"), (r'postgres://[^/\s]+:[^@\s]+@', "PostgreSQL connection string with password"), (r'mysql://[^/\s]+:[^@\s]+@', "MySQL connection string with password"), (r'Bearer\s+[a-zA-Z0-9_\-\.]+', "Bearer token"), (r'ghp_[a-zA-Z0-9]{36}', "GitHub personal access token"), (r'sk-[a-zA-Z0-9]{48}', "OpenAI API key"), (r'xox[baprs]-[a-zA-Z0-9-]+', "Slack token"), ] ``` ### Technical Analysis The generic API-key, password, secret, and token expressions require the credential value to begin and end with quotation marks. Common environment and configuration formats such as the following therefore evade these expressions: ```text API_KEY=actual-secret-value password=actual-password ACCESS_TOKEN=long-unquoted-token ``` The provider-specific expressions cover only a limited set of formats and exact legacy lengths. Credentials from other providers, newer token formats, and arbitrary high-entropy secrets may consequently remain undetected. This is security-relevant because the validator is described as checking for accidental secrets, and its final verdict can report a handoff as ready when `secrets_found` is empty. A regex denylist cannot provi ...[truncated 1175 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Support quoted and unquoted assignment values, including shell, dotenv, YAML, JSON, and Markdown forms. 2. Add maintained provider-specific patterns for commonly used credentials, without relying exclusively on fixed legacy token lengths. 3. Supplement pattern matching with entropy analysis and contextual indicators such as `key`, `secret`, `password`, `credential`, and `token`. 4. Redact matched values from all output and diagnostics. 5. Integrate a mature secret-scanning tool where available and pin its version. 6. Change the report language to clarify that no scanner can guarantee the absence of secrets. 7. Add regression tests for unquoted assignments, multiline private keys, connection strings, authorization headers, and modern provider token formats. 8. Consider blocking finalization when high-entropy values occur near credential-related labels, even if their exact provider is unknown. ]]>

T05 · Unauthorized Access and Privilege Escalation

Note
Location
scripts/check_staleness.py:219
Finding
Untrusted Handoff Metadata Controls the Directory Used for Git Inspection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_staleness.py:219-231` **Related Input Parsing**: `scripts/check_staleness.py:58-62` **Vulnerability Type**: Untrusted path usage and out-of-scope local repository inspection **Risk Level**: Low ### Complete Code Snippet The project path is parsed directly from handoff content: ```python # Parse Project path match = re.search(r'Project:\s*(.+?)(?:\n|$)', content) if match: metadata["project_path"] = match.group(1).strip() ``` It is then trusted as the working directory for Git commands: ```python # Determine project path project_path = metadata.get("project_path") if not project_path or not Path(project_path).exists(): # Fallback: assume handoff is in .claude/handoffs/ within project project_path = str(path.parent.parent.parent) # Check if git repo success, _ = run_cmd(["git", "rev-parse", "--git-dir"], cwd=project_path) is_git_repo = success result = { "handoff_file": str(path), "project_path": project_path, "is_git_repo": is_git_repo, "created": metadata["created"], "handoff_branch": metadata["branch"], } ``` ### Technical Analysis The `Project:` field is controlled by the contents of the supplied handoff document. If it names any existing directory accessible to the current user, the script accepts it without requiring that it correspond to the project containing the handoff. Subsequent operations run fixed-argument Git commands in that directory and can collect the current branch, commit messages, and changed filenames. Fixed argument arrays avoid shell metacharacter injection, so this is not command injection. The issue is instead a missing trust-boundary check: untrusted document metadata determines the scope of local inspection. ### Attack Path 1. An attacker prepares a handoff containing a `Project:` field that points to another likely local repository. 2. The attacker persuades a user or agent to run `check_staleness.py` against that handoff ...[truncated 1089 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat the handoff's `Project:` field as informational rather than authoritative. 2. Derive the default project root from the handoff location, such as the parent of `.claude/handoffs`. 3. Resolve paths with `Path.resolve()` and require the selected path to equal the derived project root or fall within an explicitly approved root. 4. If the recorded path differs from the derived root, stop and require explicit user confirmation before running Git commands. 5. Add an explicit command-line option for intentional cross-project inspection rather than enabling it through document metadata. 6. Avoid printing unnecessary repository metadata and paths. 7. Add tests using crafted absolute paths, relative traversal paths, symlinks, nonexistent paths, and paths outside the containing project. ]]>
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
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented purpose and the observed behavior are not tightly aligned: the skill description emphasizes creating and resuming handoff documents, while the workflow includes filesystem inspection, metadata extraction, and repository-state gathering. This mismatch can mislead users and reviewers about the real operational scope, causing them to authorize broader access than they intended.

Credential Access

High
Category
Privilege Escalation
Content
(r'postgres://[^/\s]+:[^@\s]+@', "PostgreSQL connection string with password"),
    (r'mysql://[^/\s]+:[^@\s]+@', "MySQL connection string with password"),
    (r'Bearer\s+[a-zA-Z0-9_\-\.]+', "Bearer token"),
    (r'ghp_[a-zA-Z0-9]{36}', "GitHub personal access token"),
    (r'sk-[a-zA-Z0-9]{48}', "OpenAI API key"),
    (r'xox[baprs]-[a-zA-Z0-9-]+', "Slack token"),
]
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Session Persistence

Medium
Category
Rogue Agent
Content
# Session Handoff

Create comprehensive handoff documents that enable fresh AI agents to seamlessly continue work with zero ambiguity. Solves the long-running agent context exhaustion problem.

## What's Inside
Confidence
90% confidence
Finding
The skill is explicitly designed to persist detailed session state so a fresh agent can resume with 'zero ambiguity'. Session persistence is inherently risky because handoff documents may contain sensitive data, internal reasoning summaries, credentials, tokens, file paths, or other context that outlives the original session and can be accessed later by unintended parties or agents.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The listed trigger phrases are broad, common conversational language such as 'continue where we left off' and 'save progress', which can cause the skill to activate in unintended contexts. In an agent environment, overbroad activation increases the chance that session data is captured, persisted, or reused when the user did not explicitly request that behavior.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The README instructs users to install directly from a remote GitHub source via `npx add` without any pinned version, tag, or commit. That creates a supply-chain risk because the referenced content can change over time, causing future installs to pull unexpected or malicious code.

Skill Enumeration

Medium
Category
Agent Snooping
Content
From your project root:

```bash
mkdir -p .claude/skills
cp -r ~/.ai-skills/skills/tools/session-handoff .claude/skills/session-handoff
```
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
#### Claude Code (global)

```bash
mkdir -p ~/.claude/skills
cp -r ~/.ai-skills/skills/tools/session-handoff ~/.claude/skills/session-handoff
```
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill instructs the agent to read and write files and execute shell commands, but it declares no explicit tool scope or permission boundaries. That creates an overbroad execution surface where a caller may trigger filesystem and shell activity without clear least-privilege constraints, increasing the risk of unintended file modification, data exposure, or command execution.

External Model or Provider Selection

Medium
Category
Excessive Agency
Content
---
name: session-handoff
model: standard
description: |
  WHAT: Create comprehensive handoff documents that enable fresh AI agents to seamlessly continue work with zero ambiguity. Solves long-running agent context exhaustion problem.
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The activation keywords include broad, common phrases such as 'save progress' and 'continue where we left off' that can occur in normal conversation. This raises the chance of accidental invocation, which is more dangerous here because the skill can trigger file operations and shell-based workflows.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_cmd(cmd: list[str], cwd: str = None) -> tuple[bool, str]:
    """Run a command and return (success, output)."""
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_cmd(cmd: list[str], cwd: str = None) -> tuple[bool, str]:
    """Run a command and return (success, output)."""
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.