Back to skill

Security audit

Research Library

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local research-library skill, but it needs Review because its URL import and project-path handling create concrete file and network safety risks.

Review before installing. Prefer the ClawHub-reviewed package path over the unpinned pip name, run it as a normal user, avoid importing untrusted URLs or FTP links, use simple safe project IDs, and assume indexed metadata and exports may include sensitive file contents, GPS, author, or device information.

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

T09 · Insecure Skill Coding Practices

Error
Location
reslib/cli.py:539
Finding
Unrestricted Remote Document Retrieval Enables Server-Side Request Forgery and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `reslib/cli.py:283-289` and `reslib/cli.py:539-552` **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unbounded remote download **Risk Level**: High ### Vulnerable Code ```python def is_url(s: str) -> bool: """Check if a string is a URL.""" try: result = urlparse(s) return result.scheme in ("http", "https", "ftp") except Exception: return False ``` ```python if is_url(path): source_url = path # Download the file if not quiet and not json_output: echo_info(f"Downloading from {path}...") try: import urllib.request import tempfile # Create temp file with appropriate extension parsed = urlparse(path) ext = Path(parsed.path).suffix or ".bin" with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp: urllib.request.urlretrieve(path, tmp.name) file_path = Path(tmp.name) except Exception as e: echo_error(f"Failed to download: {e}") ctx.exit(1) ``` ### Technical Analysis The `add` command retrieves a user-supplied URL without validating its destination. The implementation accepts HTTP, HTTPS, and FTP URLs, but does not: - Reject loopback, link-local, private, multicast, or reserved IP addresses. - Block cloud instance metadata endpoints. - Validate DNS resolutions before connecting. - Revalidate destinations after HTTP redirects. - Apply explicit connection or read timeouts. - Impose a maximum response or downloaded-file size. - Restrict remote retrieval to approved domains. Consequently, a caller can direct the process to request services that are reachable from the machine running the CLI but are not reachable from the caller's own network position. Redirects and DNS rebinding may also bypass checks if only the original URL is validated in a future partial fix. Because the downloaded response is subsequently hashed, copied into the attac ...[truncated 2009 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable remote ingestion by default and require an explicit option to enable it. 2. Permit only HTTPS, or HTTP and HTTPS when HTTP is operationally necessary. Remove FTP support. 3. Resolve the hostname before connecting and reject every address in loopback, private, link-local, multicast, unspecified, and reserved ranges. 4. Apply the same hostname and IP validation after every redirect. 5. Prevent DNS rebinding by connecting to a validated resolved address while preserving correct TLS hostname verification. 6. Configure short connection and read timeouts. 7. Stream downloads in fixed-size chunks instead of using `urlretrieve()`. 8. Abort when the response exceeds a configured maximum size. 9. Validate `Content-Length` when present, but do not rely on it as the sole size control. 10. Consider an allowlist of trusted hosts for environments where remote ingestion is required. 11. Ensure partially downloaded temporary files are deleted in a `finally` block. 12. Add tests covering loopback addresses, IPv6 loopback, private ranges, link-local metadata endpoints, redirects to private addresses, DNS rebinding scenarios, timeouts, and oversized responses. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
reslib/cli.py:597
Finding
Unsanitized Project Identifier Allows Attachment Writes Outside the Data Directory<![CDATA[ ## Vulnerability Details **File Location**: `reslib/cli.py:294-304` and `reslib/cli.py:597-602` **Vulnerability Type**: Path traversal and arbitrary file placement **Risk Level**: High ### Vulnerable Code ```python def validate_project_exists(conn: sqlite3.Connection, project_id: str) -> bool: """Check if a project exists, creating it if auto-create is enabled.""" cursor = conn.execute("SELECT id FROM projects WHERE id = ?", (project_id,)) if cursor.fetchone(): return True # Auto-create project conn.execute( "INSERT INTO projects (id, name) VALUES (?, ?)", (project_id, project_id.replace("-", " ").title()) ) conn.commit() return True ``` ```python # Copy file to attachments directory dest_dir = attachments_dir / project / datetime.now().strftime("%Y-%m") dest_dir.mkdir(parents=True, exist_ok=True) dest_path = dest_dir / f"{file_hash[:8]}_{file_path.name}" if not dest_path.exists(): shutil.copy2(file_path, dest_path) ``` ### Technical Analysis The user-controlled `project` value is used directly as a filesystem path component. `validate_project_exists()` only creates a database record; it does not validate the identifier as a safe single path segment. Python path composition does not enforce containment: - A project value containing `../` can traverse above `attachments_dir`. - An absolute project path can replace the preceding `attachments_dir` portion entirely. - Nested separators allow callers to create an arbitrary directory hierarchy. The destination filename contains a generated hash prefix, which limits direct control over the final filename. Nevertheless, the directory location is attacker-controlled, and a copy of an attacker-selected source file can be placed anywhere writable by the process. The application does not resolve the destination and confirm that it remains beneath the configured attachment root before calling `mkdir()` and `copy2()`. ### Attack Path 1. The atta ...[truncated 1493 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat project IDs as identifiers, not paths. 2. Enforce a conservative allowlist, such as: ```python PROJECT_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$") ``` 3. Reject identifiers containing path separators, `.` or `..` path components, drive prefixes, null bytes, or absolute paths. 4. Resolve the attachment root and candidate destination before creating directories: ```python root = attachments_dir.resolve() candidate = (root / project / datetime.now().strftime("%Y-%m")).resolve() try: candidate.relative_to(root) except ValueError: raise click.ClickException("Invalid project path") ``` 5. Perform containment validation on the final destination path as well as its parent. 6. Avoid following attacker-controlled symlinks beneath the attachment root. Where possible, use descriptor-relative filesystem operations and reject symlinked path components. 7. Set restrictive permissions on newly created library directories and files. 8. Add tests for `../`, absolute POSIX paths, Windows drive paths, UNC paths, nested separators, encoded separators, and symlink escapes. 9. Consider replacing user-derived directory names with generated internal project IDs while storing display names only in SQLite. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
reslib/worker.py:916
Finding
Extraction Timeout Leaves Parser Threads Running Indefinitely<![CDATA[ ## Vulnerability Details **File Location**: `reslib/worker.py:916-940` **Vulnerability Type**: Ineffective timeout and denial of service **Risk Level**: Medium ### Vulnerable Code ```python def _extract_with_timeout( self, path: Path, mime_type: Optional[str], timeout: int ) -> ExtractionResult: """ Run extraction with timeout. Uses a separate thread with join timeout to enforce extraction limits. """ result_holder = {"result": None, "error": None} def do_extract(): try: result_holder["result"] = self.extractors.extract(path, mime_type) except Exception as e: result_holder["error"] = e thread = threading.Thread(target=do_extract) thread.start() thread.join(timeout=timeout) if thread.is_alive(): # Extraction timed out # Note: We can't actually kill the thread in Python, but we can # proceed and let it finish in the background raise ExtractorTimeoutError( f"Extraction timed out after {timeout}s for {path}" ) if result_holder["error"]: raise result_holder["error"] return result_holder["result"] ``` ### Technical Analysis The code presents the thread join interval as an extraction timeout, but it only limits how long the worker waits. Python cannot safely terminate the running thread, so a timed-out parser continues executing in the background. The spawned thread is also non-daemonic by default. A hung parser can therefore remain active indefinitely and may prevent clean process termination. When multiple extraction jobs time out, each job can leave another active parser thread behind. The normal worker loop then continues to accept further jobs and spawn additional threads. The nominal worker-count limit consequently does not bound the actual number of concurrent extraction operations. Document parsers process complex, attacker-influenced formats such as PDFs and images. Malformed ...[truncated 1255 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Execute each untrusted document extraction in a separate process rather than a thread. 2. Terminate and reap the child process when the timeout expires. 3. Apply operating-system resource limits for: - CPU time. - Address-space or resident memory. - Open files. - Output size. - Child processes. 4. Run parser processes under a dedicated, unprivileged account or sandbox with no network access and limited filesystem visibility. 5. Limit input file size before invoking a parser. 6. Enforce format-specific limits, including PDF page count, image dimensions, decompressed image size, archive expansion ratio, and maximum extracted-text length. 7. Bound the number of active and recently timed-out extraction processes. 8. Use retry policies that do not repeatedly process deterministic timeout inputs. 9. Record and quarantine files that exceed resource limits. 10. Add stress tests confirming that repeated timeouts do not increase active parser concurrency beyond the configured bound. ]]>

T08 · Insecure Dependencies

Warning
Location
_meta.json:31
Finding
Unpinned Registry Installation Instruction Can Resolve to Unreviewed Package Code<![CDATA[ ## Vulnerability Details **File Location**: `_meta.json:31` **Vulnerability Type**: Unverified and unpinned software supply-chain dependency **Risk Level**: Medium ### Vulnerable Code ```json "installation_instructions": "pip install research-library or clawhub install research-library" ``` Related mutable dependency declarations are also present: ```json "dependencies": [ "click>=8.0", "pdfplumber>=0.10", "pytesseract>=0.3.10" ] ``` ### Technical Analysis The metadata directs users to install `research-library` from the Python package registry by name without an exact version, artifact hash, verified source URL, or immutable commit reference. The audited artifact does not contain packaging metadata such as `pyproject.toml`, `setup.py`, or a lock file that establishes a verifiable relationship between the reviewed files and the package obtained by the documented `pip install research-library` command. As a result, following the installation instruction may retrieve code different from the audited artifact. Registry ownership changes, package-name collisions, dependency confusion, or a compromised release account could cause installation of attacker-controlled code. Python package installation may execute build backend or setup logic before the package is used. The broad `>=` dependency constraints also permit future dependency versions that were not included in this audit. ### Attack Path 1. A user follows the documented command `pip install research-library`. 2. The package installer resolves the mutable registry name rather than the locally reviewed artifact. 3. A malicious, compromised, or unrelated distribution is returned under that name, or a mutable transitive dependency resolves to a compromised release. 4. Package build or installation logic executes with the user's privileges. 5. The installed package can then access the same files, environment, network, and user permissions available to the Python installation process. This ...[truncated 604 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the registry-by-name instruction with installation from the reviewed artifact or an official repository pinned to an immutable commit. 2. Publish complete packaging metadata that identifies the official distribution and source repository. 3. Pin the application and dependencies to reviewed versions. 4. Use a lock file and require package hashes, for example with `pip --require-hashes`. 5. Publish signed releases and verify signatures or attestations during installation. 6. Document the authoritative package owner and registry namespace. 7. Use reproducible builds and provide checksums for released artifacts. 8. Audit transitive dependencies and generate a software bill of materials. 9. Automate vulnerability and package-integrity scanning in the release pipeline. 10. Avoid claiming that `pip install research-library` installs this exact reviewed code unless that mapping is established and verifiable. ]]>
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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Rogue AgentSelf-Modification, Session Persistence
Findings (35)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The documented behavior claims a local research library with search, isolation, backup, restore, and async extraction, but the available evidence instead emphasizes test execution and subprocess usage without demonstrating those core features. This mismatch is dangerous because users and orchestrators may trust the declared purpose while the skill performs other actions, including running pytest via subprocess, which can execute arbitrary code in the repository context.

Missing User Warnings

High
Confidence
96% confidence
Finding
The restore command documentation does not clearly warn that restoring a backup can overwrite the current database state and discard more recent changes. In a local research library handling user documents and metadata, ambiguous restore guidance can cause destructive data loss, especially when paired with `--force` and automation.

Context Leakage

High
Category
Data Exfiltration
Content
snippet += "..."
        return snippet[:max_length]
    
    # Extract context around match
    start = max(0, match_idx - context_words)
    end = min(len(words), match_idx + context_words + 1)
Confidence
75% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Memory Manipulation

High
Category
Memory Poisoning
Content
if released_count:
                logger.info(f"Released {released_count} in-progress jobs back to queue")
            
            # Clear state
            self._worker_threads.clear()
            self._heartbeat_thread = None
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
#### Manage Projects
```bash
reslib projects list
reslib projects create --name "CNC Tool Changer"
reslib projects archive arduino  # Soft-delete old project
```
Confidence
60% 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.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This markdown file documents `reslib backup` and especially `reslib restore` as normal commands, but it does not warn that restore can replace or roll back the user's local library state. Because restore affects stored user data and system state, the skill description should disclose that impact before users run it.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises capabilities that imply file access, shell execution, and possibly network use, but it does not declare any explicit tool scope or permissions boundary. In a skill ecosystem, this increases the chance that an agent invokes the skill with broader privileges than users expect, enabling unintended file modification, command execution, or outbound access.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
Backup and restore operations inherently carry overwrite and data-loss risk, but the skill documentation does not warn users about destructive restore behavior or recovery limitations. In a system handling local research data, an unsafe restore flow could overwrite current state, corrupt projects, or lead to irreversible loss.

Session Persistence

Medium
Category
Rogue Agent
Content
- `reslib get` — View document details
- `reslib archive` / `reslib unarchive` — Manage documents
- `reslib export` — Export as JSON/Markdown
- `reslib link` — Create document relationships
- `reslib projects` — Manage projects
- `reslib tags` — Manage tags
- `reslib status` — System overview
Confidence
74% confidence
Finding
The skill persists and organizes user research artifacts, links, tags, and project state over time, but there is no clear disclosure of retention boundaries, lifecycle controls, or safeguards around stored session-like knowledge. Persistent cross-referenced data can accumulate sensitive project context and later be surfaced unexpectedly through search, export, or restore operations.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```

**Options:**
- `-f, --force` — Archive without confirmation

**Examples:**
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```

**Options:**
- `-f, --force` — Archive without confirmation

**Examples:**
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```

**Options:**
- `-f, --force` — Archive without confirmation

**Examples:**
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**Options:**
- `--list` — List available backups instead of restoring
- `-f, --force` — Restore without confirmation

**Examples:**
Confidence
88% confidence
Finding
A `restore --force` option removes the confirmation barrier for an operation that can overwrite current state, making destructive restoration easier in scripts or by mistaken invocation. In the context of a local-first document library with backups and project data, this materially increases the risk of accidental or automated data loss.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This markdown file documents a bulk operation that archives every research item in a project using `xargs` with `reslib archive {} --force`. Although archiving is described elsewhere as reversible, this example encourages a sweeping data-modifying action without any warning to verify the target project or review the matched IDs before execution.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The guide explicitly documents extraction of sensitive metadata such as EXIF GPS coordinates and PDF author/creation data, but it does not warn users that this metadata may contain personal, location, or organizational information. In a local-first research library that indexes many file types, silently extracting and surfacing such metadata increases the risk of privacy leakage through search, display, export, backups, or cross-project references.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Install All
```bash
pip install pdfplumber pillow pytesseract
sudo apt install tesseract-ocr  # Debian/Ubuntu
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The URL validator permits ftp, http, and https, enabling network retrieval beyond a strictly local workflow. Supporting FTP is especially risky because it is plaintext and unauthenticated by default, increasing exposure to tampering, credential leakage, and unsafe remote ingestion.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The add command accepts arbitrary URLs and downloads them directly, which contradicts the stated local-first model and expands the attack surface to remote content ingestion. This can enable SSRF-style access to internal services if the CLI runs in a trusted environment, or at minimum cause unexpected network egress and ingestion of untrusted files.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The export command writes document content, tags, links, and potentially extracted full text to an output file, but there is no confirmation prompt or explicit user-facing warning immediately before the write. Although the command name implies export, the code path performs a potentially privacy-impacting file write of stored research contents without additional disclosure at the point of action.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The confidence heuristic rewards text containing a hard-coded set of common English words, which disadvantages documents in other languages without offering user choice or documenting the limitation. This effectively bakes in an English-first assumption in natural-language processing behavior, which can violate language/locale neutrality expectations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The image extractor explicitly converts EXIF metadata into searchable text and includes sensitive fields such as GPSInfo, CameraOwnerName, BodySerialNumber, XPAuthor, and related identifiers in returned extraction results. In a research library that indexes and cross-references files, this can unintentionally expose location, device, and owner-identifying information to users, logs, search indexes, backups, or downstream consumers without consent or disclosure.

Session Persistence

Medium
Category
Rogue Agent
Content
deduplicate: bool = True
    ) -> Optional[int]:
        """
        Add a job to the extraction queue.
        
        Args:
            attachment_id: ID of attachment to process
Confidence
80% 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.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The class docstring and __init__ documentation say the ranking engine can be initialized with custom confidence, recency, and relevance weights, implying instance-specific scoring behavior. However, score() and score_with_breakdown() delegate to compute_rank_score(), which always uses the module-level constants CONFIDENCE_WEIGHT, RECENCY_WEIGHT, and RELEVANCE_WEIGHT instead of self.confidence_weight, self.recency_weight, and self.relevance_weight.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest emphasizes a local-first research library with search, but this file is specifically documented as a full-text search engine. In addition to searching, the constructor creates directories and may create a new database schema, which is materially broader than read/query behavior for a search component.

Description-Behavior Mismatch

Medium
Confidence
85% confidence
Finding
The manifest states 'Project isolation with cross-references,' which implies boundaries between projects with limited exceptions for linked items. This file includes unrestricted search across all projects, allowing enumeration of content from every project rather than only isolated per-project access with cross-reference traversal.

Static analysis

No suspicious patterns detected.