Back to skill

Security audit

Workspace Hygiene — Auto-Clean for AI Agents

Security checks for vulnerabilities and agentic risk

Overview

This workspace-cleanup skill is purpose-aligned, but it automatically changes agent memory and instruction files with weak safeguards and can permanently delete small files.

Review this before installing or scheduling it. It is not showing exfiltration or deception, but it can automatically change future agent context, archive active files, and permanently delete short Markdown files. Use only after making the workspace path configurable, replacing deletion with quarantine/archive, adding backups and collision-safe moves, and requiring explicit approval or protected-section parsing before changing AGENTS.md or MEMORY.md.

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

T09 · Insecure Skill Coding Practices

Error
Location
workspace-hygiene.sh:34
Finding

Unsafe Line-Based Truncation of Agent Instruction and Memory Files

Content
View full analysis
5 entries === if [ -f "$WORKSPACE/MEMORY.md" ]; then mem_lines=$(wc -l < "$WORKSPACE/MEMORY.md") if [ "$mem_lines" -gt 80 ]; then # Archive excess, keep first 80 lines tail -n +81 "$WORKSPACE/MEMORY.md" >> "$ARCHIVE/memory-overflow-$TODAY.md" 2>/dev/null head -n 80 "$WORKSPACE/MEMORY.md" > "$WORKSPACE/MEMORY.md.tmp" mv "$WORKSPACE/MEMORY.md.tmp" "$WORKSPACE/MEMORY.md" ((lines_removed += mem_lines - 80)) ((trim_count++)) fi fi # === 3. AGENTS.md: trim if over 50 lines === if [ -f "$WORKSPACE/AGENTS.md" ]; then agent_lines=$(wc -l < "$WORKSPACE/AGENTS.md") if [ "$agent_lines" -gt 50 ]; then tail -n +51 "$WORKSPACE/AGENTS.md" >> "$ARCHIVE/agents-overflow-$TODAY.md" 2>/dev/null head -n 50 "$WORKSPACE/AGENTS.md" > "$WORKSPACE/AGENTS.md.tmp" mv "$WORKSPACE/AGENTS.md.tmp" "$WORKSPACE/AGENTS.md" ((lines_removed += agent_lines - 50)) ((trim_count++)) fi fi ``` ### Technical Analysis The script truncates `MEMORY.md` and `AGENTS.md` at fixed line boundaries without understanding their semantic structure. It does not distinguish action logs from active instructions, verify that a Markdown section remains complete, preserve designated security directives, or validate the resulting file. This behavior also differs from the documented workflow in `SKILL.md`, which says that bloat should first be identified and old content moved while leaving a pointer. The implementation instead assumes that every line after the configured limit is disposable from the active file. Although overflow content is appended to an archive, the active files are immediately replaced. Consequently, important safety constraints, operating rules, references, or active memory l ...[truncated 1331 chars]
Remediation
View remediation
` and ``. - Maintain a protected list of sections that must never be removed, including safety constraints and active-state references. - Parse Markdown sections and archive complete logical units rather than arbitrary lines. - Create a timestamped backup before modifying either file. - Write the replacement to a secure temporary file in the same directory, validate its contents, synchronize it to disk, and atomically rename it. - Confirm that archival succeeded before replacing the active file. - Leave a valid archive pointer as required by `SKILL.md`. - Consider requiring interactive approval before modifying `AGENTS.md`, because it controls future Agent behavior. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
workspace-hygiene.sh:83
Finding

Referenced Memory Files Are Archived Without Checking STATE.md

Content
View full analysis
/dev/null) now=$(date +%s) age_days=$(( (now - mtime) / 86400 )) if [ "$age_days" -gt 7 ]; then mv "$f" "$ARCHIVE/" 2>/dev/null ((archive_count++)) fi done ``` ### Technical Analysis The documented rule in `SKILL.md` only treats a file as stale when it has not been updated for at least seven days **and is not referenced by `STATE.md`**. The implementation checks only modification time and filename patterns. It never reads or resolves references from `STATE.md`. A file may remain actively required even when its modification time is old. Moving it changes its path and can invalidate references used by future Agent sessions or other workspace automation. Additionally, `stat` errors are suppressed. If `mtime` is empty, the subsequent arithmetic is not robustly validated. The primary confirmed issue, however, is the absence of the documented reference-protection check. ### Attack Path 1. `STATE.md` references a file under `memory/`, such as `memory/project-context.md`. 2. The referenced file remains valid and actively needed but is not modified for more than seven days. 3. The hygiene script runs. 4. The script identifies the file as stale based only on its modification time. 5. The file is moved to `memory/archive/`. 6. The original reference in `STATE.md` becomes stale, and later sessions may fail to load the required context. An actor able to manipulate timestamps or create references can ...[truncated 576 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
workspace-hygiene.sh:63
Finding

Archive Filename Collisions Can Overwrite Previously Retained Data

Content
View full analysis
/dev/null || date -v-3d +%Y-%m-%d 2>/dev/null) if [ -n "$cutoff" ]; then for f in "$WORKSPACE"/memory/20*.md; do [ ! -f "$f" ] && continue fname=$(basename "$f" .md) # Extract date prefix (YYYY-MM-DD) fdate=$(echo "$fname" | grep -oP '^\d{4}-\d{2}-\d{2}') [ -z "$fdate" ] && continue # Skip today's [ "$fdate" = "$TODAY" ] && continue # Archive if older than cutoff if [[ "$fdate" < "$cutoff" ]]; then mv "$f" "$ARCHIVE/" 2>/dev/null ((archive_count++)) fi done fi ``` ### Technical Analysis The script moves files into a flat archive directory while preserving their basenames. It does not check whether the destination already exists, generate a unique versioned filename, or use no-clobber behavior. On implementations where `mv` replaces an existing destination file, a newly archived file with the same basename can overwrite previously retained content. This contradicts the stated retention goal of never deleting information and makes the archive unsuitable as a reliable recovery mechanism. Errors are redirected to `/dev/null`, and `archive_count` is incremented regardless of whether `mv` succeeds. The resulting report can therefore claim a successful archive operation even when a collision or filesystem error prevented it. The same unsafe move pattern also appears elsewhere in the script, including root media moves, package-file moves, stale-memory archival, and fragment archival. ### Attack Path 1. An archived file already exists as `memory/archive/2026-01-01.md`. 2. A different file with the same basename is created or restored at `memory/2026-01-01.md`. 3. The file qualifies as older than the archival ...[truncated 891 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
workspace-hygiene.sh:111
Finding

Short Markdown Files Are Permanently Deleted Without Archival

Content
View full analysis
/dev/null ``` ### Technical Analysis The comment says that the operation removes empty files, but `-size -10c` matches every file smaller than ten bytes, not only zero-byte files. A short Markdown file can be valid and meaningful—for example, a compact status, sentinel, identifier, or control marker. The matching files are deleted directly through `find -delete`, with no archival copy, user confirmation, content check, or recovery mechanism. This conflicts with the rule in `SKILL.md` that memory files should never be deleted and should always be archived. Error output is discarded, so deletion problems are not visible in the hygiene report. Deletions are also absent from the reported archive and trim counters. ### Attack Path 1. A valid Markdown state or marker file smaller than ten bytes exists directly under `memory/`. 2. The hygiene script runs. 3. `find` matches the file based only on its byte size. 4. The file is permanently deleted rather than moved to the archive. 5. Any process or future Agent session depending on that marker no longer finds it. An attacker with workspace write access could intentionally make a target file shorter than ten bytes before a scheduled run. However, the vulnerability can also cause accidental loss without malicious involvement. ### Impact Assessment The issue does not increase the attacker's privileges, but it causes irreversible deletion within the direct `memory/` directory. The impact includes loss of state markers, short notes, workflow sentinels, or other compact Markdown content. The command is constrained by `-maxdepth 1` and `-name "*.md"`, so it does not recursively delete the archive. Nevertheless, exe ...[truncated 110 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill explicitly instructs the agent to delete empty files ('Empty files (<10 bytes) → delete') as part of an automated hygiene routine, but it provides no confirmation step, allowlist, or warning about destructive behavior. In a workspace-maintenance context, even small or intentionally empty sentinel/config files can be meaningful, so automated deletion increases the risk of unintended data loss or workflow breakage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script permanently deletes small markdown files in the workspace memory directory via 'find ... -delete' with no confirmation, quarantine step, or safety checks. In a cron-driven root-owned cleanup context, this can silently destroy legitimate notes or state files if they are briefly empty or truncated, causing loss of audit/history data and potentially destabilizing agent behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
62% confidence
Finding

The natural-language and configuration choice of /root/.openclaw/workspace forces operation in a root-scoped environment. This is an organizational-policy style constraint embedded in the file without any documented user choice or justification for why the skill must run only in that context.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
99% confidence
Finding

The section header at L034 states 'trim action log if >5 entries', which implies a threshold of 5 entries. However, the implemented condition at L037 checks for more than 80 lines and preserves the first 80 lines, so the documentation actively misstates the behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.