Back to skill

Security audit

Photo Grader

Security checks for vulnerabilities and agentic risk

Overview

The skill is for photo grading, but it needs Review because it executes an unaudited sibling Python module and its setup helper can install a dependency into the active environment.

Install only if you trust the surrounding photo-skills checkout, especially the sibling photo-toolkit/scripts/file_matcher.py file, and prefer running it inside a project virtual environment. Review setup_deps.sh before running it because it may invoke pip3 install tomli, and treat --overwrite and batch directory options as actions that can modify or create many local photo outputs.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T08 · Insecure Dependencies

Note
Location
requirements.txt:7
Finding

Unpinned and Unverified Python Dependency Installation

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:7, scripts/setup_deps.sh:84-90, SKILL.md:78-79
Vulnerability Type: Software supply-chain exposure through mutable dependency resolution
Risk Level: Low

Vulnerable Code

requirements.txt:7:

text
tomli>=1.1.0 ; python_version < "3.11"

scripts/setup_deps.sh:84-90:

bash
else
    echo -e "  ${YELLOW}⚠${NC} tomli — 未安装(Python < 3.11 需要)"
    echo "       安装中..."
    pip3 install tomli && echo -e "  ${GREEN}✓${NC} tomli 安装完成" || {
        echo -e "  ${RED}✗${NC} tomli 安装失败,请手动运行: pip3 install tomli"
        ALL_OK=false
    }
fi

SKILL.md:78-79:

bash
source .venv/bin/activate
pip install -r photo-grader/requirements.txt

Technical Analysis

The dependency declaration provides only a lower version bound. It does not use an exact version, a lockfile, package hashes, or an explicitly trusted package index. Consequently, each installation can resolve to different upstream content.

The setup script compounds this exposure by invoking pip3 install tomli directly. It does not require an activated virtual environment or use python3 -m pip, so the package may be installed into whichever Python environment the pip3 executable controls.

This is not evidence that tomli itself is malicious. The weakness is that dependency identity and integrity are not reproducibly enforced. A compromised upstream release, package-index account, index configuration, or network/package mirror could introduce code that executes during installation or subsequent import.

Attack Path

  1. A user or Agent runs scripts/setup_deps.sh on Python earlier than 3.11.
  2. Neither tomllib nor tomli is available.
  3. The script executes pip3 install tomli.
  4. pip3 resolves the latest package accepted by the mutable lower-bound constraint from its configured index or mirror.
  5. If the resolved dis ...[truncated 825 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin tomli to a specifically reviewed version rather than using only a lower bound.

  2. Generate a lock or constraints file containing cryptographic hashes for every permitted distribution.

  3. Install with hash enforcement, for example:

    bash
    python3 -m pip install --require-hashes -r requirements.lock
    
  4. Use python3 -m pip so the installer is tied to the Python interpreter that will run the application.

  5. Require and verify an activated project-local virtual environment before installing dependencies.

  6. Document the trusted package index and prevent unintentional resolution through untrusted extra indexes or mirrors.

  7. Prefer failing with clear installation instructions over silently modifying a global Python environment.

T08 · Insecure Dependencies

Warning
Location
scripts/grade.py:42
Finding

Unverified Sibling Module Is Executed Through Python Path Precedence

Content
View full analysis

Vulnerability Details

File Location: scripts/grade.py:42-44
Vulnerability Type: Untrusted local dependency loading and Python module path injection
Risk Level: Medium

Vulnerable Code

python
# Add photo-toolkit to path for shared utilities
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "photo-toolkit" / "scripts"))
from file_matcher import find_file_by_stem, find_raw_file, SUPPORTED_EXTENSIONS

Technical Analysis

The script inserts a sibling directory at index zero of sys.path and then imports file_matcher. Index zero gives that directory precedence during module resolution. Python executes module-level code immediately when the import occurs.

The photo-toolkit component is not included in the audited artifact, is not declared in requirements.txt, and is not subject to a version or integrity check in the reviewed code. Therefore, the effective behavior of grade.py depends on executable code outside the Skill package.

An attacker who can create or modify the expected sibling path can supply a malicious file_matcher.py. The malicious code would execute before command-line parsing, dependency checks, or photo processing. No user-controlled module name is involved, so exploitation requires write access to the relevant parent or sibling directory rather than merely control of grading JSON.

Attack Path

  1. The Skill is placed in a directory whose parent is also expected to contain photo-toolkit/scripts.
  2. An attacker obtains write access to that sibling location, or supplies a project bundle containing a substituted photo-toolkit.
  3. The attacker creates or modifies photo-toolkit/scripts/file_matcher.py and places arbitrary Python statements at module scope.
  4. A user or Agent starts scripts/grade.py.
  5. The script prepends the attacker-controlled directory to sys.path.
  6. Python imports and executes the malicious file_matcher.py with the g ...[truncated 554 chars]
Remediation
View remediation

Remediation Suggestions

  1. Package file_matcher inside this Skill or a normal, versioned Python package that can be audited and installed reproducibly.
  2. Remove the direct sys.path.insert(0, ...) modification and use an explicit package import.
  3. Declare the shared component as a pinned dependency with integrity verification if it must remain external.
  4. If a sibling dependency is unavoidable, resolve the module path and verify that it is the expected regular file under a trusted, non-user-writable directory before importing it.
  5. Consider verifying the dependency against an approved cryptographic digest or signed release.
  6. Run the grading process with least privilege and keep project dependency directories non-writable by less-trusted users.
  7. Add startup failure handling that clearly reports a missing trusted dependency rather than importing any matching module found through altered search precedence.
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (18)

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

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

md
| `--output`      | Output directory                                     | from config  |
| `--quality`     | JPEG quality (1-100)                                 | 95           |
| `--workers`     | Parallel workers                                     | auto (max 8) |
| `--overwrite`   | Overwrite existing files                             | off          |
| `--dry-run`     | Preview only                                         | off          |
| `--pp3-only`    | Only generate PP3 files, don't render                | off          |
| `--pp3-output`  | Directory for PP3-only output                        | `./pp3/`     |

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill clearly expects shell execution plus file reads and writes, but it does not declare any explicit tool scope or permissions boundaries. In an agent environment, that omission increases the chance the skill can run with broader-than-intended capabilities, making filesystem changes and command execution less constrained and less auditable.

Content

No source excerpt is available for this finding.

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 · SKILL.md (reported line 71)May include surrounding context.

Declaration file: requirements.txt

Prefer venv: Before running scripts, activate the project-root virtual environment (e.g. .venv/). If it doesn't exist, create one first:

bash
# Create venv and install dependencies (recommended)

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

sudo apt install rawtherapee-cli

Fedora / RHEL

sudo dnf install RawTherapee

text

> macOS note: always verify with `rawtherapee-cli -h`. If it exits with `133` / `SIGTRAP`, macOS likely blocked it before startup. This is common when an agent installs RawTherapee via Homebrew and the user has not explicitly opened/authorized the app or CLI yet. A user-installed and authorized Homebrew CLI can work; otherwise use the official standalone `rawtherapee-cli` from the RawTherapee package.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring presents python grade.py grading_params.json --dry-run as a usage mode alongside rendering modes, while the script-level dry-run path exits early after listing files in main and never reaches the per-file dry-run branch that writes PP3 previews. This creates an intent/documentation mismatch about what dry-run actually does at the top-level command behavior.

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/grade.py (reported line 129)May include surrounding context.

python
def _validate_rt_cli_executable(rt):
    """Run a lightweight smoke test to ensure rawtherapee-cli can actually start."""
    try:
        result = subprocess.run([rt, "-h"], capture_output=True, text=True, timeout=15)
    except (OSError, subprocess.TimeoutExpired) as e:
        return False, f"failed to start: {e}"

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/grade.py (reported line 709)May include surrounding context.

python
cli += ["-Y"]
        cli += ["-c", str(raw_path)]  # Must be last

        result = subprocess.run(cli, capture_output=True, text=True, timeout=300)

        tmp_pp3.unlink(missing_ok=True)

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The CLI help describes --dry-run as "Preview without processing", which implies no filesystem side effects. However, grade_single_file still writes PP3 files to the output directory in dry-run mode before returning, so the documented intent of a side-effect-free preview is contradicted by actual behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This shell script presents nearly all user-facing status, error, and installation guidance in Chinese, including dependency checks and remediation steps. Because the file provides no opt-in, fallback language, or documented region-specific justification, it violates the language/locale policy criterion for natural-language policy issues.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
else
    echo -e "  ${RED}✗${NC} rawtherapee-cli — 未安装(必须安装)"
    echo "       macOS: 可用 Homebrew(需用户手动打开/授权)或官网独立 rawtherapee-cli,并放入 PATH"
    echo "       Debian/Ubuntu: sudo apt install rawtherapee-cli"
    echo "       Fedora/RHEL: sudo dnf install RawTherapee"
    echo "       验证: rawtherapee-cli -h"
    ALL_OK=false

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup_deps.sh (reported line 67)May include surrounding context.

sh
else
    echo -e "  ${RED}✗${NC} rawtherapee-cli — 未安装(必须安装)"
    echo "       macOS: 可用 Homebrew(需用户手动打开/授权)或官网独立 rawtherapee-cli,并放入 PATH"
    echo "       Debian/Ubuntu: sudo apt install rawtherapee-cli"
    echo "       Fedora/RHEL: sudo dnf install RawTherapee"
    echo "       验证: rawtherapee-cli -h"
    ALL_OK=false

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup_deps.sh (reported line 68)May include surrounding context.

sh
else
    echo -e "  ${RED}✗${NC} rawtherapee-cli — 未安装(必须安装)"
    echo "       macOS: 可用 Homebrew(需用户手动打开/授权)或官网独立 rawtherapee-cli,并放入 PATH"
    echo "       Debian/Ubuntu: sudo apt install rawtherapee-cli"
    echo "       Fedora/RHEL: sudo dnf install RawTherapee"
    echo "       验证: rawtherapee-cli -h"
    ALL_OK=false

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup_deps.sh (reported line 116)May include surrounding context.

sh
else
    echo -e "  ${RED}✗${NC} rawtherapee-cli — 未安装(必须安装)"
    echo "       macOS: 可用 Homebrew(需用户手动打开/授权)或官网独立 rawtherapee-cli,并放入 PATH"
    echo "       Debian/Ubuntu: sudo apt install rawtherapee-cli"
    echo "       Fedora/RHEL: sudo dnf install RawTherapee"
    echo "       验证: rawtherapee-cli -h"
    ALL_OK=false

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup_deps.sh (reported line 118)May include surrounding context.

sh
else
    echo -e "  ${RED}✗${NC} rawtherapee-cli — 未安装(必须安装)"
    echo "       macOS: 可用 Homebrew(需用户手动打开/授权)或官网独立 rawtherapee-cli,并放入 PATH"
    echo "       Debian/Ubuntu: sudo apt install rawtherapee-cli"
    echo "       Fedora/RHEL: sudo dnf install RawTherapee"
    echo "       验证: rawtherapee-cli -h"
    ALL_OK=false

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script is presented as a dependency check helper, but it also performs pip3 install tomli automatically. Even though the package is low-risk and relevant to Python TOML parsing, unexpected installation side effects expand the trust boundary, modify the host environment, and may trigger execution of package-manager code in contexts where users expected a read-only check.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Automatic installation of a Python package is not strictly justified by a shell-based dependency check, and the install occurs without a strong user-consent boundary. This creates supply-chain and environment-modification risk, especially in agent-driven workflows where dependency scripts may run automatically on behalf of a user.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

This TOML example is entirely annotated in Chinese, which creates a language-specific usage constraint for readers of the skill configuration. The file does not offer an alternative language, opt-in, or explanation that the skill is intended only for a Chinese-speaking or region-specific audience.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code file contains a natural-language string for the --raw-dir argument help text in Chinese while the rest of the interface and documentation are in English. That creates an inconsistent language policy experience and effectively forces one locale for part of the skill without opt-in or justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.