Back to skill

Security audit

Pet Companion Journal

Security checks for vulnerabilities and agentic risk

Overview

The skill is a mostly transparent local pet journal, but it needs review because pet IDs are not validated and can make the scripts read or write JSON files outside the promised pet-data folder.

Review or patch the pet_id path handling before relying on this skill. If installing anyway, set PET_COMPANION_HOME to an isolated directory, avoid manually supplied pet IDs with slashes or dots, and remember that pet health notes and photo references are stored locally in plaintext. Expect some Chinese output templates and UTC+8 timestamps unless customized.

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

Error
Location
scripts/common.py:44
Finding

Path Traversal Through Unvalidated Pet Identifiers

Content
View full analysis
Dict[str, Any]: path = storage_root() / 'pets' / f'{pet_id}.json' data = read_json(path) if not data: raise SystemExit(f'Pet not found: {pet_id}') return data ``` ```python # scripts/pet_manager.py:9-10 pet_id = args.pet_id or slugify(args.name) path = storage_root() / 'pets' / f'{pet_id}.json' ``` ```python # scripts/pet_manager.py:36-37 path = storage_root() / 'pets' / f'{args.pet_id}.json' data = read_json(path) ``` ```python # scripts/pet_manager.py:56-57 path = storage_root() / 'pets' / f'{args.pet_id}.json' data = read_json(path) ``` ```python # scripts/reminder_manage.py:9-10 load_pet(args.pet_id) path = storage_root() / 'reminders' / f'{args.pet_id}.json' ``` ```python # scripts/reminder_manage.py:27-28 path = storage_root() / 'reminders' / f'{args.pet_id}.json' data = read_json(path, {'pet_id': args.pet_id, 'reminders': []}) ``` ```python # scripts/reminder_check.py:11-13 def load_reminders(pet_id=None): root = storage_root() / 'reminders' files = [root / f'{pet_id}.json'] if pet_id else sorted(root.glob('*.json')) ``` ### Technical Analysis User-supplied `pet_id` values are interpolated directly into filesystem paths. Only automatically generated identifiers pass through `slugify`; an explicit `--pet-id` is accepted without validation. Python's `pathlib` path composition does not enforce containment. A value containing `../` can escape the intended `pets` or `reminders` directory. If the final component is absolute, it replaces the preceding storage-root components entirely. The `.json` suffix limits targets to JSON filenames but does not ...[truncated 1978 chars]
Remediation
View remediation
str: if not PET_ID_PATTERN.fullmatch(pet_id): raise SystemExit('Invalid pet ID') return pet_id ``` 2. Reject absolute paths, directory separators, empty identifiers, `.` components, and `..` components rather than attempting to normalize them silently. 3. Resolve every constructed path and enforce containment: ```python def contained_json_path(directory: Path, pet_id: str) -> Path: pet_id = validate_pet_id(pet_id) base = directory.resolve() candidate = (base / f'{pet_id}.json').resolve() try: candidate.relative_to(base) except ValueError: raise SystemExit('Path escapes the storage directory') return candidate ``` 4. Apply the helper consistently in: - `load_pet` - Pet creation, update, and view operations - Reminder creation and listing - Reminder checking by pet ID 5. Do not rely solely on `slugify`, because it transforms invalid identifiers and may create collisions. Explicit identifiers should be validated and rejected if malformed. 6. Add regression tests covering: - `../` traversal - Absolute paths - Embedded `/` and `\` - Empty and dot-only identifiers - Valid identifiers - Verification that no file outside the designated directory is read or modified ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/common.py:60
Finding

Predictable Record Identifiers Permit Silent Record Overwrites

Content
View full analysis
str: return f"{prefix}_{datetime.now(TZ).strftime('%Y%m%d%H%M%S')}" ``` ```python # scripts/common.py:72-78 def write_record(frontmatter: Dict[str, Any], body: str) -> Path: dt = datetime.fromisoformat(frontmatter['created_at']) file_name = f"{dt.strftime('%Y-%m-%d')}-{frontmatter['type']}-{frontmatter['record_id']}.md" path = record_dir(dt) / file_name content = '---\n' + json.dumps(frontmatter, ensure_ascii=False) + '\n---\n\n' + body.strip() + '\n' path.write_text(content, encoding='utf-8') return path ``` ### Technical Analysis `make_record_id` uses the current timestamp with only one-second resolution. Two records created during the same second receive the same `record_id`. The output filename consists of the record date, record type, and generated identifier. Therefore, two records of the same type created in the same second resolve to the same path. `Path.write_text` opens the target for replacement rather than exclusive creation, so the later write silently destroys the earlier record. The supplied `created_at` value does not prevent this issue. It changes the directory and date prefix, while the identifier remains based on the current clock. Same-type records with matching output dates can still collide. ### Attack Path 1. Create a valid pet profile. 2. Submit two `record_add.py` operations for the same record type within one clock second. 3. Both calls receive the same timestamp-based `record_id`. 4. Both calls derive the same Markdown output path. 5. The second `write_text` call replaces the first file without warning. 6. Queries subsequently return only the later record. An attacker able to trigger rapid record creation could intentio ...[truncated 541 chars]
Remediation
View remediation
str: return f'{prefix}_{uuid4().hex}' ``` 2. Create record files exclusively so an existing record is never silently replaced: ```python with path.open('x', encoding='utf-8') as handle: handle.write(content) ``` 3. If an exclusive-create collision occurs, generate a new identifier and retry a bounded number of times. 4. Consider adding an atomic write process for record integrity: - Write complete content to a temporary file in the same directory. - Flush and synchronize it if durability is required. - Atomically move it into place without overwriting an existing record. 5. Add tests that create many same-type records concurrently and assert: - Every record has a distinct ID. - Every output path is distinct. - No existing file is overwritten. - The query result count matches the number of successful writes. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The implemented script is a narrow local CRUD tool for pet profile metadata stored under a local pets directory as JSON files. This partially aligns with the description's 'record pet basics' and privacy-conscious local storage aspects. However, the declared purpose substantially overstates the skill's functionality by claiming dedicated archives with journals, photos, feeding logs, health records, and reminders. None of those features appear in this code chunk except for a single avatar_media field and general notes, which are not equivalent to the richer archive and tracking capabilities described. There are no undeclared dangerous capabilities, but there is a clear description-to-behavior mismatch because the actual primary behavior is limited to pet profile management.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

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

md
Use `scripts/export_report.py` to produce a compact summary for a time range.

## Output Rules

- Keep responses warm, clear, and organized.
- For write actions, confirm what was saved and under which pet.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/verify.py (reported line 45)May include surrounding context.

python
raise SystemExit("clawhub.json version must match skill.json")

    with tempfile.TemporaryDirectory() as tmp:
        env = os.environ.copy()
        env["PET_COMPANION_HOME"] = str(Path(tmp) / "pet-data")

        print("[verify] creating pet profile")

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 11)May include surrounding context.

bash
export PET_COMPANION_HOME=/tmp/pet-companion-demo
python3 scripts/pet_manager.py create --pet-id tofu --name Tofu --species cat --breed Ragdoll --birthday 2022-05-01
python3 scripts/record_add.py --pet-id tofu --type health --title "Annual checkup" --body "Vet said overall condition looked normal; follow up on dental cleaning." --tags vet dental
python3 scripts/record_query.py --pet-id tofu --type health --keyword dental
python3 scripts/reminder_manage.py add --pet-id tofu --title "Dental follow-up" --reminder-type follow-up --due-at 2026-07-01T09:00:00+08:00

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs use of shell commands and local file operations (python3 scripts/*.py, reading and writing under ~/.pet-companion/) but does not declare any explicit tool scope or permissions boundary. That makes the effective capability surface broader and less auditable, increasing the chance of unintended file access, environment-variable abuse, or unsafe shell execution when the skill is invoked.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The skill is explicitly designed to persist pet profiles, photos, feeding logs, health records, reminders, and other ongoing notes across sessions. Persistent storage of potentially sensitive household and health-related data increases privacy risk, especially on shared machines or when defaulting to a user home directory without retention controls, access restrictions, or consent checks.

Content

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

md
---
name: pet-companion-journal
description: Create and maintain dedicated archives for each pet, including profiles, daily journals, photos, feeding logs, health records, and care reminders. Use when the user wants to record pet basics such as name and birthday, save pet photos with captions, track food and health history, review warm moments, or manage reminders per pet. Local-first and privacy-conscious.
version: 1.1.0
---

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The example values for user-facing fields such as name, nickname, breed, personality tags, notes, title, and reminder text are entirely in Chinese, which can implicitly steer implementations or generated content toward a specific language. The document does not state that the schema is China-specific or that other languages are equally supported, so this creates a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list for adding records includes very short, generic phrases such as '记个照片', '记录一下今天洗澡', and '帮我存一下这张照片', which can plausibly appear in ordinary conversation without the user intending to invoke this specific skill. In an agent environment, overly broad activation patterns can cause unintended invocation, leading to accidental persistence of private pet-related data or misrouting user intent into record-creation actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The query examples include broad prompts like '最近有没有异常', '最近的照片记录', and '查一下健康记录' without requiring a clear pet target or bounded scope. This can cause the skill to activate on vague conversational queries and expose or summarize sensitive pet history when the user may have meant a different task or a different pet.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

Timeline/review triggers such as '回顾一下这周发生了什么' and '过去一个月都记了什么' are generic enough to overlap with normal assistant requests unrelated to pets. If the skill activates on these phrases based on weak context, it may retrieve and present archived pet data unexpectedly, creating privacy and integrity risks through unintended access or confusing task interception.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file states the goal is to provide a Chinese output format and describes usage principles as a default, which imposes a language choice in the skill's behavior. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

TZ is fixed to UTC+8 and all generated timestamps use that setting, which imposes a specific locale-related behavior regardless of the user's actual timezone. This is a natural-language policy concern because the file offers no opt-in, configuration note, or justification for forcing one locale/timezone.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code emits user-facing report headings and labels entirely in Chinese, which imposes a specific language on all users. The file provides no opt-in, language selection, or justification that the skill is intentionally limited to a Chinese-speaking or region-specific context.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/verify.py (reported line 21)May include surrounding context.

python
def run(script: str, args: list[str], env: dict[str, str], parse_json: bool = True):
    result = subprocess.run(
        [sys.executable, str(SCRIPTS / script), *args],
        cwd=ROOT,
        env=env,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown file contains all example titles, tags, and bodies in Chinese, which can imply a fixed language requirement for the skill's outputs or inputs. Because there is no accompanying note that the language is optional, user-selectable, or intended for a China-specific audience, it may conflict with language/locale choice expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The code initializes a storage root from PET_COMPANION_HOME and creates multiple persistent directories, and later helper functions write JSON and markdown record files to that location. In this file there is no confirmation prompt, user-facing log/print, or explanatory comment/docstring disclosing that user data will be stored on disk.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/pet_manager.py (reported line 41)May include surrounding context.

python
if not data:
        raise SystemExit(f'Pet not found: {args.pet_id}')
    for field in ['name', 'species', 'breed', 'gender', 'birthday', 'adoption_date', 'color_markings', 'notes', 'avatar_media']:
        value = getattr(args, field)
        if value is not None:
            data[field] = value
    if args.nickname is not None:

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The code hard-codes UTC+8 via TZ = timezone(timedelta(hours=8)), which imposes a locale-specific time basis on reminder verification behavior. Under the policy, forcing a specific locale without user choice or clear justification is a natural-language policy concern, even in code.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest sets "language": "en", which indicates an English-only locale constraint. There is no nearby documentation offering user opt-in or explaining why the skill must be limited to English, so this appears to violate the language/locale policy criterion.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.