Skip to content

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?

Private authentication — TOTP

See the authentication contract and administrative guide.

  • Activated accounts require username, password and six ASCII digits in one form. Codes rotate every 30 seconds with a ±1-period tolerance; accepted and earlier periods cannot be reused.
  • Keep TOTP_ENCRYPTION_KEY independent of SECRET_KEY, supplied through the environment. Never publish real factors, provisioning keys, codes or ciphertext in browser state, URLs, logs, test traces or reports. The administrative key display is for manual enrollment in a trusted interactive terminal only.
  • Missing or unreadable protection fails closed; never silently restore password-only access. Unactivated accounts have an explicit transition path, not an error fallback.
  • Validate CSRF on login and POST logout, internal return paths, session generation and no-store responses. A confirmed activation or replacement revokes older sessions.
  • Persist five account failures or twenty origin failures over five minutes and a five-minute wait. Success does not clear the current failure window; blocked attempts neither extend the wait nor consume codes.
  • Consume a code atomically before granting a session, including requests from different origins. SQLite checks do not prove MariaDB locking.
  • Enrollment and replacement require server administrative access. Pending preparation expires after ten minutes; the old factor remains active until replacement confirmation. No public recovery endpoint or backup codes are provided.
  • Require HTTPS and secure cookies outside isolated local mode. Trust at most one verified proxy for origin/protocol only; do not trust raw forwarded headers by default.

Required negative checks include expired/replayed/malformed codes, rejected factors, CSRF, external redirects, obsolete sessions, key/database/commit failures and simultaneous requests. Service tests, HTTP tests, administration tests and database tests exercise these contracts. This checklist defines safeguards, not evidence that a deployment or remote verification has occurred.

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.