Back to skill

Security audit

CSV Inspect

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local CSV preview helper with some real data-exposure and terminal-output hardening risks, but no hidden persistence, exfiltration, or destructive behavior.

Install only if you are comfortable with a local CLI previewing selected CSV contents in the terminal. Avoid running it on highly sensitive, attacker-supplied, or extremely large CSVs until output sanitization and bounded encoding detection are added; use --json when safer structured output is preferred.

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

Warning
Location
scripts/csv-inspect:174
Finding

Unescaped Terminal Control Sequences in CSV Output

Content
View full analysis

Vulnerability Details

File Location: scripts/csv-inspect, lines 174–205
Vulnerability Type: Terminal output injection
Risk Level: Medium

Vulnerable Code

python
for name in info["names"]:
    lines.append(f"  {name}")
lines.append(f"types (from first {info.get('scan_rows', 0)} data rows):")
for col in info["types"]:
    sample = ", ".join(col["sample"]) if col["sample"] else ""
    extra = f"  sample=[{sample}]" if sample else ""
    lines.append(
        f"  {col['name']}: {col['type']}  empty={col['empty_in_scan']}{extra}"
    )
python
for row in rows:
    out.append(
        "  "
        + " | ".join(
            (row[i] if i < len(row) else "")[: widths[i]].ljust(widths[i])
            for i in range(len(names))
        )
    )

Technical Analysis

Column names, sampled values, and displayed rows originate from the inspected file and are inserted directly into text output. The program does not escape ASCII control characters, the escape character, or ANSI/OSC terminal sequences.

Although limiting strings to 40 Python characters constrains payload length in the table, it does not neutralize terminal sequences. Samples and names outside the table are not subject to that limit. When text output is connected to a compatible terminal, a malicious CSV can therefore alter colors, move or erase terminal content, create deceptive hyperlinks, change a terminal title, or trigger other terminal-specific behavior. The exact effect depends on terminal capabilities and configuration.

JSON mode is less exposed because json.dump escapes control characters, but ordinary text mode is the default.

Attack Path

  1. An attacker creates or supplies a CSV containing ANSI, OSC, or other terminal control sequences in a header or cell.
  2. A user or agent invokes csv-inspect on that untrusted file in default text mode.
  3. The parser preserves the embedded sequence as ...[truncated 730 chars]
Remediation
View remediation

Remediation Suggestions

Introduce one centralized display-escaping function and apply it to every untrusted field before computing display widths or constructing text output.

The sanitizer should:

  • Render C0 and C1 controls, ESC, DEL, carriage returns, newlines, tabs, and other non-printable characters as visible escaped forms.
  • Neutralize complete ANSI CSI, OSC, DCS, APC, and related escape sequences.
  • Preserve the original values only in structured internal data.
  • Continue using JSON serialization for JSON mode rather than manually constructing JSON.
  • Optionally provide an explicit, prominently documented raw-output mode, while keeping sanitized output as the default.

Example hardening pattern:

python
def display_safe(value: str) -> str:
    return value.encode("unicode_escape").decode("ascii")

safe_names = [display_safe(name) for name in names]
safe_rows = [[display_safe(cell) for cell in row] for row in rows]

A production implementation should also account for Unicode formatting and bidirectional control characters. Add regression tests using CSI color/erase sequences, OSC hyperlinks, embedded newlines, carriage returns, tabs, and Unicode bidi controls.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/csv-inspect:27
Finding

Unbounded Memory Consumption During Encoding Detection

Content
View full analysis

Vulnerability Details

File Location: scripts/csv-inspect, lines 27–29
Vulnerability Type: Local denial of service through unrestricted file read
Risk Level: Medium

Vulnerable Code

python
def detect_encoding(path: Path, nbytes: int = 65536) -> str:
    raw = path.read_bytes()[:nbytes]
    if raw.startswith(b"\xff\xfe") or raw.startswith(b"\xfe\xff"):

Technical Analysis

Path.read_bytes() reads the entire file into a bytes object before the slice retains only the first nbytes. Consequently, a multi-gigabyte input can cause a similarly large temporary allocation even though encoding detection requires only 65,536 bytes.

This defeats the documented streaming design and exposes the process to memory exhaustion. The later CSV row-counting operation streams the file, but it occurs only after the full-file allocation in detect_encoding().

Attack Path

  1. An attacker provides or directs the user to inspect an extremely large local file presented as a delimited table.
  2. The user or agent runs csv-inspect with that path.
  3. detect_encoding() calls path.read_bytes(), attempting to load the complete file into memory.
  4. Only after that allocation does Python slice the result to 65,536 bytes.
  5. Available memory may be exhausted, causing severe memory pressure, process termination, or broader host instability.

Impact Assessment

Exploitation grants no additional permissions and does not provide code execution. The impact is availability loss within the privileges of the process. At minimum, the inspection process may be terminated; on a resource-constrained system, concurrent applications or the host may also experience memory pressure. Exploitation requires the victim to inspect an attacker-controlled or attacker-selected oversized readable file.

Remediation
View remediation

Remediation Suggestions

Open the file in binary mode and read only the required prefix:

python
def detect_encoding(path: Path, nbytes: int = 65536) -> str:
    with path.open("rb") as fh:
        raw = fh.read(nbytes)
    if raw.startswith(b"\xff\xfe") or raw.startswith(b"\xfe\xff"):
        return "utf-16"
    # Continue existing detection logic.

Additional hardening should include:

  • Validate that nbytes is non-negative and impose a small upper bound if it can ever become user-controlled.
  • Add a regression test that uses a large sparse file and verifies bounded peak memory usage.
  • Document that complete row counting still performs an O(file-size) streaming read and may require substantial time, even after memory usage is corrected.
  • Consider an optional maximum-byte or maximum-row processing limit for environments that inspect untrusted files under strict resource constraints.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (4)

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · README.md (reported line 22)May include surrounding context.

clawhub install csv-inspect

or use this folder as the skill root

mkdir -p ~/.local/bin ln -sf /absolute/path/to/skill/scripts/csv-inspect ~/.local/bin/csv-inspect

text

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script intentionally prints head/tail rows and per-column sample values taken directly from the inspected file, which can expose sensitive data such as PII, credentials, tokens, or financial records to the terminal, logs, or downstream tooling. In this skill’s context, previewing CSV contents is its core purpose, so the behavior is expected, but it still creates a real data-exposure risk because there is no warning, masking, or safer default mode.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

Scanner excerpt · LICENSE (reported line 12)May include surrounding context.

text
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE

Static analysis

No suspicious patterns detected.