T01 · Skill Instruction Hijacking
- Location
prompt_builder.py:202- Finding
Untrusted GitHub Discussion Content Is Inserted Directly into the LLM Prompt
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill matches its stated purpose, but it handles GitHub tokens and may send private PR discussion text to an LLM when PR insights are enabled.
Install only if you are comfortable giving the skill read access to the GitHub repositories visible to the token. Prefer a fine-grained token or a narrowly scoped classic token with GITHUB_ORG set, leave PR insights off unless raw PR discussions are appropriate to send to your LLM provider, review and delete .pullstar artifacts when they may contain private data, and use an isolated Python environment with pinned dependencies where possible.
prompt_builder.py:202Untrusted GitHub Discussion Content Is Inserted Directly into the LLM Prompt
scripts/agent_finalize_1on1.py:150Mandatory Finalization Path Emits Unrelated Promotional Content and an External Link
SKILL.md:37Installation Instructions Use Unpinned Third-Party Dependencies
This description materially understates operational capability by framing the flow as a simple five-tool deterministic process while the documented behavior includes potentially unbounded GitHub API access, local credential/file access, network calls, and optional extraction of PR discussion text for LLM submission. That mismatch can cause operators to grant or approve the skill with an incomplete understanding of its actual data access and exfiltration surface.
This description materially understates operational capability by framing the flow as a simple five-tool deterministic process while the documented behavior includes potentially unbounded GitHub API access, local credential/file access, network calls, and optional extraction of PR discussion text for LLM submission. That mismatch can cause operators to grant or approve the skill with an incomplete understanding of its actual data access and exfiltration surface.
This description materially understates operational capability by framing the flow as a simple five-tool deterministic process while the documented behavior includes potentially unbounded GitHub API access, local credential/file access, network calls, and optional extraction of PR discussion text for LLM submission. That mismatch can cause operators to grant or approve the skill with an incomplete understanding of its actual data access and exfiltration surface.
This description materially understates operational capability by framing the flow as a simple five-tool deterministic process while the documented behavior includes potentially unbounded GitHub API access, local credential/file access, network calls, and optional extraction of PR discussion text for LLM submission. That mismatch can cause operators to grant or approve the skill with an incomplete understanding of its actual data access and exfiltration surface.
This description materially understates operational capability by framing the flow as a simple five-tool deterministic process while the documented behavior includes potentially unbounded GitHub API access, local credential/file access, network calls, and optional extraction of PR discussion text for LLM submission. That mismatch can cause operators to grant or approve the skill with an incomplete understanding of its actual data access and exfiltration surface.
This description materially understates operational capability by framing the flow as a simple five-tool deterministic process while the documented behavior includes potentially unbounded GitHub API access, local credential/file access, network calls, and optional extraction of PR discussion text for LLM submission. That mismatch can cause operators to grant or approve the skill with an incomplete understanding of its actual data access and exfiltration surface.
The skill is designed to retrieve a GitHub personal access token from CLI args, environment variables, local credential files, or a .env file, then use it for GitHub API access. Handling credentials in multiple local sources increases secret exposure risk, and the recommended classic PAT with broad repo scope amplifies impact if the skill, surrounding agent, or local environment is compromised.
- Python 3.11+
- Install dependencies: `pip install PyGithub python-dotenv requests`
- A GitHub personal access token (see Security section below)
---
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Default socket timeout for all network operations (GitHub API calls)
socket.setdefaulttimeout(60) # 60 seconds per API call max
# Load .env from repo root regardless of where the script is invoked from
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Default socket timeout for all network operations (GitHub API calls)
socket.setdefaulttimeout(60) # 60 seconds per API call max
# Load .env from repo root regardless of where the script is invoked from
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Default socket timeout for all network operations (GitHub API calls)
socket.setdefaulttimeout(60) # 60 seconds per API call max
# Load .env from repo root regardless of where the script is invoked from
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Default socket timeout for all network operations (GitHub API calls)
socket.setdefaulttimeout(60) # 60 seconds per API call max
# Load .env from repo root regardless of where the script is invoked from
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Default socket timeout for all network operations (GitHub API calls)
socket.setdefaulttimeout(60) # 60 seconds per API call max
# Load .env from repo root regardless of where the script is invoked from
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent.parent / ".env")
# ---------------------------------------------------------------------------
The skill instructs the agent to use shell execution, file reads/writes, environment-based secret resolution, and network access to GitHub and an LLM provider, but it does not declare any explicit tool scope such as allowed-tools or permissions. That omission weakens least-privilege controls and makes it harder for a host system to constrain what the skill may access, especially since the markdown itself directs the agent to run commands and handle sensitive artifacts.
The code intentionally packages raw PR review and comment excerpts into the LLM prompt, which means untrusted natural-language content from GitHub becomes model input and later part of a persisted payload. This increases risk of sensitive data leakage, unintended disclosure of internal discussions, and prompt-level influence from adversarial or manipulative comment text embedded in the source material.
The code persists the full LLM input payload to disk, and that payload can include raw PR review and comment text assembled earlier from GitHub activity. Even if this is intended for debugging or reproducibility, it creates a local data exposure channel: sensitive or user-provided text may be stored in plaintext artifacts, retained longer than expected, and accessed by other local users, tools, or later pipeline stages.
The manifest describes a fully deterministic pipeline with 5 tool calls and zero sub-agent spawns. However, the module docstring defines an agent flow where this script prepares artifacts, then instructs an agent to perform the LLM call and run a separate finalization step, which is materially broader than a purely deterministic no-sub-agent pipeline.
The code uses subprocess.run to invoke ingest.py, score.py, and agent_prepare_1on1.py. For a skill whose stated purpose is generating a 1-on-1 brief from GitHub activity, spawning local processes is not justified by the manifest description and introduces a capability beyond the stated semantic scope.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _run(cmd: list[str]) -> None:
"""Run a subprocess, inheriting stdout/stderr. Exit on failure."""
result = subprocess.run(cmd)
if result.returncode != 0:
sys.exit(result.returncode)
The manifest describes a 'fully deterministic pipeline — 5 tool calls, zero sub-agent spawns,' but this script explicitly consumes '.pullstar/llm_output_{login}.json' produced by an agent and even permits raw plain-text model output copied from a chat interface. That behavior introduces nondeterministic LLM-generated content into the pipeline, which materially conflicts with the manifest's determinism claim.
For a skill whose stated purpose is generating a 1-on-1 brief from GitHub activity, directly loading a project .env file and a user-scoped ~/.pullstar/credentials file is an additional secret-access capability. While a GitHub token is expected, the broader credential-harvesting behavior from multiple filesystem locations is not declared in the manifest and exceeds the minimal capability implied by the skill description.
No suspicious patterns detected.