Back to skill

Security audit

Repo PR Triage

Security checks for vulnerabilities and agentic risk

Overview

This skill performs disclosed GitHub PR triage tasks, with a real caution that repository text can influence generated prompts and should be reviewed before use.

Use this with repositories you trust or review the generated interview prompt before letting an agent act on it. Treat README, PR bodies, releases, and report text as untrusted; do not allow automatic labeling, closing, or posting based only on the generated output without human review.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/onboard.py:103
Finding
Indirect Prompt Injection Through Untrusted Repository Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/onboard.py:62-69`, `scripts/onboard.py:101-126`, and `scripts/onboard.py:182-194` **Vulnerability Type**: Untrusted remote content embedded into an Agent-facing prompt **Risk Level**: Medium ### Vulnerable Code ```python # scripts/onboard.py:62-69 # README readme = run_gh(["api", f"repos/{repo}/readme", "--jq", ".content", "-H", "Accept: application/vnd.github.raw+json"], ignore_errors=True) if readme: context["readme"] = readme[:5000] # Truncate to keep prompt manageable ``` ```python # scripts/onboard.py:101-126 readme_snippet = context.get("readme", "Not available")[:2000] prompt = f"""# Vision Interview for {name} ## Repo Context (auto-gathered) - **Repo:** {repo} - **Description:** {desc} - **Stars:** {stars} | **Language:** {language} - **Topics:** {', '.join(topic_names) if topic_names else 'none'} - **Open issues/PRs:** {context.get('open_issues', 'unknown')} ### README excerpt: ``` {readme_snippet} ``` ### Recent releases: {context.get('releases', 'None found')} --- ## Interview Questions Ask the repo owner these questions to build their vision document and scoring rubric. Adapt based on the repo context above. Skip questions that are already answered by the README. ``` ```python # scripts/onboard.py:182-194 # Generate interview prompt prompt = generate_interview_prompt(repo, context) # Save interview prompt output_dir = Path(args.output_dir) output_dir.mkdir(parents=True, exist_ok=True) interview_path = output_dir / "interview-prompt.md" interview_path.write_text(prompt) print(f"Interview prompt saved to: {interview_path}", file=sys.stderr) # Also print to stdout for the agent to use directly print(prompt) ``` ### Technical Analysis The onboarding workflow retrieves README content from a user-selected GitHub repository and interpolates that content verbatim into a prompt explicitly intended for direct Agent use. Repository owners and ...[truncated 2871 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all GitHub-derived fields as untrusted data, including README text, descriptions, topics, release output, and repository names. 2. Add an explicit instruction before the repository context stating that embedded content is data only and that instructions, tool requests, or policy claims inside it must never be followed. 3. Prevent Markdown fence termination by selecting a delimiter longer than any matching run in the content or by encoding the content as a JSON string inside a clearly delimited data structure. 4. Prefer extracting narrowly defined repository facts instead of embedding raw README content into an Agent prompt. 5. Separate trusted workflow instructions and untrusted repository evidence into distinct structured fields. 6. Sanitize or escape Markdown control characters when generating Agent-facing and human-facing documents. 7. Add adversarial tests covering: - Triple-backtick fence termination. - Fake system or developer messages. - Requests to invoke tools or disclose credentials. - Nested Markdown, HTML comments, and misleading headings. - Injection attempts in metadata and release text. 8. Ensure any Agent consuming the generated prompt operates with minimum privileges and requires explicit user confirmation before taking external or destructive actions. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (35)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The declared description presents a broader triage system for both PRs and issues, with onboarding, scanning, de-duplication, and reporting. The supplied code only implements the scanning portion for open pull requests. It fetches PR metadata with gh CLI, parses a local vision document for green/red keywords, scores PRs using fixed heuristics, identifies possible duplicate PRs by title overlap, and writes JSON. There is no code for interviewing a repo owner, generating a vision document, triaging issues, or producing markdown reports. The primary purpose is adjacent to the description but materially narrower, so this is a mismatch.

Self-Modification

High
Category
Rogue Agent
Content
### From Lex Fridman Podcast #491:
- "AI agents will replace 80% of apps" (timestamp 3:02)
- Discussed acquisition offers from OpenAI and Meta (chose OpenAI for alignment)
- "Self-modifying AI agent" as a core concept
- Security concerns discussed at length (1:02:26)

### From README:
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
Use `references/rubric-template.md` as the starting template for the rubric.
"""
    return prompt


def main():
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Credential Access

High
Category
Privilege Escalation
Content
self.assertEqual(onboard.parse_repo("owner/repo"), "owner/repo")

    def test_path_traversal(self):
        result = onboard.parse_repo("https://github.com/../../etc/passwd")
        self.assertIsInstance(result, str)

    def test_url_with_semicolon(self):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
self.assertEqual(onboard.parse_repo("owner/repo"), "owner/repo")

    def test_path_traversal(self):
        result = onboard.parse_repo("https://github.com/../../etc/passwd")
        self.assertIsInstance(result, str)

    def test_url_with_semicolon(self):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
self.assertEqual(onboard.parse_repo("owner/repo"), "owner/repo")

    def test_path_traversal(self):
        result = onboard.parse_repo("https://github.com/../../etc/passwd")
        self.assertIsInstance(result, str)

    def test_url_with_semicolon(self):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_shell_injection_in_repo_name(self):
        ctx = {"metadata": {"name": "$(rm -rf /)", "description": "", "stargazerCount": 0,
                             "primaryLanguage": {"name": "Python"}, "repositoryTopics": []}}
        prompt = onboard.generate_interview_prompt("$(rm -rf /)/repo", ctx)
        # Just verify it produces a string without crashing
        self.assertIsInstance(prompt, str)
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIn("none", md)

    def test_shell_injection_in_title(self):
        pr = _make_scored_pr(title="$(rm -rf /)")
        md = report.pr_to_markdown(pr, "owner/repo")
        self.assertIn("$(rm -rf /)", md)
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIn("none", md)

    def test_shell_injection_in_title(self):
        pr = _make_scored_pr(title="$(rm -rf /)")
        md = report.pr_to_markdown(pr, "owner/repo")
        self.assertIn("$(rm -rf /)", md)
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIn("none", md)

    def test_shell_injection_in_title(self):
        pr = _make_scored_pr(title="$(rm -rf /)")
        md = report.pr_to_markdown(pr, "owner/repo")
        self.assertIn("$(rm -rf /)", md)
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_shell_injection_in_title(self):
        pr = _make_scored_pr(title="$(rm -rf /)")
        md = report.pr_to_markdown(pr, "owner/repo")
        self.assertIn("$(rm -rf /)", md)


class TestGenerateReport(unittest.TestCase):
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_shell_injection_in_title(self):
        pr = _make_scored_pr(title="$(rm -rf /)")
        md = report.pr_to_markdown(pr, "owner/repo")
        self.assertIn("$(rm -rf /)", md)


class TestGenerateReport(unittest.TestCase):
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
100% 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).

Chaining Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
self.assertIsInstance(result, str)

    def test_url_injection(self):
        result = scan.parse_repo("https://github.com/owner/repo; rm -rf /")
        self.assertIsInstance(result, str)
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Static analysis

No suspicious patterns detected.