update the repo
This commit is contained in:
191
AGENT_SKILL/secrets_and_phi_safety/EXAMPLES.md
Normal file
191
AGENT_SKILL/secrets_and_phi_safety/EXAMPLES.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Examples: secrets_and_phi_safety
|
||||
|
||||
## Correct Examples
|
||||
|
||||
### Secret Loading
|
||||
```python
|
||||
# Good: Loading from environment variables
|
||||
import os
|
||||
|
||||
API_KEY = os.environ.get("EXTERNAL_API_KEY")
|
||||
if not API_KEY:
|
||||
raise ValueError("EXTERNAL_API_KEY environment variable not set")
|
||||
|
||||
DATABASE_URL = os.environ.get("DATABASE_URL")
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY", "default-dev-key-change-in-prod")
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Good: Node.js environment variables
|
||||
const apiKey = process.env.API_KEY;
|
||||
const dbPassword = process.env.DB_PASSWORD;
|
||||
|
||||
if (!apiKey || !dbPassword) {
|
||||
throw new Error('Missing required environment variables');
|
||||
}
|
||||
```
|
||||
|
||||
### PHI Protection in Logs
|
||||
```python
|
||||
# Good: Logging without PHI
|
||||
import logging
|
||||
import hashlib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def process_patient_record(patient_record):
|
||||
# Log only non-identifying information
|
||||
logger.info(
|
||||
f"Processing record for patient age group: {get_age_group(patient_record.age)}"
|
||||
)
|
||||
|
||||
# If logging an ID is absolutely necessary, use a hash
|
||||
patient_hash = hashlib.sha256(
|
||||
patient_record.medical_record_number.encode()
|
||||
).hexdigest()[:8]
|
||||
logger.debug(f"Processing hashed MRN: {patient_hash}...")
|
||||
|
||||
# Never do this:
|
||||
# logger.info(f"Processing patient {patient_record.name} (MRN: {patient_record.mrn})")
|
||||
# logger.error(f"Failed to process {patient_record.id}: {str(e)}") # if id is PHI
|
||||
```
|
||||
|
||||
### Test Data with Synthetic Identifiers
|
||||
```python
|
||||
# Good: Test fixtures use SYNTH-* prefix
|
||||
TEST_PATIENTS = [
|
||||
{
|
||||
"patient_id": "SYNTH-PAT-0001",
|
||||
"name": "John Doe", # Note: In real tests, even names should be fake/synthetic
|
||||
"mrn": "SYNTH-MRN-0001",
|
||||
"date_of_birth": "1980-01-01",
|
||||
# ... other test fields
|
||||
},
|
||||
{
|
||||
"patient_id": "SYNTH-PAT-0002",
|
||||
"name": "Jane Smith",
|
||||
"mrn": "SYNTH-MRN-0002",
|
||||
"date_of_birth": "1992-05-15",
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Configuration Files
|
||||
```yaml
|
||||
# Good: docker-compose.yml using environment variables
|
||||
version: '3.8'
|
||||
services:
|
||||
api:
|
||||
image: medical-api:latest
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
- API_KEY=${API_KEY}
|
||||
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||
secrets:
|
||||
- db_cert # Reference to external secret store, not file in repo
|
||||
|
||||
# Secrets are NOT stored in the repo
|
||||
# Use Docker secrets or Kubernetes secrets or similar external secret management
|
||||
```
|
||||
|
||||
```ini
|
||||
# Good: config.ini with environment variable substitution
|
||||
[database]
|
||||
url = ${DATABASE_URL}
|
||||
# or using os.path.expandvars equivalent
|
||||
```
|
||||
|
||||
### .gitignore Protection
|
||||
```gitignore
|
||||
# .gitignore at repository root
|
||||
# ... other entries
|
||||
secrets/
|
||||
*.env
|
||||
*.pem
|
||||
*.key
|
||||
```
|
||||
|
||||
### Safe Error Messages
|
||||
```python
|
||||
# Good: Generic error messages that don't leak PHI
|
||||
try:
|
||||
process_claim(claim_data)
|
||||
except ValidationError as e:
|
||||
# Log detailed error internally (without PHI)
|
||||
logger.warning(f"Claim validation failed: {e}")
|
||||
# Return generic message to user
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Claim validation failed. Please check the submitted information."
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error processing claim: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Internal server error. Please contact support."
|
||||
)
|
||||
```
|
||||
|
||||
## Incorrect Examples
|
||||
|
||||
### Secret Exposure
|
||||
```python
|
||||
# Bad: Hard-coded secret
|
||||
API_KEY = "sk_live_1234567890abcdef" # VIOLATION: exposed in code
|
||||
|
||||
# Bad: Secret in config file committed to repo
|
||||
# config.yaml
|
||||
# api_key: "actual_secret_key_here" # VIOLATION
|
||||
|
||||
# Bad: Using .env file that gets committed
|
||||
# .env (committed to git)
|
||||
# DATABASE_URL=postgres://user:password@localhost/db
|
||||
```
|
||||
|
||||
### PHI in Logs and Errors
|
||||
```python
|
||||
# Bad: Logging full patient identifier
|
||||
logger.error(f"Failed to update patient {patient.patient_id}: {str(e)}")
|
||||
# If patient_id is MRN or similar, this leaks PHI
|
||||
|
||||
# Bad: Error message with PHI
|
||||
raise ValueError(f"Invalid DOB for patient {patient.name}: {patient.dob}")
|
||||
|
||||
# Bad: Including PHI in HTTP response
|
||||
return {
|
||||
"error": f"Patient {patient.mrn} not found", # MRN is PHI
|
||||
"code": "PATIENT_NOT_FOUND"
|
||||
}
|
||||
```
|
||||
|
||||
### Real Data in Tests
|
||||
```python
|
||||
# Bad: Test using real patient-like data
|
||||
TEST_PATIENT = {
|
||||
"patient_id": "12345678", # Looks like real MRN
|
||||
"name": "John Doe", # Realistic name
|
||||
"ssn": "123-45-6789", # Actual SSN format
|
||||
"dob": "1980-01-01",
|
||||
}
|
||||
```
|
||||
|
||||
### Missing .gitignore Protection
|
||||
```gitignore
|
||||
# .gitmissing: secrets/ directory NOT ignored
|
||||
# This would allow secrets to be committed
|
||||
# (no entry for secrets/ here)
|
||||
```
|
||||
|
||||
### Insecure Configuration
|
||||
```yaml
|
||||
# Bad: Hard-coded secrets in config
|
||||
database:
|
||||
host: localhost
|
||||
port: 5432
|
||||
username: admin
|
||||
password: "super_secret_password" # VIOLATION
|
||||
|
||||
tls:
|
||||
cert_file: "/etc/ssl/certs/real-cert.pem" # Hard-coded path, should be env var
|
||||
key_file: "/etc/ssl/private/real-key.pem"
|
||||
```
|
||||
Reference in New Issue
Block a user