Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:00:18 +08:00
commit 765529cd13
69 changed files with 18291 additions and 0 deletions

View File

@@ -0,0 +1,293 @@
# Converting Bash Scripts to Python uv Scripts
This document guides the conversion of bash scripts to Python uv scripts.
## When Conversion Makes Sense
### ✅ Good Candidates for Conversion
**Convert when**:
- Script needs better error handling
- Cross-platform compatibility required
- Complex data processing needed
- API interactions involved
- Script will grow in complexity
**Examples**:
```bash
# Good candidate - API interaction
curl -X POST https://api.example.com/data \
-H "Authorization: Bearer $TOKEN" \
-d '{"status": "active"}'
# Good candidate - Data processing
cat data.json | jq '.users[] | select(.active == true)'
# Good candidate - Complex logic
for file in $(find /data -name "*.log"); do
count=$(grep -c "ERROR" "$file")
if [ $count -gt 100 ]; then
# Complex processing
fi
done
```
### ❌ Keep as Bash When
**Don't convert when**:
- Simple file operations (cp, mv, mkdir)
- Shell-specific features heavily used (job control, pipes)
- System administration tasks
- Script is <10 lines and works fine
- Team primarily knows bash
**Examples**:
```bash
# Keep as bash - Simple operations
#!/bin/bash
mkdir -p /var/log/app
cp config.yaml /etc/app/
# Keep as bash - Shell-specific
#!/bin/bash
find /data -name "*.tmp" -mtime +7 -delete
# Keep as bash - System admin
#!/bin/bash
systemctl restart nginx
journalctl -u nginx -f
```
## Common Conversions
### File Operations
**Bash**:
```bash
#!/bin/bash
if [ -f "/etc/config.yaml" ]; then
cp /etc/config.yaml /backup/
fi
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
from pathlib import Path
import shutil
config = Path("/etc/config.yaml")
if config.exists():
shutil.copy(config, "/backup/")
```
### Environment Variables
**Bash**:
```bash
#!/bin/bash
API_URL=${API_URL:-"https://api.example.com"}
if [ -z "$API_TOKEN" ]; then
echo "Error: API_TOKEN not set" >&2
exit 1
fi
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import os
import sys
API_URL = os.getenv("API_URL", "https://api.example.com")
API_TOKEN = os.getenv("API_TOKEN")
if not API_TOKEN:
print("Error: API_TOKEN not set", file=sys.stderr)
sys.exit(1)
```
### Running Commands
**Bash**:
```bash
#!/bin/bash
set -euo pipefail
output=$(systemctl status nginx)
if [ $? -ne 0 ]; then
echo "Error: nginx not running"
exit 1
fi
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import subprocess
import sys
try:
result = subprocess.run(
["systemctl", "status", "nginx"],
capture_output=True,
text=True,
check=True
)
print(result.stdout)
except subprocess.CalledProcessError:
print("Error: nginx not running", file=sys.stderr)
sys.exit(1)
```
### HTTP Requests
**Bash**:
```bash
#!/bin/bash
response=$(curl -s -X GET https://api.github.com/users/octocat)
name=$(echo "$response" | jq -r '.name')
echo "Name: $name"
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# ]
# ///
import httpx
response = httpx.get("https://api.github.com/users/octocat")
response.raise_for_status()
data = response.json()
print(f"Name: {data['name']}")
```
### JSON Processing
**Bash**:
```bash
#!/bin/bash
jq '.users[] | select(.active == true) | .name' data.json
```
**Python**:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import json
from pathlib import Path
data = json.loads(Path("data.json").read_text())
active_names = [
user["name"]
for user in data["users"]
if user.get("active")
]
print("\n".join(active_names))
```
## Decision Framework
```text
Should I convert this bash script?
├─ Is it >50 lines? → Consider conversion
├─ Does it process JSON/YAML? → Strong candidate
├─ Does it make API calls? → Strong candidate
├─ Does it have complex logic? → Consider conversion
├─ Does it need better error handling? → Consider conversion
├─ Is it mostly shell commands? → Keep as bash
└─ Is it <10 lines and works? → Keep as bash
```
## Hybrid Approach
Sometimes the best solution is calling bash from Python:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""
Hybrid: Use Python for logic, bash for system commands.
"""
import subprocess
def backup_with_rsync(source: str, dest: str):
"""Use rsync (better than reimplementing in Python)."""
subprocess.run(
["rsync", "-av", "--delete", source, dest],
check=True
)
# Python logic here
# ...
# Leverage bash tools where appropriate
backup_with_rsync("/data/", "/backup/")
```
## Summary
**Convert to Python when**:
- Complex logic or data processing
- API interactions
- Cross-platform needs
- Better error handling required
- Will grow in complexity
**Keep as Bash when**:
- Simple file operations
- System administration
- Heavily uses shell features
- Works well and won't change
- Team expertise is bash
**Consider Hybrid**:
- Complex Python logic + system commands
- Leverage both Python libraries and shell tools

View File

@@ -0,0 +1,63 @@
# Dependency Management Reference
> **Status**: 🚧 Placeholder - Content in development
## Overview
Comprehensive guide to managing dependencies in UV single-file scripts using PEP 723 inline metadata.
## Topics to Cover
- [ ] Version pinning strategies
- [ ] Semantic versioning best practices
- [ ] Dependency conflict resolution
- [ ] Optional dependencies
- [ ] Development vs production dependencies
- [ ] Security updates and vulnerability scanning
- [ ] Lock file equivalents for scripts
## Quick Reference
### Version Pinning Strategies
**Exact pinning** (most restrictive):
```python
# /// script
# dependencies = ["requests==2.31.0"]
# ///
```
**Compatible release** (recommended):
```python
# /// script
# dependencies = ["requests~=2.31.0"] # >=2.31.0, <2.32.0
# ///
```
**Minimum version**:
```python
# /// script
# dependencies = ["requests>=2.31.0"]
# ///
```
### Following Repository Standards
From this repository's `pyproject.toml`, we use:
- `>=` for minimum versions with flexibility
- Specific version ranges for critical dependencies
- Regular dependency audits with Renovate
## TODO
This file will be expanded to include:
- Complete version specifier syntax
- Dependency resolution strategies
- Security scanning integration
- Update strategies and automation
- Conflict resolution techniques

View File

@@ -0,0 +1,391 @@
# PEP 723: Inline Script Metadata Specification
Complete reference for PEP 723 inline script metadata format used by uv.
## Overview
PEP 723 defines a standardized way to embed dependency and configuration metadata directly within Python script files. This eliminates the need for separate `requirements.txt` files and enables self-contained, reproducible scripts.
## Basic Format
```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "package-name>=1.0.0",
# ]
# ///
```
**Key Requirements:**
- Must appear as comments (`#`)
- Must use `# /// script` as opening marker
- Must use `# ///` as closing marker
- Must be valid TOML syntax
- Recommended placement: After shebang, before module docstring
## Complete Specification
### Required Fields
#### requires-python
Specifies minimum Python version:
```python
# /// script
# requires-python = ">=3.11"
# ///
```
**Formats:**
```python
requires-python = ">=3.11" # Minimum version
requires-python = ">=3.11,<3.13" # Version range
requires-python = "==3.12" # Exact version
```
#### dependencies
Lists required packages:
```python
# /// script
# dependencies = [
# "httpx>=0.27.0",
# "rich>=13.0.0",
# "typer~=0.9.0",
# ]
# ///
```
**Version Specifiers:**
```python
"package" # Any version
"package>=1.0" # Minimum version
"package>=1.0,<2.0" # Version range
"package~=1.2.3" # Compatible release (1.2.x)
"package==1.2.3" # Exact version
```
### Optional Fields
#### [tool.uv] Section
uv-specific configuration:
```python
# /// script
# requires-python = ">=3.11"
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2024-10-01T00:00:00Z"
# index-url = "https://pypi.org/simple"
# ///
```
**Available options:**
- `exclude-newer`: Only use packages published before this date
- `index-url`: Alternative PyPI index
- `extra-index-url`: Additional package indexes
- `find-links`: Additional package sources
- `no-index`: Ignore PyPI entirely
#### [tool.uv.sources]
Custom package sources:
```python
# /// script
# dependencies = ["my-package"]
#
# [tool.uv.sources]
# my-package = { git = "https://github.com/user/repo", tag = "v1.0" }
# ///
```
#### Valid [tool.uv] Fields
Additional uv-specific configuration (optional):
```python
# /// script
# requires-python = ">=3.11"
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2025-01-01T00:00:00Z" # Reproducibility constraint
# ///
```
**Note**: For custom metadata like purpose, team, author, use Python docstrings instead:
```python
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""
Purpose: cluster-monitoring
Team: infrastructure
Author: devops@example.com
Created: 2024-10-20
"""
```
## Complete Examples
### Minimal Script
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""Simple script with no dependencies"""
print("Hello, world!")
```
### Production Script
```python
#!/usr/bin/env -S uv run --script --quiet
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# "rich>=13.0.0",
# "typer>=0.9.0",
# ]
#
# [tool.uv]
# exclude-newer = "2025-01-01T00:00:00Z"
# ///
"""
API client for Proxmox cluster monitoring
Purpose: api-client
Team: infrastructure
Author: devops@spaceships.work
Usage:
check_cluster.py [--node NODE] [--json]
"""
import typer
import httpx
from rich import print
```
### With Git Dependencies
```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "my-internal-lib",
# ]
#
# [tool.uv.sources]
# my-internal-lib = { git = "https://github.com/org/lib.git", tag = "v1.2.3" }
# ///
```
### With Local Dependencies
```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "my-local-package",
# ]
#
# [tool.uv.sources]
# my-local-package = { path = "../my-package", editable = true }
# ///
```
## Placement Guidelines
### Correct Placement
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""Module docstring comes after metadata"""
import sys
def main():
pass
if __name__ == "__main__":
main()
```
### Multiple Metadata Blocks (Invalid)
```python
# ❌ INVALID - Only one metadata block allowed
# /// script
# requires-python = ">=3.11"
# ///
# /// script
# dependencies = ["httpx"]
# ///
```
## Validation
### Valid Metadata
```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0", # Comments allowed in arrays
# "rich",
# ]
# ///
```
### Invalid Metadata
```python
# ❌ Missing closing marker
# /// script
# requires-python = ">=3.11"
# ❌ Invalid TOML syntax
# /// script
# dependencies = httpx # Missing quotes
# ///
# ❌ Not in comments
/// script
requires-python = ">=3.11"
///
```
## Creating Metadata
### Manual Creation
Add metadata block manually:
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# ]
# ///
import httpx
```
### Using uv init
Generate script with metadata:
```bash
uv init --script my_script.py --python 3.11
```
### Using uv add
Add dependencies to existing script:
```bash
uv add --script my_script.py httpx rich typer
```
## Validation Tools
### Check Metadata Validity
```bash
# Parse metadata
uv run --script my_script.py --dry-run
# Validate with custom tool
python tools/validate_script.py my_script.py
```
### Extract Metadata
```python
import re
import tomllib
def extract_metadata(script_path: str) -> dict:
"""Extract PEP 723 metadata from script"""
with open(script_path) as f:
content = f.read()
# Find metadata block
pattern = r'# /// script\n((?:# .*\n)+)# ///'
match = re.search(pattern, content)
if not match:
return {}
# Parse TOML (remove leading # from each line)
toml_lines = match.group(1).split('\n')
toml_content = '\n'.join(line[2:] for line in toml_lines if line.startswith('# '))
return tomllib.loads(toml_content)
```
## Compatibility
### PEP 723 Support
- ✅ uv (native support)
- ✅ pip (via `pip-run`)
- ✅ pipx (v1.4.0+)
- ⚠️ Other tools (check documentation)
### Fallback for Non-Supporting Tools
```python
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx"]
# ///
"""
Fallback installation for non-PEP-723 tools:
pip install httpx
python script.py
"""
```
## Best Practices
1. **Always include requires-python** - Prevents compatibility issues
2. **Pin major versions** - `>=X.Y.Z` for stability
3. **Add metadata section** - Document purpose and ownership
4. **Keep dependencies minimal** - Only what's necessary
5. **Document fallbacks** - Help non-uv users
6. **Validate syntax** - Use validation tools
7. **Version consistently** - Match project conventions
## References
- [PEP 723 Specification](https://peps.python.org/pep-0723/)
- [uv Documentation](https://docs.astral.sh/uv/)
- [TOML Specification](https://toml.io/)
- [Python Version Specifiers](https://peps.python.org/pep-0440/)

View File

@@ -0,0 +1,523 @@
# Security Patterns for uv Scripts
Security best practices for Python single-file scripts following Virgo-Core patterns.
## Never Hardcode Secrets
### ❌ Anti-Pattern: Hardcoded Secrets
```python
# NEVER DO THIS
API_KEY = "sk_live_1234567890abcdef"
DATABASE_URL = "postgresql://user:password@localhost/db"
PROXMOX_PASSWORD = "admin123"
```
**Risks:**
- Secrets exposed in version control
- No audit trail
- Difficult to rotate
- Same credentials across environments
### ✅ Pattern 1: Environment Variables
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import os
import sys
def get_secret(name: str, required: bool = True) -> str:
"""Get secret from environment"""
value = os.getenv(name)
if required and not value:
print(f"Error: {name} environment variable not set", file=sys.stderr)
sys.exit(1)
return value
# Usage
PROXMOX_PASSWORD = get_secret("PROXMOX_PASSWORD")
API_URL = get_secret("PROXMOX_API_URL")
```
**Usage:**
```bash
export PROXMOX_PASSWORD="$(cat ~/.secrets/proxmox_pass)"
./script.py
```
### ✅ Pattern 2: Keyring Integration
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "keyring>=24.0.0",
# ]
# ///
import keyring
import sys
def get_password(service: str, username: str) -> str:
"""Get password from system keyring"""
password = keyring.get_password(service, username)
if not password:
print(f"Error: No password found for {username}@{service}", file=sys.stderr)
print(f"Set with: keyring set {service} {username}", file=sys.stderr)
sys.exit(1)
return password
# Usage
proxmox_password = get_password("proxmox", "terraform@pam")
```
**Setup:**
```bash
# Store password in system keyring
keyring set proxmox terraform@pam
# Prompts for password, stores securely
# Run script (no password in environment)
./script.py
```
### ✅ Pattern 3: Infisical Integration (Repository Standard)
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "infisical-python>=2.3.3",
# ]
# ///
from infisical import InfisicalClient
import os
import sys
def get_infisical_secret(
secret_name: str,
project_id: str,
environment: str = "prod",
path: str = "/"
) -> str:
"""Get secret from Infisical vault"""
try:
client = InfisicalClient()
secret = client.get_secret(
secret_name=secret_name,
project_id=project_id,
environment=environment,
path=path
)
return secret.secret_value
except Exception as e:
print(f"Error retrieving secret {secret_name}: {e}", file=sys.stderr)
sys.exit(1)
# Usage (following Virgo-Core pattern)
PROXMOX_PASSWORD = get_infisical_secret(
secret_name="PROXMOX_PASSWORD",
project_id="7b832220-24c0-45bc-a5f1-ce9794a31259",
environment="prod",
path="/matrix"
)
```
**Setup:**
```bash
# Authenticate with Infisical
infisical login
# Run script (secrets fetched automatically)
./script.py
```
## Input Validation
### ❌ Anti-Pattern: No Validation
```python
import subprocess
# DANGEROUS - Command injection risk
def ping_host(hostname: str):
subprocess.run(f"ping -c 3 {hostname}", shell=True)
# User provides: "localhost; rm -rf /"
ping_host(sys.argv[1]) # 💥 System destroyed
```
### ✅ Pattern: Validate All Inputs
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
import re
import sys
import subprocess
def validate_hostname(hostname: str) -> bool:
"""Validate hostname format"""
# Only allow alphanumeric, dots, hyphens
pattern = r'^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$'
if not re.match(pattern, hostname):
return False
# Max length check
if len(hostname) > 253:
return False
return True
def ping_host(hostname: str):
"""Safely ping a host"""
if not validate_hostname(hostname):
print(f"Error: Invalid hostname: {hostname}", file=sys.stderr)
sys.exit(1)
# Use list form (no shell injection)
result = subprocess.run(
["ping", "-c", "3", hostname],
capture_output=True,
text=True
)
return result.returncode == 0
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: ping_host.py <hostname>", file=sys.stderr)
sys.exit(1)
ping_host(sys.argv[1])
```
### Input Validation Patterns
```python
import re
from ipaddress import IPv4Address, AddressValueError
def validate_ip(ip: str) -> bool:
"""Validate IPv4 address"""
try:
IPv4Address(ip)
return True
except AddressValueError:
return False
def validate_port(port: int) -> bool:
"""Validate TCP/UDP port number"""
return 1 <= port <= 65535
def validate_vmid(vmid: int) -> bool:
"""Validate Proxmox VMID (100-999999999)"""
return 100 <= vmid <= 999999999
def validate_path(path: str) -> bool:
"""Validate file path (no directory traversal)"""
# Reject paths with ../
if ".." in path:
return False
# Reject absolute paths
if path.startswith("/"):
return False
return True
```
## Dependency Security
### Pin Dependencies
```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0", # ✅ Minimum version pinned
# "rich>=13.0.0", # ✅ Known good version
# ]
# ///
```
**Why pin?**
- Prevents automatic upgrades with vulnerabilities
- Ensures reproducible execution
- Allows controlled dependency updates
### Exclude Recent Packages
```python
# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx>=0.27.0"]
#
# [tool.uv]
# exclude-newer = "2024-10-01T00:00:00Z" # Only packages before this date
# ///
```
**Use cases:**
- Prevent supply chain attacks from compromised packages
- Freeze dependencies at known-good state
- Reproducible builds in CI/CD
### Check for Vulnerabilities
```bash
# Use safety or pip-audit
uv pip install safety
safety check --json
# Or use built-in tools
uv pip list --format json | jq '.[] | select(.name == "httpx")'
```
## File Operations Security
### ❌ Anti-Pattern: Unsafe File Access
```python
# DANGEROUS - Path traversal vulnerability
def read_log(filename: str):
with open(f"/var/log/{filename}") as f:
return f.read()
# User provides: "../../../etc/passwd"
read_log(sys.argv[1]) # 💥 Reads /etc/passwd
```
### ✅ Pattern: Safe File Operations
```python
import os
import sys
from pathlib import Path
def safe_read_log(filename: str, log_dir: str = "/var/log") -> str:
"""Safely read log file"""
# Resolve to absolute paths
log_dir_path = Path(log_dir).resolve()
file_path = (log_dir_path / filename).resolve()
# Ensure file is within log directory
try:
file_path.relative_to(log_dir_path)
except ValueError:
print(f"Error: Path traversal detected: {filename}", file=sys.stderr)
sys.exit(1)
# Check file exists and is readable
if not file_path.exists():
print(f"Error: File not found: {filename}", file=sys.stderr)
sys.exit(1)
if not file_path.is_file():
print(f"Error: Not a file: {filename}", file=sys.stderr)
sys.exit(1)
# Read with size limit
MAX_SIZE = 10 * 1024 * 1024 # 10MB
if file_path.stat().st_size > MAX_SIZE:
print(f"Error: File too large: {filename}", file=sys.stderr)
sys.exit(1)
with open(file_path) as f:
return f.read()
```
## Command Execution Security
### ❌ Anti-Pattern: Shell Injection
```python
# DANGEROUS
import subprocess
def list_directory(path: str):
subprocess.run(f"ls -la {path}", shell=True)
# User provides: "; rm -rf /"
list_directory(sys.argv[1]) # 💥 Disaster
```
### ✅ Pattern: Safe Command Execution
```python
import subprocess
import sys
def list_directory(path: str):
"""Safely list directory contents"""
# Validate path first
if not validate_path(path):
print(f"Error: Invalid path: {path}", file=sys.stderr)
sys.exit(1)
# Use list form (no shell)
try:
result = subprocess.run(
["ls", "-la", path],
capture_output=True,
text=True,
check=True,
timeout=5 # Prevent hanging
)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error: {e.stderr}", file=sys.stderr)
sys.exit(1)
except subprocess.TimeoutExpired:
print("Error: Command timed out", file=sys.stderr)
sys.exit(1)
```
## Logging and Audit
### Secure Logging
```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "structlog>=24.0.0",
# ]
# ///
import structlog
import sys
# Configure structured logging
structlog.configure(
processors=[
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
logger_factory=structlog.PrintLoggerFactory(file=sys.stderr),
)
log = structlog.get_logger()
def process_data(user_id: int, action: str):
"""Process user action with audit logging"""
log.info(
"user_action",
user_id=user_id,
action=action,
# Don't log sensitive data!
)
# ... processing logic ...
log.info(
"action_completed",
user_id=user_id,
action=action,
status="success"
)
```
### Never Log Secrets
```python
# ❌ BAD - Logs password
log.info(f"Connecting with password: {password}")
# ✅ GOOD - No sensitive data
log.info("Connecting to API", endpoint=api_url)
# ✅ GOOD - Masked credentials
log.info("Authentication successful", user=username)
```
## Network Security
### HTTPS Only
```python
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["httpx>=0.27.0"]
# ///
import httpx
import sys
def fetch_api(url: str) -> dict:
"""Fetch data from API (HTTPS only)"""
# Validate HTTPS
if not url.startswith("https://"):
print("Error: Only HTTPS URLs allowed", file=sys.stderr)
sys.exit(1)
with httpx.Client(verify=True) as client: # Verify SSL certs
response = client.get(url, timeout=10.0)
response.raise_for_status()
return response.json()
```
### Certificate Verification
```python
import httpx
import certifi
# Use trusted CA bundle
client = httpx.Client(verify=certifi.where())
# For internal CAs, specify cert path
client = httpx.Client(verify="/path/to/internal-ca.pem")
# ONLY disable for development (never in production)
if os.getenv("DEVELOPMENT") == "true":
client = httpx.Client(verify=False)
```
## Security Checklist
Before deploying a script:
- [ ] No hardcoded secrets
- [ ] Secrets from environment/keyring/Infisical
- [ ] All inputs validated
- [ ] No shell=True in subprocess
- [ ] File paths checked for traversal
- [ ] Dependencies pinned
- [ ] No sensitive data in logs
- [ ] HTTPS for network requests
- [ ] Certificate verification enabled
- [ ] Timeouts on external calls
- [ ] Error messages don't leak information
- [ ] Principle of least privilege applied
## References
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [Infisical Python SDK](https://infisical.com/docs/sdks/languages/python)
- [Python Security Best Practices](https://python.readthedocs.io/en/latest/library/security_warnings.html)
- Virgo-Core Infisical integration: [../../ansible/tasks/infisical-secret-lookup.yml](../../ansible/tasks/infisical-secret-lookup.yml)