Back to skill

Security audit

Image Cropper

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent image-cropping tool, but its COCO mode can read image files outside the chosen image directory when given a crafted annotation file.

Review this skill before installing. Use it only on trusted annotation files or run it in a sandbox with access limited to the intended image dataset, especially for COCO input. Avoid reusing an output directory containing files you need to preserve, and prefer pinning Pillow in your environment.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cropper.py:188
Finding

COCO Annotation Filename Path Traversal Allows Unauthorized Local Image Access

Content
View full analysis

Vulnerability Details

File Location: scripts/cropper.py, lines 188-224
Vulnerability Type: Path traversal caused by an untrusted COCO file_name
Risk Level: Medium

Vulnerable Code

python
for img_id, img_info in images.items():
    img_name = img_info.get('file_name', f'{img_id}.jpg')
    img_path = images_dir / img_name
    
    if not img_path.exists():
        continue
    
    width = img_info.get('width', 0)
    height = img_info.get('height', 0)
    
    if width == 0 or height == 0:
        with Image.open(img_path) as img:
            width, height = img.size
    
    # Parse annotations
    annotations = parse_coco_annotation(coco_data, img_id)
    
    if not annotations:
        continue
    
    if args.objects:
        for i, bbox in enumerate(annotations):
            class_id, x1, y1, x2, y2 = bbox
            
            if args.min_size:
                if (x2 - x1) < args.min_size or (y2 - y1) < args.min_size:
                    continue
            
            cropped = crop_image(str(img_path), bbox, args.padding, width, height)
            base_name = Path(img_name).stem
            output_name = f"{base_name}_{i}.{args.format}"
            output_path = output_dir / output_name
            cropped.save(output_path, quality=args.quality)
            total_cropped += 1
    else:
        cropped = crop_image(str(img_path), annotations[0], args.padding, width, height)
        base_name = Path(img_name).stem
        output_path = output_dir / f"{base_name}_crop.{args.format}"
        cropped.save(output_path, quality=args.quality)
        total_cropped += 1

Technical Analysis

The COCO file_name property is read from an externally supplied JSON annotation and appended directly to images_dir. The application does not reject absolute paths, normalize traversal components, resolve symbolic links, or verify ...[truncated 1972 chars]

Remediation
View remediation

Remediation Suggestions

Resolve and validate every annotation-derived path before accessing it:

python
images_root = Path(args.images).resolve()
raw_name = img_info.get("file_name", f"{img_id}.jpg")
relative_name = Path(raw_name)

if relative_name.is_absolute():
    raise ValueError(f"Absolute image path is not allowed: {raw_name}")

img_path = (images_root / relative_name).resolve()

try:
    img_path.relative_to(images_root)
except ValueError:
    raise ValueError(f"Image path escapes the image directory: {raw_name}")

if not img_path.is_file():
    continue

The containment check must occur after resolution so that both .. traversal and symbolic-link escapes are rejected. Consider additionally allowing only expected filename suffixes, rejecting unexpected nested paths if dataset semantics do not require them, and logging rejected records without exposing sensitive absolute paths. Add regression tests covering absolute paths, ../ traversal, nested traversal, and symlinks that point outside the image root.

T08 · Insecure Dependencies

Note
Location
SKILL.md:39
Finding

Unpinned Pillow Dependency Creates a Non-Reproducible Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 39-43
Vulnerability Type: Unpinned third-party dependency installation
Risk Level: Low

Vulnerable Code

markdown
## Installation

```bash
pip install pillow
text

### Technical Analysis

The documented installation command retrieves whichever Pillow release satisfies pip's default resolution at installation time. No reviewed version, lock file, package hash, or package-index restriction is specified. Consequently, separate installations can receive different code even though the audited project remains unchanged.

This is a supply-chain hardening weakness rather than evidence that the current Pillow package is malicious. Exploitation depends on hostile or compromised package-index content, a compromised future dependency release, or manipulation of the package source used by pip.

### Attack Path

1. A user follows the documented installation command.
2. Pip queries its configured package index and resolves the current release of Pillow.
3. If the selected release or package source has been compromised, unreviewed code is installed into the environment.
4. Malicious package behavior may run during installation, import, or subsequent image processing with the privileges of the user running pip or the cropper.

### Impact Assessment

The immediate project contains no demonstrated malicious dependency payload. However, mutable dependency resolution prevents reproducible installation and allows dependency code to change after this Skill has been reviewed.

If the dependency source were compromised, code could run with the installing user's privileges. The resulting scope could include files, environment variables, network access, and other resources available to that account. The actual impact is contingent upon a separate supply-chain compromise and is therefore rated Low for this project.
Remediation
View remediation

Remediation Suggestions

Pin Pillow to a reviewed version in a dependency file and verify downloaded artifacts with hashes. For example:

text
Pillow==REVIEWED_VERSION --hash=sha256:REVIEWED_DISTRIBUTION_HASH

Install it using:

bash
python -m pip install --require-hashes -r requirements.txt

Record hashes for every supported platform artifact or deploy from a controlled internal package mirror. Automate dependency updates through a review process that runs tests and vulnerability scanning before changing the pinned version. The documentation should reference the locked dependency file instead of instructing users to install an unconstrained latest release.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (1)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This code creates and uses a user-supplied output directory for saving cropped images, which is a file-writing operation covered by the warning requirement for code files. While the script's purpose implies cropping, there is no explicit confirmation prompt or user-facing warning that running it will create files and may overwrite existing names in the target directory.

Content

No source excerpt is available for this finding.