Platform Bridge - Security Documentation¶
Security analysis and best practices fer the platform bridge module.
Security Model¶
The platform bridge follows a delegation security model where authentication and authorization be handled by official CLI tools (gh and az), not by the bridge itself.
Core Principles¶
- No Credential Storage: Bridge never stores or handles credentials
- CLI Authentication: Delegates all authentication to gh/az CLI tools
- Input Validation: All user inputs be validated before subprocess calls
- Safe Subprocess: Parameterized commands prevent shell injection
- Fail Secure: Errors don't leak sensitive information
Authentication Delegation¶
GitHub Authentication¶
The bridge uses gh CLI fer all GitHub operations:
# Bridge NEVER handles GitHub tokens directly
bridge = PlatformBridge() # No tokens or credentials passed
# Authentication handled by gh CLI
issue = bridge.create_issue(title="Test", body="Body")
# Under the hood: gh uses credentials from ~/.config/gh/hosts.yml
User Authentication Flow:
- User runs:
gh auth login - GitHub CLI authenticates through OAuth
- Credentials stored in
~/.config/gh/hosts.yml - Bridge calls
ghcommands which use stored credentials
Benefits:
- Bridge code never sees tokens
- GitHub handles token refresh
- Standard gh security model applies
Azure DevOps Authentication¶
The bridge uses az CLI fer all Azure DevOps operations:
# Bridge NEVER handles Azure DevOps PATs directly
bridge = PlatformBridge() # No tokens or credentials passed
# Authentication handled by az CLI
issue = bridge.create_issue(title="Test", body="Body")
# Under the hood: az uses credentials from ~/.azure/
User Authentication Flow:
- User runs:
az login - Azure CLI authenticates through browser
- Credentials stored in
~/.azure/ - Bridge calls
azcommands which use stored credentials
Benefits:
- Bridge code never sees tokens or PATs
- Azure handles token refresh
- Standard az security model applies
Input Validation¶
All user inputs be validated before bein' passed to subprocess calls.
Title Validation¶
def _validate_title(title: str) -> None:
"""Validate issue/PR title"""
if not title or not title.strip():
raise ValueError("Title cannot be empty")
if len(title) > 256:
raise ValueError("Title too long (max 256 characters)")
# No shell metacharacters in title
dangerous_chars = ['$', '`', '$(', '|', '&', ';']
if any(char in title for char in dangerous_chars):
raise ValueError(f"Title contains invalid characters: {dangerous_chars}")
Branch Name Validation¶
def _validate_branch_name(branch: str) -> None:
"""Validate git branch name"""
if not branch or not branch.strip():
raise ValueError("Branch name cannot be empty")
# Git branch name rules
invalid_patterns = ['..', '~', '^', ':', '\\', '*', '?', '[']
if any(pattern in branch for pattern in invalid_patterns):
raise ValueError(f"Branch name contains invalid patterns")
if branch.startswith('/') or branch.endswith('/'):
raise ValueError("Branch name cannot start or end with /")
Body/Description Validation¶
def _validate_body(body: str) -> None:
"""Validate issue/PR body"""
if not body or not body.strip():
raise ValueError("Body cannot be empty")
# No null bytes (can confuse subprocess)
if '\x00' in body:
raise ValueError("Body contains null bytes")
# Reasonable size limit (prevent DoS)
if len(body) > 65536: # 64KB
raise ValueError("Body too large (max 64KB)")
Safe Subprocess Usage¶
All subprocess calls use parameterized commands to prevent shell injection.
Correct Pattern (Safe)¶
# SAFE - Command and args as list
subprocess.run(
["gh", "issue", "create", "--title", title, "--body", body],
capture_output=True,
text=True,
timeout=30
)
Why this be safe:
- Command and arguments passed as list
- No shell interpretation
- Arguments can't be interpreted as commands
- Even if title/body contain
; rm -rf /, they be treated as literal strings
Anti-Pattern (Unsafe)¶
# UNSAFE - Don't do this!
subprocess.run(
f"gh issue create --title '{title}' --body '{body}'",
shell=True, # ❌ Dangerous!
capture_output=True
)
Why this be dangerous:
- Shell interprets the entire string
- Special characters in title/body can execute commands
- Example attack:
title = "test'; rm -rf /; echo '"
Timeout Protection¶
All subprocess calls have timeouts to prevent hangs:
Timeout Values:
- Standard operations: 30 seconds
- CI status checks: 60 seconds (can be slow)
- Large file uploads: 120 seconds
Error Handling¶
Errors be handled to prevent information leakage:
Safe Error Messages¶
try:
result = subprocess.run(cmd, ...)
if result.returncode != 0:
# Don't expose full command in error
raise RuntimeError(f"Operation failed: {sanitize_error(result.stderr)}")
except subprocess.TimeoutExpired:
# Don't expose what command timed out
raise RuntimeError("Operation timed out after 30 seconds")
except Exception as e:
# Don't expose internal details
raise RuntimeError(f"Unexpected error: {type(e).__name__}")
Information Leakage Prevention¶
What we DON'T expose:
- Full subprocess commands (might contain sensitive args)
- File system paths (might reveal directory structure)
- Internal stack traces (might reveal implementation details)
What we DO expose:
- Operation type (e.g., "create issue failed")
- User-actionable guidance (e.g., "run gh auth login")
- Error categories (e.g., "authentication required")
Filesystem Security¶
The bridge reads git configuration but never writes to filesystem (except through CLI tools).
Read-Only Operations¶
# Bridge only reads git config
def _get_git_remote(repo_path: Path) -> str:
"""Read git remote URL (read-only)"""
result = subprocess.run(
["git", "remote", "-v"],
cwd=repo_path,
capture_output=True,
text=True
)
return result.stdout
No Filesystem Writes¶
The bridge never writes to:
- Git configuration (
.git/config) - Credential files (
~/.config/gh/,~/.azure/) - System directories
- Temporary files with sensitive data
All writes be handled by official CLI tools which have their own security models.
Threat Model¶
Threats We Mitigate¶
- Shell Injection: Parameterized commands prevent command injection
- Credential Theft: No credentials stored or handled by bridge
- Information Leakage: Error messages don't expose sensitive details
- DoS via Large Inputs: Size limits on all inputs
- Path Traversal: Repository paths validated
Threats Out of Scope¶
These be handled by CLI tools or operating system:
- Network Security: gh/az CLI handle HTTPS connections
- Token Refresh: CLI tools manage token lifecycle
- Multi-Factor Auth: Handled by GitHub/Azure DevOps
- Rate Limiting: CLI tools handle API rate limits
- Audit Logging: Platform services log all operations
Trust Boundaries¶
User Code → PlatformBridge → gh/az CLI → GitHub/Azure DevOps API
↑ ↑ ↑ ↑
| | | |
| | | +-- Trusted (official API)
| | +----------------- Trusted (official CLI)
| +---------------------------------- Trusted (our code)
+----------------------------------------------- Untrusted (user input)
Trust Assumptions:
- User input be UNTRUSTED (validate everything)
- gh/az CLI tools be TRUSTED (official tools)
- Platform APIs be TRUSTED (GitHub/Azure)
- Operating system be TRUSTED (subprocess isolation)
Security Best Practices¶
For Users¶
- Keep CLI Tools Updated:
# GitHub CLI
gh version # Check current version
brew upgrade gh # Update (macOS)
# Azure CLI
az version # Check current version
brew upgrade azure-cli # Update (macOS)
- Use Secure Authentication:
# GitHub - Use OAuth, not PATs
gh auth login # Follow OAuth flow
# Azure - Use browser auth
az login # Opens browser
- Review Permissions:
# GitHub - Check token permissions
gh auth status
# Azure - Check account permissions
az account show
- Rotate Credentials Regularly:
For Developers¶
- Never Add Credential Parameters:
# ❌ WRONG - Don't add token parameters
def create_issue(self, title: str, token: str):
...
# ✅ RIGHT - Delegate to CLI
def create_issue(self, title: str):
subprocess.run(["gh", "issue", "create", ...])
- Always Validate Inputs:
# ✅ RIGHT - Validate before subprocess
def create_issue(self, title: str, body: str):
self._validate_title(title)
self._validate_body(body)
subprocess.run([...])
- Use Parameterized Commands:
# ✅ RIGHT - List of args
subprocess.run(["gh", "issue", "create", "--title", title])
# ❌ WRONG - String with shell=True
subprocess.run(f"gh issue create --title '{title}'", shell=True)
- Set Timeouts:
# ✅ RIGHT - Always set timeout
subprocess.run(cmd, timeout=30)
# ❌ WRONG - No timeout (can hang forever)
subprocess.run(cmd)
Security Audit Checklist¶
When reviewin' platform bridge code:
- No credential storage or handling
- All subprocess calls use list (not string with shell=True)
- All user inputs validated before subprocess
- Timeouts set on all subprocess calls
- Error messages don't leak sensitive info
- No filesystem writes except through CLI tools
- Branch names validated against git rules
- Size limits enforced on all inputs
- No null bytes in string inputs
- Exception handling prevents info leakage
Reporting Security Issues¶
If ye discover a security vulnerability in the platform bridge:
- Don't open a public GitHub issue
- Do email security@amplihack.dev with details
- Include:
- Description of vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if ye have one)
We'll respond within 48 hours and coordinate a fix.
See Also¶
- Platform Bridge Overview - Feature documentation
- API Reference - Complete API
- GitHub CLI Security - gh authentication
- Azure CLI Security - az authentication