Back to skill

Security audit

Prompt Git

Security checks for vulnerabilities and agentic risk

Overview

PromptGit is a coherent local prompt manager, but it has a real local file-read safety issue when repository metadata is poisoned.

Review before installing if you may use shared, synced, or imported PromptGit repositories. Keep the repository private, do not import or sync untrusted repository metadata, back up before using --overwrite or manual deletion, and avoid storing secrets or regulated data unless filesystem permissions or encryption protect the files.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
prompt_git.py:127
Finding

Unvalidated version identifiers permit path traversal during content reads

Content
View full analysis
str: """Get content file path for a version.""" return os.path.join(self._get_prompt_dir(name), f'{version_id}.txt') def _load_versions(self, name: str) -> List[PromptVersion]: """Load version history for a prompt.""" versions_path = self._get_versions_path(name) if not os.path.exists(versions_path): return [] try: with open(versions_path, 'r', encoding='utf-8') as f: data = json.load(f) return [PromptVersion(**v) for v in data] ``` The resulting path is subsequently opened without verifying that it remains inside the prompt directory: ```python for v in versions: if v.id == version_id: # Load content content_path = self._get_content_path(name, v.id) try: with open(content_path, 'r', encoding='utf-8') as f: v.content = f.read() return v except FileNotFoundError: return None ``` ### Technical Analysis Prompt names are validated by `_sanitize_name()`, but version identifiers loaded from `versions.json` are trusted without equivalent validation. `_get_content_path()` directly appends the metadata-controlled identifier to a filesystem path. A version identifier such as `../../target` produces a path ending in `../../target.txt`. The operating system normalizes these traversal components when the file is opened, allowing the read operation to escape the intended prompt directory. The generated version identifiers are normally 16-character hexadecimal SHA-256 prefixes, so accepting path separators or identifiers outside that format is unnecessary. This issue is especially relevant to repositories obtained through the documented G ...[truncated 1412 chars]
Remediation
View remediation
str: if not isinstance(version_id, str) or not re.fullmatch(r'[0-9a-f]{16}', version_id): raise ValueError("Invalid version identifier") return version_id ``` 2. Perform containment validation on the complete resolved file path: ```python def _get_content_path(self, name: str, version_id: str) -> str: self._validate_version_id(version_id) prompt_dir = os.path.realpath(self._get_prompt_dir(name)) candidate = os.path.realpath( os.path.join(prompt_dir, f'{version_id}.txt') ) if os.path.commonpath([prompt_dir, candidate]) != prompt_dir: raise ValueError("Content path escapes the prompt directory") return candidate ``` 3. Validate all fields loaded from `versions.json` and `index.json` against an explicit schema before constructing `PromptVersion` objects. 4. Reject inconsistent metadata where a current version is absent from the validated version history. 5. Add regression tests covering `../`, `..\`, absolute paths, null bytes, malformed hashes, and poisoned `index.json` or `versions.json` files. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
prompt_git.py:72
Finding

Prompt repository files are created without enforced private permissions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (20)

Hidden Instructions

High
Category
Prompt Injection
Confidence
60% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

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

md
# PromptGit — Local Prompt Version Control

**Git for your prompts. Track every change, diff versions, rollback mistakes, never lose a good prompt again.**

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The code is related to prompt management and remains local/offline, so it is within the same general domain. However, the declared description emphasizes Git-like version tracking features such as tracking changes, diffing versions, and rollback. This specific code chunk does not implement diffing or rollback; instead, its primary function is importing and exporting prompts and histories for sharing. That is a materially different capability not reflected in the declared description. Although export/import may be a supporting feature of a broader prompt versioning tool, for this code chunk the main behavior is undeclared, so this should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The supplied code chunk does not implement the declared core functionality of version tracking, diffing versions, or rollback. Instead, it provides a search/browse CLI for an existing prompt repository. While this may be part of a broader prompt-management tool, this specific chunk’s primary behavior is materially different from the declared purpose. It remains local/offline in spirit and does not introduce suspicious external access, but the functional description is incomplete/inaccurate for this code because it omits the search, filtering, similarity, and stats capabilities and emphasizes version-control operations not present here.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · prompt_git.py (reported line 298)May include surrounding context.

python
reverse=True
        )
        
        return prompts[:limit]
    
    def get_similar(self, name: str, threshold: float = 0.5) -> List[tuple]:
        """

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · prompt_search.py (reported line 176)May include surrounding context.

python
reverse=True
        )
        
        return prompts[:limit]
    
    def get_similar(self, name: str, threshold: float = 0.5) -> List[tuple]:
        """

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This markdown file instructs users to manually delete folders from ~/.promptgit/prompts/, which is a destructive operation affecting stored prompt data. While the section explains that deletion is not built in to avoid accidental deletes, the workaround itself does not explicitly warn that manual folder deletion can permanently remove prompts or versions if they are not otherwise backed up.

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

Import a prompt

python3 prompt_export.py import shared-prompt.json

Import and overwrite existing

python3 prompt_export.py import shared-prompt.json --overwrite

text

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documented import ... --overwrite flow can replace existing prompt history or current prompt state without a prominently colocated warning, increasing the risk of accidental destructive writes. In a tool that manages long-lived prompt assets, users may invoke overwrite casually and lose important local data or replace trusted prompts with imported content.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
77% confidence
Finding

The documented overwrite import operation can persist externally sourced content into the local repository and replace existing records, creating a durable state change from imported data. Even though the README advises against importing untrusted JSON elsewhere, the usage example normalizes overwrite behavior without emphasizing persistence and recovery risks at the point of action.

Content

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

Import a prompt

python3 prompt_export.py import shared-prompt.json

Import and overwrite existing

python3 prompt_export.py import shared-prompt.json --overwrite

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

Telling users to manually delete the prompt folder without an adjacent irreversible-data-loss warning creates a realistic risk of accidental destruction of stored prompts and version history. Because the storage layout is filesystem-based, users may remove the wrong directory or assume recovery is supported when it is not.

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 · prompt_export.py (reported line 144)May include surrounding context.

python
# Check if exists
        existing = self.repo.get_version(name)
        if existing and not overwrite:
            raise ValueError(f"Prompt already exists: {name}. Use --overwrite to replace.")
        
        # Import history if available and requested
        if preserve_history and 'history' in data:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

When importing with both overwrite enabled and history preservation, the code deletes the existing versions file before reconstructing history from untrusted import data. If the import is malformed, incomplete, reordered, or fails partway through, prior local history can be permanently lost without an additional confirmation step or atomic rollback path.

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 · prompt_git.py (reported line 192)May include surrounding context.

python
# Get parent (most recent version)
        parent_id = versions[-1].id if versions else None
        
        # Create new version
        version = PromptVersion(
            id=version_id,
            content=content,

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This markdown file includes an explicit example for import shared-prompt.json --overwrite, which indicates a potentially destructive operation that can replace existing stored prompt content. Although the README contains a general disclaimer later, it does not provide a specific user warning near this operation about overwriting existing data or recommending backup/review before use.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

md
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

md
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for

Vague Triggers

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The manifest description is broad marketing language about tracking and rolling back prompts, but it does not define when the skill should activate or what user requests should map to this skill versus other prompt-editing tasks. For manifest/markdown files, the absence of specific trigger scope or negative examples can cause unintended invocations in systems that rely on descriptive matching.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · prompt_search.py (reported line 95)May include surrounding context.

python
results = []
        
        for prompt in prompts:
            date_str = getattr(prompt, field, None)
            if not date_str:
                continue

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · prompt_search.py (reported line 353)May include surrounding context.

python
else:
                print(f"Found {len(results)} prompts:\n")
                for p in results:
                    date_val = getattr(p, args.field)
                    print(f"  {p.name} — {date_val[:10]}")
        
        elif args.command == 'regex':