Security Checklist
Context
The project handles API credentials (Marketstack), SMTP credentials, database passwords, and GitHub tokens. Constitution Principle V is non-negotiable: "Credentials, tokens, and environment secrets MUST NOT be hardcoded in source files, tests, or committed artifacts."
This checklist is applied to every new integration, every new configuration addition, and every code review involving credentials or external connections.
Details
Pre-commit checklist (every change involving credentials or external services)
BEFORE committing:
[ ] No API keys, passwords, or tokens in any .py, .yml, .json, .md, or .ini file
[ ] Sensitive values are loaded from environment variables only
✓ os.environ.get("API_KEY") or os.getenv("API_KEY")
✗ api_key = "abc123"
[ ] .env files are listed in .gitignore (verify with: git check-ignore -v .env)
[ ] Test files use mock credentials (e.g., "test-key", "mock-token"), never real ones
[ ] config.ini contains no secrets — only structure keys with empty/placeholder values
Environment variable pattern
function get_required_secret(key_name):
value = os.environ.get(key_name)
if value is None or value.strip() == "":
raise EnvironmentError(
f"Required secret '{key_name}' not set. "
f"Set it via environment variable before running."
)
return value
# Usage:
api_key = get_required_secret("MARKETSTACK_API_KEY")
smtp_password = get_required_secret("SMTP_PASSWORD")
Fail-fast at startup is preferred over runtime surprises when credentials are missing.
Negative test requirements
For every integration with an external service, at least one negative test MUST exist:
def test_missing_api_key_raises_explicit_error():
# Temporarily unset the key
with mock.patch.dict(os.environ, {}, clear=True):
os.environ.pop("MARKETSTACK_API_KEY", None)
with pytest.raises(EnvironmentError, match="MARKETSTACK_API_KEY"):
initialize_api_client()
This ensures credential errors are caught with a clear message, not a cryptic AttributeError or None dereference.
OWASP Top 10 checklist (for new endpoints or data flows)
[ ] A01 Broken Access Control — is authentication required? is it enforced?
[ ] A02 Cryptographic Failures — are secrets stored in plaintext anywhere?
[ ] A03 Injection — is user input ever passed directly to SQL, shell, or eval?
[ ] A05 Security Misconfiguration — are default passwords changed? debug mode off?
[ ] A07 Auth/Session Failures — are sessions invalidated on logout?
[ ] A09 Security Logging — are auth failures and sensitive actions logged?
Links
- Real implementation: code_source_simule/manage_users.py
- AI scope rules (secrets exclusion): .github/agents/copilot-instructions.md
- Test example: tests/test_copilot_scope_rule.py
Examples
New API integration: Before writing any code, confirm the API key loading uses get_required_secret(), add the key name to .env.example (with placeholder value), add it to CI secrets, and write the negative test above before the feature test.