This document describes the security architecture of mcp-github-crunchtools.
| Asset | Sensitivity | Impact if Compromised |
|---|---|---|
| GitHub Personal Access Token | Critical | Full account access, code access, CI/CD manipulation |
| Repository Source Code | High | Intellectual property theft, supply chain attacks |
| Pull Requests / Issues | Medium | Information disclosure, workflow manipulation |
| CI Check Results | Medium | Infrastructure details |
| Actor | Capability | Motivation |
|---|---|---|
| Malicious AI Agent | Can craft tool inputs | Data exfiltration, privilege escalation |
| Local Attacker | Access to filesystem | Token theft, configuration tampering |
| Network Attacker | Man-in-the-middle | Token interception (mitigated by TLS) |
| Vector | Description | Mitigation |
|---|---|---|
| Token Leakage | Token exposed in logs, errors, or outputs | Never log tokens, scrub from errors |
| Input Injection | Malicious owner/repo or path | Strict input validation |
| Path Traversal | Manipulated file paths | Allowlist validation, reject .. |
| SSRF | Redirect API calls to internal services | HTTPS enforcement, URL validation |
| Denial of Service | Exhaust GitHub rate limits | Rate-limit awareness |
| Privilege Escalation | Access beyond token scope | Server relies on token scope |
| Supply Chain | Compromised dependencies | Automated CVE scanning |
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Input Validation │
│ - Allowlist for owner/repo characters │
│ - Positive-integer validation for issue/PR numbers │
│ - Path traversal rejection for file reads │
├─────────────────────────────────────────────────────────────┤
│ Layer 2: Token Handling │
│ - Environment variable only (never file, never arg) │
│ - Never log, never include in errors │
│ - Use Authorization: Bearer header (not in URL) │
├─────────────────────────────────────────────────────────────┤
│ Layer 3: API Client Hardening │
│ - Configurable base URL with HTTPS enforcement (GHES) │
│ - Pinned X-GitHub-Api-Version │
│ - TLS certificate validation (default in httpx) │
│ - Request timeout enforcement (30s) │
│ - Response size limits (10MB) │
├─────────────────────────────────────────────────────────────┤
│ Layer 4: Output Sanitization │
│ - Redact tokens from any error messages │
│ - Limit response sizes to prevent memory exhaustion │
│ - Structured errors without internal details │
├─────────────────────────────────────────────────────────────┤
│ Layer 5: Runtime Protection │
│ - No filesystem writes │
│ - No shell execution (subprocess) │
│ - No dynamic code evaluation (eval/exec) │
├─────────────────────────────────────────────────────────────┤
│ Layer 6: Supply Chain Security │
│ - Automated CVE scanning via GitHub Actions │
│ - Dependabot alerts enabled │
│ - Container built on Hummingbird for minimal CVEs │
└─────────────────────────────────────────────────────────────┘
The API token is handled with multiple protections:
from pydantic import SecretStr
class Config:
def __init__(self):
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ConfigurationError("GITHUB_TOKEN required")
# Store as SecretStr to prevent accidental logging
self._token = SecretStr(token)
@property
def token(self) -> str:
"""Get token value - use sparingly."""
return self._token.get_secret_value()
def __repr__(self) -> str:
return "Config(token=***)"The API base URL is validated to prevent SSRF:
- Must be a valid URL with scheme and netloc
- Must use HTTPS unless connecting to localhost
- Trailing slashes are stripped
- Defaults to
https://api.github.com; override withGITHUB_API_URLfor GHES
All inputs are validated:
- Owner / repo names: letters, digits, dots, hyphens, underscores only
- Issue / PR / page numbers: must be positive integers
- File paths: rejected if they contain
.. - States: allowlist of "open", "closed", "all"
- Comment bodies: 1-65536 characters, extra fields rejected (extra="forbid")
Errors are sanitized before being returned:
class GitHubApiError(UserError):
def __init__(self, code: int, message: str):
# Sanitize message to remove any token references
token = os.environ.get("GITHUB_TOKEN", "")
safe_message = message.replace(token, "***") if token else message
super().__init__(f"GitHub API error {code}: {safe_message}")GitHub signals primary rate limiting with HTTP 403 plus
x-ratelimit-remaining: 0; the client maps this (and HTTP 429) to a
RateLimitError that surfaces the retry window.
Grant read access to repository contents, issues, and pull requests
(fine-grained PAT), or repo:status + public_repo for classic tokens limited
to public repositories.
Capabilities: list/read issues, PRs, diffs, checks, files; search Risk: Information disclosure only
Add write access to issues and pull requests if you need
create_issue_comment_tool.
Capabilities: the above plus commenting on issues/PRs Risk: Can post comments if the token is compromised
For minimum privilege, grant only:
- Read contents/issues/pull-requests — if you only need to read
- Add write issues/pull-requests — only if you need to comment
This project uses GitHub Actions to automatically scan for CVEs:
- Scheduled Scans: dependency audits on a regular cadence
- PR Checks: every pull request is scanned before merge
- Dependabot: enabled for automatic security updates
The container image is built on Hummingbird Python from Project Hummingbird:
| Advantage | Description |
|---|---|
| Minimal CVE Count | Dramatically reduced attack surface |
| Rapid Security Updates | Security patches applied promptly |
| Python Optimized | Pre-configured Python environment |
| Non-Root Default | Runs as non-root user |
| Production Ready | Proper signal handling, minimal footprint |
| Event | Level | Fields |
|---|---|---|
| Server startup | INFO | version, GitHub API URL |
| GitHub API call | DEBUG | method, path (no auth headers) |
| Permission denied | WARN | required_scope |
| Rate limited | WARN | retry_after |
| Error | ERROR | error_type (no internals) |
- API tokens (any form)
- Full request/response bodies
- Issue/PR descriptions (may contain secrets)
- File content
Before each release:
- All inputs validated
- No token exposure in logs or errors
- No filesystem writes
- No shell execution
- No eval/exec
- Rate limiting considered
- Error messages don't leak internals
- Dependencies scanned for CVEs
- Container rebuilt with latest Hummingbird base
Report security vulnerabilities using GitHub's private security advisory. This creates a private channel visible only to maintainers.
Do NOT open public issues for security vulnerabilities.