Back to skill

Security audit

Manifest Diff

Security checks for vulnerabilities and agentic risk

Overview

The bundled manifest diff tool itself is simple, but its install instructions tell users to download executable Python from a mutable GitHub branch before running it.

Review the install path before using this skill. Prefer the bundled manifest_diff.py from the reviewed package, or require a pinned commit and checksum before downloading and executing a remote copy. Do not run the CI verifier against untrusted repositories outside an isolated environment.

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

T03 · Remote Payload Retrieval and Execution

Warning
Location
README.md:33
Finding

Mutable Remote Python Payload Downloaded and Subsequently Executed

Content
View full analysis

Vulnerability Details

File Location: README.md:33-43; duplicate instructions at SKILL.md:31-41
Vulnerability Type: Remote payload retrieval and execution through a mutable upstream branch
Risk Level: Medium

Complete vulnerable code snippet (README.md:33-43):

markdown
## Install

```bash
# Requires Python 3.8+. No pip install needed.
curl -O https://raw.githubusercontent.com/itsPremkumar/manifest-diff/main/manifest_diff.py
# Or copy the file anywhere — it's self-contained.

Quick start

bash
python manifest_diff.py self-test
text

The same installation and execution sequence appears in `SKILL.md:31-41`.

### Technical Analysis

The documented installation process downloads executable Python source from the mutable `main` branch of a personal GitHub repository. The command does not pin the payload to an immutable commit or versioned release and does not verify a checksum or cryptographic signature.

Although `curl` does not execute the response directly, the quick-start instructions subsequently execute the downloaded file with Python. The effective payload can therefore change after the Skill package has been reviewed. If the maintainer account, repository, branch, or content-delivery path is compromised, the remote file could be replaced with arbitrary Python code.

Remote retrieval is not required for the declared manifest-comparison functionality because `manifest_diff.py` is already included in the audited package. The instruction consequently introduces an avoidable network and supply-chain trust boundary beyond the minimum privileges needed by the Skill.

### Attack Path

1. An attacker compromises the upstream maintainer account, repository, or mutable `main` branch.
2. The attacker replaces `manifest_diff.py` with malicious Python code.
3. A user follows the installation command in `README.md` or `SKILL.md`.
4. `curl` downloads the attacker-controlled version wit
...[truncated 950 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the remote download instruction and direct users to run the bundled, reviewed manifest_diff.py.
  2. If remote installation must remain supported, reference an immutable commit hash or versioned release artifact rather than main.
  3. Publish and require verification of a SHA-256 checksum or cryptographic signature before execution.
  4. Use failure-aware download options such as curl --fail --location --output manifest_diff.py.
  5. Display the expected checksum in documentation and stop installation when verification fails.
  6. Keep network retrieval and execution as separate, explicit steps so users can inspect the downloaded source.
  7. Apply the same correction to both README.md and SKILL.md to prevent users from following an unsafe duplicate instruction.

T09 · Insecure Skill Coding Practices

Note
Location
ci/ci_check.py:16
Finding

Deployment Check Silently Validates the Wrong Directory

Content
View full analysis

Vulnerability Details

File Location: ci/ci_check.py:16-20, with the result trusted by ci/verify_product.py:97-100
Vulnerability Type: Fail-open CI validation caused by incorrect path resolution
Risk Level: Low

Complete vulnerable code snippet (ci/ci_check.py:16-20):

python
def main():
    here = os.path.dirname(os.path.abspath(__file__))
    skill = os.path.join(here, "SKILL.md")
    if not os.path.isfile(skill):
        print("SKIP: no SKILL.md (not a skill package)")
        return 0

Code that treats the skipped check as a pass (ci/verify_product.py:97-100):

python
ci = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ci_check.py")
if os.path.isfile(ci) and os.path.isfile(skill):
    r = subprocess.run([sys.executable, ci], cwd=folder, capture_output=True, text=True)
    all_ok &= axis(r.returncode == 0, "deploy-ready (ci/ci_check.py PASS)")

Technical Analysis

ci_check.py derives here from its own __file__ path and consequently searches for ci/SKILL.md. In this package, the actual file is located at the repository root as SKILL.md. Setting cwd=folder in verify_product.py does not correct the issue because the checker uses an absolute path based on __file__, not the current working directory.

When ci/SKILL.md is absent, ci_check.py prints a skip message and returns exit code zero. verify_product.py only checks that zero exit status and labels the result deploy-ready (ci/ci_check.py PASS). Thus, the intended frontmatter and tool-presence checks are not actually applied to the root Skill file.

This is a fail-open validation weakness rather than direct arbitrary code execution. It reduces confidence in the package's CI security and correctness gates.

Attack Path

  1. A malformed, incomplete, or intentionally manipulated root SKILL.md is submitted to the verification process.
  2. verify_product.py invokes `ci/ci_chec ...[truncated 1049 chars]
Remediation
View remediation

Remediation Suggestions

  1. Make ci_check.py accept the product root as an explicit command-line argument.
  2. Alternatively, resolve SKILL.md from os.getcwd() when the checker is intentionally run with cwd=folder, or use the parent directory of ci/.
  3. Return a nonzero status when SKILL.md is required but absent; do not treat a missing required file as a successful skip.
  4. Update verify_product.py to pass the target product directory explicitly.
  5. Distinguish PASS, SKIP, and FAIL through separate exit codes or structured output.
  6. Require both a zero exit code and an explicit successful validation result before reporting deployment readiness.
  7. Add regression tests confirming that malformed root frontmatter fails, a missing root SKILL.md fails, and a valid root package passes.
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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
Findings (4)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

This function executes arbitrary Python files found in the target product directory using the local interpreter to probe for a self-test mode. In a CI or verification context, this means untrusted repository code is executed before trust is established, enabling arbitrary code execution on the runner if a malicious skill provides a crafted Python file.

Content

Scanner excerpt · ci/verify_product.py (reported line 37)May include surrounding context.

python
def has_self_test(fp):
    r = subprocess.run([sys.executable, fp, "self-test"], capture_output=True, text=True, timeout=30)
    return r.returncode == 0 and "PASS" in r.stdout

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 · ci/verify_product.py (reported line 67)May include surrounding context.

python
all_ok &= axis(False, "frontmatter (no SKILL.md)")
        all_ok &= axis(False, "docs (no SKILL.md)")
    # 3 compiles
    comp_ok = all(subprocess.run([sys.executable, "-m", "py_compile", fp],
                                capture_output=True).returncode == 0 for fp in files)
    all_ok &= axis(comp_ok, "compiles (py_compile all .py)")
    # 4 self-test (any of 3 valid forms)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

This code directly executes any test_*.py or *_test.py file discovered in the product folder. In a repository verification workflow, attackers can place malicious code in a test file to gain arbitrary code execution on the analyst or CI environment, potentially exfiltrating tokens, modifying artifacts, or pivoting within the build system.

Content

Scanner excerpt · ci/verify_product.py (reported line 79)May include surrounding context.

python
tests = [os.path.join(folder, f) for f in os.listdir(folder)
                 if (f.startswith("test_") or f.endswith("_test.py")) and f.endswith(".py")]
        if tests:
            st_ok = all(subprocess.run([sys.executable, t], capture_output=True, text=True,
                                      timeout=30).returncode == 0 for t in tests)
            st_note = f"{len(tests)} test_*.py"
        elif re.search(r"(?im)^test\s*:", skill_txt):

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 · ci/verify_product.py (reported line 92)May include surrounding context.

python
# 7 deploy-ready
    ci = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ci_check.py")
    if os.path.isfile(ci) and os.path.isfile(skill):
        r = subprocess.run([sys.executable, ci], cwd=folder, capture_output=True, text=True)
        all_ok &= axis(r.returncode == 0, "deploy-ready (ci/ci_check.py PASS)")
    else:
        all_ok &= axis(True, "deploy-ready (skipped)")