Back to skill

Security audit

Agent Expression

Security checks for vulnerabilities and agentic risk

Overview

This meme skill mostly does what it says, but its installer pulls and activates mutable remote code across several agent apps, so users should review it before installing.

Install only after reviewing or pinning the exact repository revision. Avoid the one-line remote execution commands; download the installer, inspect it, and prefer a verified commit or release. Use vision and embedding features only with images you are comfortable sending to the configured API provider, and avoid running ingestion from projects that contain sensitive .env files.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
README.md:39
Finding

Installation Instructions Execute Mutable Remote Scripts Without Integrity Verification

Content
View full analysis
Remediation
View remediation
install.sh" | sha256sum -c - less install.sh bash install.sh ``` 5. Provide an equivalent `Get-FileHash` verification flow for PowerShell. 6. Pin any subsequent repository checkout to the same reviewed commit rather than a mutable branch. 7. Document the directories and files the installer will create or replace before execution. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
install.sh:22
Finding

Installer Clones and Updates Skill Code from an Unpinned Mutable Branch

Content
View full analysis
Existing git install, updating…" git -C "$dest" fetch --depth 1 origin "$BRANCH" git -C "$dest" checkout -q "$BRANCH" git -C "$dest" reset --hard "origin/$BRANCH" elif [[ -L "$dest" ]]; then echo "ERROR: $dest is a symlink; remove it or use --dir" >&2 exit 1 else rm -rf "$dest" mkdir -p "$(dirname "$dest")" git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$dest" fi ``` The Windows installer performs the equivalent mutable update: ```powershell & $git -C $Dest fetch --depth 1 origin $Branch & $git -C $Dest checkout -q $Branch & $git -C $Dest reset --hard "origin/$Branch" ``` ### Technical Analysis The installer defaults to the mutable `main` branch and installs whichever revision that branch references at execution time. Existing installations are forcibly reset to the latest remote branch state. The resulting directory is linked into several Agent Skill discovery paths. Therefore, a later upstream change can replace both executable scripts and Agent instructions across multiple hosts without an immutable version boundary or integrity check. The `REPO_URL` and branch are also environment-overridable. This is useful for development, but inherited environment manipulation can silently redirect installation to an unintended repository unless the user inspects those variables. ### Attack Path 1. An attacker gains the ability to modify the configured upstream branch, redirect an inherited `REPO_URL`, or otherwise control the selected repository. 2. Malicious Python scripts or Skill instructions are committed to ...[truncated 1052 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/meme_db.py:122
Finding

Predictable Shared Temporary Directory and Filename Permit Symlink or Race Attacks

Content
View full analysis
Path: """Cross-platform scratch dir (Windows TEMP / Unix /tmp).""" import tempfile d = Path(tempfile.gettempdir()) / name d.mkdir(parents=True, exist_ok=True) return d ``` The downloaded image is then written under a predictable name: ```python if source.startswith(("http://", "https://")): data, hint = am.fetch_bytes(source) ext = am.detect_ext(data, Path(hint).suffix.lower()) or ".png" path = tmp_dir / f"download{ext}" path.write_bytes(data) return path ``` Equivalent fallback behavior appears in `hermes-tools/add_meme_tool.py:310-315`. ### Technical Analysis The code creates or reuses a fixed directory under the system temporary root and writes remote content to names such as `download.png`. It does not use exclusive file creation, verify directory ownership, reject symbolic links, or create a unique per-operation directory. On systems with a shared temporary root, an attacker can pre-create the fixed directory with permissive access or place a symbolic link at the predictable destination. `Path.write_bytes()` follows symbolic links. Concurrent operations can also overwrite each other's temporary image, causing one request to classify or ingest content from another request. Exploitability across different operating-system users depends on temporary-directory ownership and permissions. The risk is stronger where the fixed directory can be pre-created by another user, where it is writable by multiple users, or where multiple untrusted requests execute under the same service account. ### Attack Path 1. The attacker predicts the directory and filename, such as `/tmp/agent-expression-meme/download ...[truncated 1111 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/add-meme.py:81
Finding

SSRF Guard Does Not Validate Redirect Destinations or Bind Connections to the Validated Address

Content
View full analysis
bool: host = (hostname or "").strip().lower() if not host or host == "localhost" or host.endswith(".local"): return True try: infos = socket.getaddrinfo(host, None) except socket.gaierror: return True for info in infos: ip = info[4][0] if ip.startswith("127.") or ip.startswith("10.") or ip.startswith("192.168."): return True if ip.startswith("169.254.") or ip.startswith("::1") or ip.startswith("fc") or ip.startswith("fd"): return True if ip.startswith("172."): try: second = int(ip.split(".")[1]) if 16 <= second <= 31: return True except (ValueError, IndexError): pass return False ``` ```python if source.startswith(("http://", "https://")): parsed = urlparse(source) if parsed.scheme not in {"http", "https"}: raise ValueError(f"unsupported URL scheme: {parsed.scheme}") if _is_private_host(parsed.hostname or ""): raise ValueError("refusing to fetch private/local URL (SSRF guard)") req = urllib.request.Request( source, headers={"User-Agent": "agent-expression/add-meme"}, ) with urllib.request.urlopen(req, timeout=30) as resp: data = resp.read(MAX_BYTES + 1) ``` ### Technical Analysis The code checks only the hostname from the initially supplied URL. Python's standard URL opener follows HTTP redirects by default, but the redirect target is not passed through `_is_private_host()` again. A public attacker-controlled endpoint can therefore redirect the request to a loopback, private-network, link-local, or cloud metadata address. There is also a time-o ...[truncated 2029 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (69)

Chaining Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The specific use of a shell pipe into bash is dangerous because it chains retrieval and execution into a single step with no validation, inspection, or integrity check. In the context of an agent skill that installs into trusted local tool directories, this can lead to arbitrary code execution and persistence if the fetched script is tampered with.

Content

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

macOS / Linux

bash
curl -fsSL https://raw.githubusercontent.com/yyh-001/agent-expression/main/install.sh | bash

Windows(PowerShell)

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

This appears to be a duplicate detection of the same abbreviated remote execution example. The security issue remains the same: direct execution of network-delivered shell content from a mutable source.

Content

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

bash
# macOS/Linux:当前仓库 / 单宿主
curl -fsSL …/install.sh | bash -s -- --project
curl -fsSL …/install.sh | bash -s -- --cursor

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

This appears to be a duplicate detection of the same abbreviated remote execution example. The security issue remains the same: direct execution of network-delivered shell content from a mutable source.

Content

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

bash
# macOS/Linux:当前仓库 / 单宿主
curl -fsSL .../install.sh | bash -s -- --project
curl -fsSL .../install.sh | bash -s -- --cursor

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

This is a duplicate of the previous remote execution pattern on the adjacent line. Even as shorthand, it instructs unsafe execution of downloaded shell code and should be treated as a genuine supply-chain risk.

Content

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

bash
# macOS/Linux:当前仓库 / 单宿主
curl -fsSL …/install.sh | bash -s -- --project
curl -fsSL …/install.sh | bash -s -- --cursor
powershell

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

This is a duplicate of the previous remote execution pattern on the adjacent line. Even as shorthand, it instructs unsafe execution of downloaded shell code and should be treated as a genuine supply-chain risk.

Content

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

bash
# macOS/Linux:当前仓库 / 单宿主
curl -fsSL .../install.sh | bash -s -- --project
curl -fsSL .../install.sh | bash -s -- --cursor
powershell

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

Calling external vision APIs and downloading remote images materially changes the threat model from a local sticker-pack utility to a networked content-processing tool. If users believe the skill is local-only, they may inadvertently expose private images, metadata, or prompts to third-party services and permit unexpected filesystem mutation.

Content

No source excerpt is available for this finding.

Chaining Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

Piping network content directly into bash is an execution-chain anti-pattern: retrieval and execution are coupled so users cannot easily verify what runs. In the context of a skill installer that also modifies multiple host integration points, successful upstream tampering could yield immediate multi-location persistence or agent-behavior hijacking.

Content

Scanner excerpt · install.sh (reported line 4)May include surrounding context.

sh
#!/usr/bin/env bash
# One-line install (multi-host by default):
#   macOS/Linux:
#     curl -fsSL https://raw.githubusercontent.com/yyh-001/agent-expression/main/install.sh | bash
#   Windows PowerShell:
#     irm https://raw.githubusercontent.com/yyh-001/agent-expression/main/install.ps1 | iex
#

Chaining Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The help text repeats the same chained remote execution pattern, normalizing unsafe installation practice. Because the skill is supposed to be local-content oriented, this installation mechanism is more dangerous than necessary and materially increases supply-chain exposure.

Content

Scanner excerpt · install.sh (reported line 36)May include surrounding context.

sh
cat <<'EOF'
Install agent-expression for mainstream Agents / IDEs.

  curl -fsSL https://raw.githubusercontent.com/yyh-001/agent-expression/main/install.sh | bash

Default: clone → ~/.agent-expression/skill, symlink into:
  ~/.agents/skills/agent-expression     (Codex / Cursor / cross-tool)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · install.sh (reported line 209)May include surrounding context.

sh
mkdir -p "$dest"
  # Copy images if missing / incomplete; always refresh portable index when absent or --pack
  if [[ ! -d "$dest/memes" ]] || [[ "$WANT_PACK" -eq 1 ]]; then
    rm -rf "$dest/memes"
    cp -a "$src/memes" "$dest/"
  fi
  if [[ ! -f "$dest/index.db" ]] || [[ "$WANT_PACK" -eq 1 ]]; then

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code converts the full image into a base64 data URL and sends it to an external '/chat/completions' endpoint for classification. If users supply sensitive local images, the entire image contents are exfiltrated to a third-party service, which is especially risky because the surrounding skill description emphasizes local handling.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/embed-memes.py (reported line 43)May include surrounding context.

python
if not cfg.get("api_key"):
        print(
            "ERROR: ZHIPU_API_KEY (or EMBEDDING_API_KEY) not set in env / .env",
            file=sys.stderr,
        )
        return 1

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

python
def data_home() -> Path:
    """Root for meme-packs/ and optional .env (host-agnostic).

    Priority: MEME_HOME → AGENT_EXPRESSION_HOME → HERMES_HOME →
    existing ~/.hermes (compat) → ~/.agent-expression.

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The dotenv_candidates function explicitly enumerates possible .env locations, establishing a mechanism to discover files that commonly store credentials. In a meme indexer, this capability is not necessary for core function and enlarges the attack surface by pulling in secrets from host directories and developer workspaces.

Content

Scanner excerpt · scripts/meme_db.py (reported line 82)May include surrounding context.

python
def dotenv_candidates() -> list[Path]:
    """Likely .env locations (first existing wins for loaders)."""
    seen: set[Path] = set()
    out: list[Path] = []
    for key in ("MEME_HOME", "AGENT_EXPRESSION_HOME", "HERMES_HOME"):

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Constructing candidate .env paths from environment-controlled base directories means an attacker or untrusted launch context can influence which secret-bearing file is loaded. That enables configuration poisoning and access to unintended credentials if the process starts with manipulated environment variables.

Content

Scanner excerpt · scripts/meme_db.py (reported line 88)May include surrounding context.

python
for key in ("MEME_HOME", "AGENT_EXPRESSION_HOME", "HERMES_HOME"):
        v = os.environ.get(key, "").strip()
        if v:
            p = Path(v).expanduser().resolve() / ".env"
            if p not in seen:
                seen.add(p)
                out.append(p)

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

Including the skill root and especially the current working directory in .env search paths is high risk because agents often run inside repositories that contain sensitive project-level .env files. This makes the skill capable of ingesting secrets from unrelated projects simply based on where it was launched.

Content

Scanner excerpt · scripts/meme_db.py (reported line 98)May include surrounding context.

python
Path(__file__).resolve().parents[1],  # skill root
        Path.cwd(),
    ):
        p = (base / ".env").resolve()
        if p not in seen:
            seen.add(p)
            out.append(p)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
95% confidence
Finding

Merging all of os.environ into a returned dictionary harvests every environment variable visible to the process, including API keys, tokens, and service credentials. In the context of an agent skill, that is especially dangerous because the collected map can be reused, logged, or surfaced by later logic, turning a local sticker utility into a broad secret-access capability.

Content

Scanner excerpt · scripts/meme_db.py (reported line 118)May include surrounding context.

python
k, _, v = line.partition("=")
            out[k.strip()] = v.strip().strip('"').strip("'")
        break
    out.update({k: v for k, v in os.environ.items() if v})
    return out