Back to skill

Security audit

skillsync

Security checks for vulnerabilities and agentic risk

Overview

SkillSync is a coherent Git backup tool for agent skills, but its installer and sync behavior create Review-level supply-chain and unintended-file-upload risks.

Install only after reviewing the installer yourself; prefer a pinned package/version over pipe-to-shell. Before init, snapshot, remote add, or sync, make sure the selected Skills directory contains no secrets or unrelated files, because repository-wide commits can include them and a remote push can preserve them in Git history. Use a private remote and treat sync --use-remote as a deliberate discard of local-only snapshots.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:34
Finding

Mutable Remote Installers Are Downloaded and Executed Directly

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:34; README.md:169,179; README.zh-CN.md:166,176; install.sh:5; install.ps1:4-5
Vulnerability Type: Remote payload retrieval and execution
Risk Level: Critical

Vulnerable Code

SKILL.md:34, README.md:169, and README.zh-CN.md:166:

sh
curl -fsSL https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.sh | sh

README.md:179 and README.zh-CN.md:176:

powershell
irm https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.ps1 | iex

install.sh:4-5:

sh
# Usage:
#   curl -fsSL https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.sh | sh

install.ps1:3-5:

powershell
# Usage (in PowerShell):
#   irm https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.ps1 | iex
#   (Windows PowerShell 5.1:  iwr <url> -UseBasicParsing | iex)

Technical Analysis

The documented installation commands retrieve scripts from the mutable main branch and immediately evaluate them in a local shell. There is no immutable commit or release pin, checksum verification, digital-signature verification, or opportunity to inspect the downloaded file before execution.

Consequently, the effective installer payload is not limited to the source reviewed in this audit. It can change after publication without requiring any modification to the audited package. The same risk applies to both Unix-like systems through sh and Windows systems through Invoke-Expression.

This remote execution channel is not the minimum privilege necessary to install a Python package. A versioned package, verified release artifact, or downloaded-and-reviewed installer would provide the declared functionality with materially less risk.

Attack Path

  1. An attacker compromises the GitHub account, repository, CI release process, or another component capable of changing the main ...[truncated 1180 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove all curl | sh, irm | iex, and equivalent direct-evaluation instructions.
  2. Publish installers as versioned release assets rather than serving them from a mutable branch.
  3. Pin installation instructions to an immutable release tag or full commit hash.
  4. Publish SHA-256 hashes and preferably cryptographic signatures through an independently authenticated release channel.
  5. Require users to download the installer to a file, verify its integrity, inspect it, and execute it separately.
  6. Prefer a trusted package-index installation with an exact version, for example a reviewed skillsync==0.3.1 release.
  7. Ensure the installer never escalates privileges automatically. If an operating-system dependency is missing, print explicit manual instructions instead.

T08 · Insecure Dependencies

Error
Location
install.sh:20
Finding

Installer and Python Dependencies Are Retrieved Without Immutable Version Pinning

Content
View full analysis

Vulnerability Details

File Location: install.sh:20,84-89; install.ps1:59,77-81; pyproject.toml:1-2,13,20
Vulnerability Type: Unpinned software supply chain
Risk Level: High

Vulnerable Code

install.sh:20:

sh
REPO_URL="${SKILLSYNC_REPO:-https://github.com/johnsonbuilds/skillsync.git}"

install.sh:84-89:

sh
# 3. Install skillsync into it
#    REPO_URL may be a git URL or a local checkout path.
case "$REPO_URL" in
    *.git | http://* | https://* | git+*) SRC="git+$REPO_URL" ;;
    *) SRC="$REPO_URL" ;;
esac
"$INSTALL_DIR/venv/bin/pip" install --upgrade "$SRC"

install.ps1:59:

powershell
$RepoUrl = if ($env:SKILLSYNC_REPO) { $env:SKILLSYNC_REPO } else { 'https://github.com/johnsonbuilds/skillsync.git' }

install.ps1:77-81:

powershell
# 3. Install skillsync into it ------------------------------------------
#    REPO_URL may be a git URL or a local checkout path.
$src = if ($needsGit) { "git+$RepoUrl" } else { $RepoUrl }
$pip = Join-Path $VenvDir 'Scripts\pip.exe'
& $pip install --upgrade $src

pyproject.toml:1-2,13,20:

toml
[build-system]
requires = ["hatchling"]

dependencies = ["typer>=0.12"]

dev = ["pytest>=8"]

Technical Analysis

Both installers direct pip to install the current default revision of the Git repository. They do not append an immutable Git commit, signed tag, or exact release version. The package metadata also allows any future Typer version at or above 0.12, while the build system accepts an unconstrained Hatchling version. No lockfile or hash-verified requirements file is present.

This creates multiple mutable supply-chain inputs:

  • The repository default branch can change independently of version 0.3.1.
  • Future dependency releases may introduce compromised or incompatible code.
  • Build dependencies execute as part of package construction and are not integrity-pinned.

...[truncated 1368 chars]

Remediation
View remediation

Remediation Suggestions

  1. Install SkillSync from a versioned package artifact using an exact version.
  2. If Git installation must remain supported, use a full immutable commit identifier, such as git+https://...git@FULL_COMMIT_HASH.
  3. Sign release tags and release artifacts, and document signature verification.
  4. Pin direct and transitive dependencies to reviewed versions in a lockfile.
  5. Generate hash-locked requirements and install with pip hash enforcement where feasible.
  6. Constrain the build backend to a reviewed exact version.
  7. Use automated dependency monitoring, but require security review and tests before updating pins.
  8. Avoid --upgrade against mutable Git sources in installation scripts.

T09 · Insecure Skill Coding Practices

Error
Location
src/skillsync/cli.py:448
Finding

Snapshots and Remote Synchronization Include Files Outside Discovered Skills

Content
View full analysis

Vulnerability Details

File Location: src/skillsync/cli.py:398-403,448-463,749-755; src/skillsync/git.py:137-138; .gitignore:1-7
Vulnerability Type: Overbroad collection and transmission of local files
Risk Level: High

Vulnerable Code

src/skillsync/cli.py:398-403:

python
typer.echo("Initializing SkillSync...")
git.init(skills_dir)
git.add_all(skills_dir)
git.commit(skills_dir, "SkillSync initial snapshot")
typer.echo("Initial snapshot created.")

src/skillsync/cli.py:448-463:

python
entries = git.status_porcelain(skills_dir)
if not entries:
    typer.echo("No changes to snapshot.")
    raise typer.Exit()

changes, _ = _skill_changes(skills_dir)
if changes:
    count = len(changes)
    typer.echo(f"{count} skill{'s' if count != 1 else ''} changed.\n")

if message is None:
    message = f"SkillSync snapshot: {_dt.datetime.now():%Y-%m-%d %H:%M}"
git.add_all(skills_dir)
created = git.commit(skills_dir, message)

src/skillsync/cli.py:749-755:

python
# 1. Never mix remote changes with unsaved local state: pre-snapshot.
if git.status_porcelain(skills_dir):
    message = f"Pre-sync snapshot: {_dt.datetime.now():%Y-%m-%d %H:%M}"
    git.add_all(skills_dir)
    created = git.commit(skills_dir, message)
    if created:
        typer.echo(f"Pre-sync snapshot created: {created}")

src/skillsync/git.py:137-138:

python
def add_all(repo: Path) -> None:
    git("add", "-A", cwd=repo)

.gitignore:1-7:

gitignore
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/
.venv/
.pytest_cache/

Technical Analysis

Skill discovery deliberately identifies only directories containing SKILL.md, and status output can count files “outside any Skill.” Despite that model, initialization, manual snapshots, and automatic pre-sync snapshots execute git add -A at the entire selected Skills-directory root.

Therefor ...[truncated 2136 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace repository-wide git add -A with explicit pathspecs derived from validated discovered Skill roots.
  2. Exclude all files outside discovered Skills by default.
  3. If unmanaged files must be supported, list every such file and require explicit confirmation before committing or uploading it.
  4. Add default exclusions for .env*, private-key formats, credential files, token stores, and harness-specific secret locations.
  5. Run secret detection before every initial snapshot, snapshot, and push; block the operation when high-confidence secrets are found.
  6. Display the exact set of files that will be committed and pushed, rather than only the number of changed Skills.
  7. Ensure ignored files are never staged merely because they were previously tracked; provide a migration command to remove unsafe tracked files.
  8. Document that deleting a secret in a later commit is insufficient and provide instructions for revocation and Git-history rewriting.
  9. Add tests proving that root-level and non-Skill files cannot enter snapshots or remote pushes without explicit authorization.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (47)

Chaining Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

The | sh construct chains network retrieval directly into command execution, removing the user's opportunity to review the script before it runs. In an agent context, this is more dangerous because automated systems may execute install snippets verbatim, magnifying supply-chain and remote code execution risk.

Content

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

One-line install (creates an isolated venv and exposes the skillsync command):

bash
curl -fsSL https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.sh | sh

On Debian/Ubuntu the installer installs python3-venv automatically (directly as

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
|---|---|
| GitHub CLI | `gh auth login` (git picks up its credential helper) |
| SSH | `ssh-keygen` + add key to GitHub, use `git@github.com:...` URLs |
| Token | Personal access token over HTTPS (credential manager or `git-credential-store`) |

If `remote add` reports `could not reach`, finish one of the setups above first.

Chaining Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

The | sh chain removes the user's opportunity to review downloaded content before execution and turns a documentation example into a one-step arbitrary code execution pattern. Because this project targets agent/developer environments and mentions automatic dependency installation, a compromised installer could modify local skills, exfiltrate secrets, or persist on the host.

Content

Scanner excerpt · README.zh-CN.md (reported line 166)May include surrounding context.

一行命令安装(创建隔离的 venv 并暴露 skillsync 命令):

bash
curl -fsSL https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.sh | sh

在 Debian/Ubuntu 上,创建 venv 失败时,安装脚本会自动安装 python3-venv

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is described as a version-control utility, but the content includes installation behavior that downloads and executes remote code and may modify the local environment. This mismatch is dangerous because users or agents may approve the skill expecting benign diff/snapshot actions while it can also perform setup operations with much broader system impact.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is described as a version-control utility, but the content includes installation behavior that downloads and executes remote code and may modify the local environment. This mismatch is dangerous because users or agents may approve the skill expecting benign diff/snapshot actions while it can also perform setup operations with much broader system impact.

Content

No source excerpt is available for this finding.

Chaining Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

The | sh pipeline executes untrusted network content without an opportunity for inspection, validation, or integrity checking. In the context of an agent skill, this is especially risky because agents may automate commands and users may overlook that the skill is effectively granting arbitrary shell execution to remote content.

Content

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

Install

bash
curl -fsSL https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.sh | sh

Or via pip (requires Python 3.12+):

YARA rule 'agent_skill_destructive_autonomous_actions': Autonomous destructive filesystem, shell history, or repository actions in AI agent skills [agent_skills]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · docs/functional-design.md (reported line 488)May include surrounding context.

nds

SkillSync should internally rely on standard Git operations where possible.

Conceptually:

text
status   → git status / git diff
diff     → git diff
snapshot → git add + git commit
log      → git log
restore  → git restore / checkout + commit

SkillSync must not implement its own version storage.


16. Safety Requirements

SkillSync must follow these rules:

Never silently overwrite

Restore requires explicit confirmation.

Never delete history

Restore creates a new Git state.

Never modify unmanaged locations

Only the configured Agent Skill directory is managed.

Never upload anything

MVP has no network functionality.

Existing Git repositories

If the Skill directory already contains a Git repository:

  • do not delete it
  • do not rewrite history
  • warn the user
  • reuse it only when safe

If supporting existing repositories creates ambiguity, skillsync init should fail safely and explain the situation rather than making assumptions

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · docs/functional-design.md (reported line 492)May include surrounding context.

md
Restore requires explicit confirmation.

### Never delete history

Restore creates a new Git state.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
87% confidence
Finding

The design includes a destructive recovery path, sync --use-remote, that performs git reset --hard origin/main. Even though intended as a conflict-resolution choice, this operation can discard uncommitted local changes and reset the working tree to remote state; in an agent context, wrapping such a command behind a high-level sync action is dangerous if confirmation, preflight checks, and recoverability guarantees are imperfect.

Content

Scanner excerpt · docs/functional-design.md (reported line 761)May include surrounding context.

md
any conflicted files outside any Skill;
* the two available choices:
  * adopt the remote version: `sync --use-remote`
    (aborts the rebase, `git reset --hard origin/main`; the abandoned local
    snapshots remain recoverable and their starting hash is printed);
  * keep the local version: manual `git push --force-with-lease`
    (SkillSync refuses to do this itself).

Chaining Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

The installation guidance explicitly chains a network fetch into shell execution, removing any review boundary between untrusted remote content and code execution. In this skill's context, that is especially risky because the script can install arbitrary Python packages from a remote repository and may also perform privileged OS package installation.

Content

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

sh
# SkillSync installer
#
# Usage:
#   curl -fsSL https://raw.githubusercontent.com/johnsonbuilds/skillsync/main/install.sh | sh
#
# What it does:
#   1. checks Python >= 3.12

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

The use of rm -rf on a computed path is inherently dangerous because any unexpected path expansion, empty variable issue, or user-controlled override can turn a reinstall step into destructive file deletion. Even though the default path is user-scoped, the script accepts environment-controlled inputs and performs no explicit path safety validation before deletion.

Content

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

sh
#    fails, install that package automatically (root directly, else via
#    sudo), then retry once before giving up with manual instructions.
create_venv() {
    rm -rf "$INSTALL_DIR/venv"
    python3 -m venv "$INSTALL_DIR/venv"
}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · README.md (reported line 309)May include surrounding context.

md
Remote operations (fetch, push, clone) run with terminal prompts disabled
and a subprocess timeout, so an unreachable remote fails cleanly instead of
hanging. SkillSync never force-pushes: there is deliberately no wrapper for
``git push --force``.
"""

from __future__ import annotations

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · README.zh-CN.md (reported line 302)May include surrounding context.

md
Remote operations (fetch, push, clone) run with terminal prompts disabled
and a subprocess timeout, so an unreachable remote fails cleanly instead of
hanging. SkillSync never force-pushes: there is deliberately no wrapper for
``git push --force``.
"""

from __future__ import annotations

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · docs/functional-design.md (reported line 763)May include surrounding context.

md
Remote operations (fetch, push, clone) run with terminal prompts disabled
and a subprocess timeout, so an unreachable remote fails cleanly instead of
hanging. SkillSync never force-pushes: there is deliberately no wrapper for
``git push --force``.
"""

from __future__ import annotations

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · src/skillsync/cli.py (reported line 716)May include surrounding context.

python
Remote operations (fetch, push, clone) run with terminal prompts disabled
and a subprocess timeout, so an unreachable remote fails cleanly instead of
hanging. SkillSync never force-pushes: there is deliberately no wrapper for
``git push --force``.
"""

from __future__ import annotations

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · src/skillsync/git.py (reported line 10)May include surrounding context.

python
Remote operations (fetch, push, clone) run with terminal prompts disabled
and a subprocess timeout, so an unreachable remote fails cleanly instead of
hanging. SkillSync never force-pushes: there is deliberately no wrapper for
``git push --force``.
"""

from __future__ import annotations

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 · src/skillsync/git.py (reported line 36)May include surrounding context.

python
def _net_env() -> dict[str, str]:
    """Environment for network commands: never prompt for credentials."""
    return {**os.environ, "GIT_TERMINAL_PROMPT": "0"}


def git(*args: str, cwd: Path, check: bool = True) -> str:

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

Set up (once)

Create an empty, private GitHub repository (e.g. you/skills), then:

bash
skillsync remote add git@github.com:you/skills.git

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The README recommends executing a remotely fetched installer directly via the shell/PowerShell without any integrity verification, pinning, or review step. If the hosting account, repository, branch, or network path is compromised, users may execute arbitrary code immediately on their machine, and the script is documented to potentially invoke package installation with elevated privileges.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
80% confidence
Finding

The skill advertises shell, network, filesystem, and environment-affecting behavior but does not declare any explicit tool scope or permissions boundaries. In an agent setting, this increases the chance that an orchestrator grants broader capabilities than intended, making risky actions like installs, git operations, or file modification less transparent to users and policy systems.

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 · docs/functional-design.md (reported line 126)May include surrounding context.

md
# 4. Repository Layout

SkillSync should create a Git repository around the Agent's Skill directory.

For Hermes:

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 · docs/product.md (reported line 11)May include surrounding context.

md
Its primary purpose is to protect Skills from unwanted changes made by AI Agents.

Modern Agents can create and modify their own Skills. A Skill may become better over time, but an Agent may also accidentally make a previously useful Skill worse.

SkillSync provides a simple safety net:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The create_venv function unconditionally deletes the existing virtual environment with rm -rf before recreating it. This can destroy an existing installation or user modifications without confirmation, and if path assumptions are ever violated, destructive deletion risks become more severe.

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 · install.sh (reported line 49)May include surrounding context.

sh
echo
    echo "Error: could not create the virtual environment (ensurepip missing)." >&2
    echo "Install the venv package for your Python and re-run this installer:" >&2
    echo "    sudo apt install python3-venv" >&2
    exit 1
}

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 · install.sh (reported line 59)May include surrounding context.

sh
echo
    echo "Error: could not create the virtual environment (ensurepip missing)." >&2
    echo "Install the venv package for your Python and re-run this installer:" >&2
    echo "    sudo apt install python3-venv" >&2
    exit 1
}