Back to skill

Security audit

Skill Sync Publisher

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent publishing helper, but it runs an unpinned external npm publisher and has review/checking gaps that could publish unintended sensitive files.

Review carefully before installing. Use dry-run mode, manually inspect `git diff` for the skill directory, do not rely on the bundled preflight as the only secret scan, and avoid the HOL adapter until `@hol-org/registry` is pinned or run it in a restricted environment with minimal credentials.

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 (3)

T08 · Insecure Dependencies

Error
Location
scripts/adapters.py:121
Finding

Unpinned npm Package Is Downloaded and Executed

Content
View full analysis
Result: if not dry_run and not command("npx"): return Result("blocked", "npx is required for HOL Registry") directory = info["skillDir"] quote = run(["npx", "--yes", "@hol-org/registry", "skills", "quote", "--dir", directory], Path(info["repoRoot"]), dry_run) if quote.status == "failed": return quote if dry_run or not _.get("yes", False): return Result("planned", "HOL quote is ready; user confirmation is required before publish", {"quote": quote.message}) return run(["npx", "--yes", "@hol-org/registry", "skills", "publish", "--dir", directory], Path(info["repoRoot"])) ``` ### Technical Analysis The HOL adapter invokes `npx --yes @hol-org/registry` without specifying an exact package version or verifying package integrity. When the package is not already available locally, `npx` can resolve and download the currently published registry version before executing it. The effective executable code may therefore change after this Skill has been reviewed. The `--yes` option suppresses npm's installation prompt, further reducing the opportunity to inspect the package version before execution. The nominally non-publishing `quote` operation also executes the package. Consequently, package code runs before the later publication confirmation check, even when the user has not yet approved the final `publish` operation. ### Attack Path 1. An attacker compromises the npm package, its maintainer account, or the relevant package distribution channel. 2. The attacker publishes a malicious version under the existing `@hol-org/registry` package name. 3. A user enables the HOL platform and runs a non-dry-run synchronization. 4. The adapter executes `npx --yes @hol-org/regis ...[truncated 1060 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/preflight.py:79
Finding

Secret Scanning Excludes Primary Supported Text File Types

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/adapters.py:45
Finding

GitHub Publication Does Not Display the Promised Exact Diff

Content
View full analysis
Remediation
View remediation
` - `git diff --cached -- ` - The exact list of untracked files that will be added. - The destination remote and branch. 2. Stage the intended files, show `git diff --cached`, and request confirmation only after the final staged patch is available. 3. Bind confirmation to a cryptographic hash of the staged diff or index tree. 4. Immediately before committing, verify that the staged hash still matches the reviewed hash. 5. Abort if the working tree or index changes after confirmation. 6. Avoid treating `--yes` as sufficient unless an automation job supplies and verifies a previously reviewed source or diff hash. 7. Report the exact commit SHA and pushed ref after success. 8. Preserve the existing restriction against unrelated dirty files, but do not treat it as a replacement for patch review. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose emphasizes publishing, updating, registering, synchronizing, and tracking a skill across several external directories and marketplaces. The supplied code chunk is only a local preflight validator/inspector. It reads SKILL.md, parses frontmatter, scans local files for possible secrets, computes a SHA-256 hash, inspects git remote/branch/status, and returns a structured readiness report. There is no external communication, no API usage, no submission logic, and no marketplace-specific behavior. While validation is mentioned in the description, the primary behavior of this code is narrower and materially different from the broader declared publish/sync/track functionality.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill describes operations that inherently require filesystem and shell access, but it does not declare an explicit tool scope such as permissions or allowed-tools. That omission weakens least-privilege controls and can let the runtime grant broader capabilities than reviewers or users expect, which is risky for a publishing workflow that touches local files, git state, and external CLIs.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

Using npx @hol-org/registry without pinning an exact version allows execution of whatever package version is current at runtime. That creates a supply-chain risk: a compromised or newly changed upstream package could execute unexpected code during a publish flow with the user's local repository access and shell privileges.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

The document instructs use of npx @hol-org/registry without pinning an exact package version, which allows execution of whatever version is currently published to the registry at runtime. In a publishing/synchronization skill, that creates a real supply-chain risk: a compromised or malicious upstream release could run arbitrary code during the user's workflow, potentially accessing repository contents, credentials, or modifying published artifacts.

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

python
def run(args: list[str], cwd: Path, dry_run: bool = False) -> Result:
    if dry_run:
        return Result("planned", "would run: " + " ".join(args))
    result = subprocess.run(args, cwd=cwd, text=True, capture_output=True, check=False)
    if result.returncode:
        detail = (result.stderr or result.stdout).strip()[-1000:]
        return Result("failed", f"{args[0]} failed: {detail}")

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

python
if dry_run:
        return Result("planned", f"would commit target skill with: {message}, then push current branch")
    if not info.get("gitStatus"):
        sha = subprocess.run(["git", "rev-parse", "HEAD"], cwd=root, text=True, capture_output=True, check=False).stdout.strip()
        return Result("published", "GitHub source is already clean and current", {"sourceCommit": sha})
    add = run(["git", "add", "--", info["skillDir"]], root)
    if add.status == "failed":

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

python
if dry_run:
        return Result("planned", f"would commit target skill with: {message}, then push current branch")
    if not info.get("gitStatus"):
        sha = subprocess.run(["git", "rev-parse", "HEAD"], cwd=root, text=True, capture_output=True, check=False).stdout.strip()
        return Result("published", "GitHub source is already clean and current", {"sourceCommit": sha})
    add = run(["git", "add", "--", info["skillDir"]], root)
    if add.status == "failed":

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

python
repo = "hashgraph-online/awesome-codex-plugins"
    owner = "liuyewang"
    fork = f"{owner}/awesome-codex-plugins"
    view = subprocess.run([gh, "repo", "view", fork], cwd=root, text=True, capture_output=True, check=False)
    if view.returncode:
        created = subprocess.run([gh, "repo", "fork", repo, "--clone=false"], cwd=root, text=True, capture_output=True, check=False)
        if created.returncode:

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

python
fork = f"{owner}/awesome-codex-plugins"
    view = subprocess.run([gh, "repo", "view", fork], cwd=root, text=True, capture_output=True, check=False)
    if view.returncode:
        created = subprocess.run([gh, "repo", "fork", repo, "--clone=false"], cwd=root, text=True, capture_output=True, check=False)
        if created.returncode:
            return Result("failed", f"could not create Awesome list fork: {(created.stderr or created.stdout).strip()[-1000:]}")
    with tempfile.TemporaryDirectory(prefix="awesome-codex-plugins-") as temp:

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

python
return Result("failed", f"could not create Awesome list fork: {(created.stderr or created.stdout).strip()[-1000:]}")
    with tempfile.TemporaryDirectory(prefix="awesome-codex-plugins-") as temp:
        checkout = Path(temp) / "list"
        clone = subprocess.run(["git", "clone", f"https://github.com/{fork}.git", str(checkout)], cwd=root, text=True, capture_output=True, check=False)
        if clone.returncode:
            return Result("failed", f"could not clone Awesome list fork: {(clone.stderr or clone.stdout).strip()[-1000:]}")
        branch = f"codex/{info['name']}-submission"

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

python
["git", "commit", "-m", f"feat: add {info['name']}"],
            ["git", "push", "-u", "origin", branch],
        ):
            result = subprocess.run(command_args, cwd=checkout, text=True, capture_output=True, check=False)
            if result.returncode:
                return Result("failed", f"Awesome list command failed: {(result.stderr or result.stdout).strip()[-1000:]}")
        body = f"Adds [{info['name']}]({source_url}) to the Development & Workflow section.\n\nThe source repository includes the required plugin manifest, security files, and HOL scanner workflow."

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

python
if result.returncode:
                return Result("failed", f"Awesome list command failed: {(result.stderr or result.stdout).strip()[-1000:]}")
        body = f"Adds [{info['name']}]({source_url}) to the Development & Workflow section.\n\nThe source repository includes the required plugin manifest, security files, and HOL scanner workflow."
        pr = subprocess.run([gh, "pr", "create", "--repo", repo, "--head", f"{owner}:{branch}", "--base", "main", "--title", f"feat: add {info['name']}", "--body", body], cwd=checkout, text=True, capture_output=True, check=False)
        if pr.returncode:
            return Result("failed", f"could not open Awesome list PR: {(pr.stderr or pr.stdout).strip()[-1000:]}")
        return Result("published", "Awesome Codex Plugins PR opened", {"remoteUrl": pr.stdout.strip().splitlines()[-1]})

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The code runs npx --yes @hol-org/registry ... without pinning a specific package version. That causes execution of whatever package version is current at runtime, which is a software supply-chain risk: a compromised upstream package, malicious new release, or dependency hijack could lead to arbitrary code execution on the publisher's machine. In the context of a skill whose purpose is publishing to registries, this is more dangerous because invoking third-party CLIs is part of the normal workflow and likely to run with developer credentials and repository access.

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

python
def _git(cwd: Path, *args: str) -> str:
    result = subprocess.run(["git", *args], cwd=cwd, text=True, capture_output=True, check=False)
    if result.returncode:
        return ""
    return result.stdout.strip()

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code persists skill state to a JSON file in the user's home directory, which is a file-write operation covered by the warning requirement for code files. The function performs the write silently and includes no confirmation prompt, logging, print statement, or explanatory comment/docstring disclosing that local state will be created or modified.

Content

No source excerpt is available for this finding.