|
| 1 | +# CLAUDE.md — MailThunder |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +MailThunder (`je_mail_thunder`) is a Python email automation library wrapping SMTP and IMAP4 protocols. It provides JSON-based scripting, project templates, and a socket server for remote execution. |
| 6 | + |
| 7 | +- **Language**: Python 3.9+ |
| 8 | +- **Package**: `je_mail_thunder` (PyPI: `je-mail-thunder`) |
| 9 | +- **License**: MIT |
| 10 | +- **Entry point**: `je_mail_thunder/__main__.py` |
| 11 | + |
| 12 | +## Build & Test |
| 13 | + |
| 14 | +```bash |
| 15 | +pip install -e . # Install in dev mode |
| 16 | +pip install -r dev_requirements.txt |
| 17 | +pytest # Run tests (testpaths = test/) |
| 18 | +``` |
| 19 | + |
| 20 | +## Architecture |
| 21 | + |
| 22 | +``` |
| 23 | +je_mail_thunder/ |
| 24 | + smtp/smtp_wrapper.py # SMTPWrapper (extends SMTP_SSL) |
| 25 | + imap/imap_wrapper.py # IMAPWrapper (extends IMAP4_SSL) |
| 26 | + utils/ |
| 27 | + executor/ # Command pattern — JSON action executor |
| 28 | + socket_server/ # TCP socket server for remote command execution |
| 29 | + save_mail_user_content/ # Credential storage (JSON file / env vars) |
| 30 | + project/template/ # Template method pattern for project scaffolding |
| 31 | + package_manager/ # Dynamic package loading |
| 32 | + json/ # JSON file I/O |
| 33 | + json_format/ # JSON processing |
| 34 | + file_process/ # Directory file listing |
| 35 | + logging/ # Centralized logger instance |
| 36 | + exception/ # Custom exception hierarchy |
| 37 | +``` |
| 38 | + |
| 39 | +## Design Patterns & Software Engineering Principles |
| 40 | + |
| 41 | +### Required Patterns |
| 42 | + |
| 43 | +- **Wrapper / Adapter Pattern**: `SMTPWrapper` and `IMAPWrapper` extend stdlib classes to add logging, auto-login, and context manager support. New protocol wrappers must follow this pattern. |
| 44 | +- **Command Pattern**: The `Executor` class maps string command names to callable actions. All new executable features must register through `event_dict`. |
| 45 | +- **Template Method Pattern**: Project scaffolding uses `template_executor.py` / `template_keyword.py`. Extend templates by adding keyword handlers, not by modifying the base flow. |
| 46 | +- **Singleton-like Module Instances**: `smtp_instance`, `imap_instance`, `executor`, `package_manager` are module-level singletons. Do not create duplicate global instances. |
| 47 | +- **Context Manager Protocol**: All wrappers implement `__enter__` / `__exit__`. New resource-holding classes must do the same. |
| 48 | + |
| 49 | +### Engineering Principles |
| 50 | + |
| 51 | +- **Single Responsibility**: Each module under `utils/` handles one concern. Do not merge unrelated logic into a single module. |
| 52 | +- **Open/Closed**: Extend behavior by adding new commands to `Executor.event_dict` or new template keywords — not by modifying existing method signatures. |
| 53 | +- **DRY**: The login logic (`try_to_login_with_env_or_content`) is shared across SMTP/IMAP. If adding new auth sources, update the shared credential flow in `save_mail_user_content/`. |
| 54 | +- **Fail Fast with Logging**: All public methods catch exceptions, log via `mail_thunder_logger`, and avoid silent failures. Follow this pattern for any new code. |
| 55 | + |
| 56 | +## Performance Guidelines |
| 57 | + |
| 58 | +- **Lazy Initialization**: `smtp_instance` and `imap_instance` are created at import time with try/except fallback to `None`. Use `later_init()` for deferred login — do not block module import with network calls. |
| 59 | +- **Avoid Redundant I/O**: When processing multiple emails, prefer batch operations. Do not open/close connections per email. |
| 60 | +- **Minimize Memory Allocation**: Use generators or iterators for large mailbox operations instead of building full lists in memory. |
| 61 | +- **Connection Reuse**: Reuse `SMTPWrapper` / `IMAPWrapper` instances within a session. Do not create new connections for each send/receive operation. |
| 62 | +- **File I/O**: Use context managers (`with` statements) for all file operations to ensure prompt resource release. |
| 63 | + |
| 64 | +## Dead Code Policy |
| 65 | + |
| 66 | +- **Remove unused imports, variables, functions, and classes** before committing. Do not leave commented-out code blocks. |
| 67 | +- **No placeholder or stub code** unless explicitly required for an interface contract. |
| 68 | +- **No backwards-compatibility shims** — if something is unused, delete it completely. |
| 69 | +- Run a linter check before committing to catch unreferenced symbols. |
| 70 | + |
| 71 | +## Security Requirements (Mandatory) |
| 72 | + |
| 73 | +### Credential Handling |
| 74 | +- **Never hardcode credentials** in source code. Credentials must come from `mail_thunder_content.json` (local, gitignored) or environment variables only. |
| 75 | +- **Never log credentials**. Sanitize all log messages — ensure `user`, `password`, and token values are never written to `mail_thunder_logger` or stdout. |
| 76 | +- **Never commit** `.env` files, `mail_thunder_content.json`, or any file containing secrets. |
| 77 | + |
| 78 | +### Input Validation |
| 79 | +- **Validate all external input** at system boundaries: JSON action files, socket server commands, CLI arguments, email headers. |
| 80 | +- **Sanitize file paths** — use `os.path.basename()` and reject path traversal patterns (`..`, absolute paths) in user-supplied filenames, especially in `output_all_mail_as_file` and attachment handling. |
| 81 | +- **Limit socket recv buffer** and validate JSON payloads before execution to prevent injection or denial-of-service. |
| 82 | + |
| 83 | +### Command Execution Safety |
| 84 | +- The `Executor` registers all Python builtins into `event_dict`. Be aware that this allows arbitrary builtin calls via JSON commands. Any new command registration via `add_command_to_executor` must validate that only `types.MethodType` or `types.FunctionType` are accepted (already enforced). |
| 85 | +- **Never use `eval()` or `exec()`** on untrusted input. |
| 86 | +- **Never use `subprocess.shell=True`** with user-provided strings. |
| 87 | + |
| 88 | +### Network Security |
| 89 | +- SMTP uses `SMTP_SSL` (port 465) — always use SSL/TLS. Do not downgrade to plain SMTP. |
| 90 | +- IMAP uses `IMAP4_SSL` — always use SSL/TLS. Do not downgrade to plain IMAP. |
| 91 | +- Socket server binds to `localhost` by default. Do not change the default bind address to `0.0.0.0` without explicit user configuration. |
| 92 | + |
| 93 | +### Dependency Security |
| 94 | +- Keep dependencies minimal (`requirements.txt` is intentionally small). |
| 95 | +- Audit new dependencies before adding. Prefer stdlib solutions. |
| 96 | + |
| 97 | +## Commit Convention |
| 98 | + |
| 99 | +- Write concise commit messages that describe the "why", not just the "what". |
| 100 | +- **Do not mention any AI assistant, model name, or tool name** (including but not limited to Claude, GPT, Copilot, etc.) in commit messages, PR descriptions, or code comments. |
| 101 | +- **Do not include `Co-Authored-By` headers referencing AI tools.** |
| 102 | +- Format: `<type>: <description>` (e.g., `fix: prevent path traversal in mail export`, `feat: add OAuth2 support for IMAP login`). |
| 103 | +- Types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `perf`, `security`. |
| 104 | + |
| 105 | +## Code Style |
| 106 | + |
| 107 | +- Follow existing project conventions — no type annotations on code you didn't write unless fixing a bug there. |
| 108 | +- Use `mail_thunder_logger` for all logging. No `print()` in library code (only in CLI/socket server output). |
| 109 | +- Exception hierarchy rooted at `MailThunderException`. New exceptions must subclass it. |
| 110 | +- All public methods need docstrings following the existing `:param` / `:return:` style. |
| 111 | + |
| 112 | +## Linter Compliance (SonarQube / Codacy / Pylint / Flake8) |
| 113 | + |
| 114 | +All code must pass static analysis from SonarQube, Codacy, Pylint, and Flake8. The rules below encode the most common quality-gate failures for this codebase — follow them proactively rather than waiting for a linter report. |
| 115 | + |
| 116 | +### Complexity & Size Limits |
| 117 | +- **Cognitive Complexity ≤ 15** per function (SonarQube `python:S3776`). Refactor deeply nested conditionals into early-returns or helper functions. |
| 118 | +- **Cyclomatic Complexity ≤ 10** per function (Pylint `R0912`). Split branchy logic. |
| 119 | +- **Function length ≤ 50 lines**, **class length ≤ 300 lines**, **module length ≤ 750 lines** (SonarQube defaults). Decompose longer units. |
| 120 | +- **Parameters ≤ 7** per function (Pylint `R0913`). Group related arguments into dataclasses or dicts. |
| 121 | +- **Max line length: 120 characters** (Flake8 `E501`, configured project-wide). |
| 122 | +- **Max nesting depth ≤ 4** (SonarQube `python:S134`). |
| 123 | + |
| 124 | +### Naming (PEP 8 / Pylint `C0103`) |
| 125 | +- `snake_case` for functions, methods, variables, modules; `PascalCase` for classes; `UPPER_SNAKE_CASE` for module-level constants. |
| 126 | +- No single-letter names except loop counters (`i`, `j`, `k`) or well-known math conventions. |
| 127 | +- Avoid shadowing builtins (`list`, `dict`, `id`, `type`, `input`, `file`) — SonarQube `python:S5806`. |
| 128 | + |
| 129 | +### Exception Handling (SonarQube / Bandit) |
| 130 | +- **Never use bare `except:`** — always catch specific exceptions (SonarQube `python:S5754`, Bandit `B110`). |
| 131 | +- **Do not swallow exceptions silently**. Log via `mail_thunder_logger.error(...)` and re-raise or convert to a `MailThunderException` subclass. |
| 132 | +- **Do not use `except Exception as e: pass`** — Codacy `PyLint-W0702/W0703`. |
| 133 | +- Chain exceptions with `raise NewError(...) from original_error` to preserve traceback (SonarQube `python:S5708`). |
| 134 | + |
| 135 | +### Duplication & Dead Code |
| 136 | +- **No duplicated blocks ≥ 3 lines** (SonarQube `python:S4144` / `common-py:DuplicatedBlocks`). Extract shared logic into helpers. |
| 137 | +- **No unused imports / variables / parameters / private functions** (Pylint `W0611`, `W0612`, `W0613`, `W0238`). |
| 138 | +- **No unreachable code** after `return` / `raise` / `break` (SonarQube `python:S1763`). |
| 139 | +- **No commented-out code** (SonarQube `python:S125`). |
| 140 | +- **No `TODO` / `FIXME` without an issue reference** (SonarQube `python:S1135`). Either fix it or file a ticket and reference it. |
| 141 | + |
| 142 | +### Comparison & Logic Correctness |
| 143 | +- Use `is None` / `is not None` rather than `== None` (Pylint `C0121`, SonarQube `python:S5727`). |
| 144 | +- Use `isinstance(x, T)` instead of `type(x) == T` (Pylint `C0123`). |
| 145 | +- Do not compare boolean literals with `==` (`if flag:` not `if flag == True:`) — SonarQube `python:S1125`. |
| 146 | +- No constant conditions in `if` / `while` (SonarQube `python:S1145`). |
| 147 | +- No identical expressions on both sides of binary operators (SonarQube `python:S1764`). |
| 148 | + |
| 149 | +### Mutable Defaults & Side Effects |
| 150 | +- **Never use mutable default arguments** (`def f(x=[])`) — Pylint `W0102`, SonarQube `python:S5717`. Use `None` and initialize inside the function. |
| 151 | +- No side effects at import time beyond logger setup and module-level singleton construction that already exists in this project. |
| 152 | + |
| 153 | +### Security Hotspots (Bandit / SonarQube) |
| 154 | +- **No hardcoded credentials / tokens / IPs** (Bandit `B105`-`B107`, SonarQube `python:S2068`). |
| 155 | +- **No `assert` for runtime validation** — asserts are stripped in optimized mode (Bandit `B101`). |
| 156 | +- **No `pickle` / `marshal` / `shelve` on untrusted data** (Bandit `B301`). |
| 157 | +- **No `yaml.load` without `SafeLoader`** (Bandit `B506`). |
| 158 | +- **No weak hashing** (`md5`, `sha1`) for security purposes (Bandit `B303`, `B324`). |
| 159 | +- **No `random` module for security tokens** — use `secrets` (Bandit `B311`). |
| 160 | +- **No `tempfile.mktemp`** — use `NamedTemporaryFile` (Bandit `B306`). |
| 161 | +- **No binding to `0.0.0.0`** without explicit user opt-in (Bandit `B104`). |
| 162 | +- **No SSL context disabling cert verification** (Bandit `B501`). |
| 163 | +- **No XML parsing with `xml.etree` / `xml.sax` / `minidom`** on untrusted input — use `defusedxml` (Bandit `B314`-`B320`). |
| 164 | + |
| 165 | +### Imports & Structure |
| 166 | +- No wildcard imports (`from x import *`) outside `__init__.py` re-export (Pylint `W0401`). |
| 167 | +- No relative imports beyond one level (`from ..x`). Prefer absolute (`from je_mail_thunder.x`). |
| 168 | +- Imports ordered: stdlib, third-party, local — separated by blank lines (Flake8 `isort`). |
| 169 | +- No circular imports (Pylint `R0401`). |
| 170 | + |
| 171 | +### Formatting |
| 172 | +- 4-space indentation, no tabs (Flake8 `W191`). |
| 173 | +- Two blank lines between top-level defs, one blank line between methods (PEP 8 / Flake8 `E302`/`E303`). |
| 174 | +- No trailing whitespace (Flake8 `W291`), files end with a single newline (Flake8 `W292`). |
| 175 | +- No multiple statements on one line (Flake8 `E701`/`E702`). |
| 176 | + |
| 177 | +### Documentation |
| 178 | +- Every public module, class, and function has a docstring (Pylint `C0111` / `missing-docstring`). Use `:param` / `:return:` / `:raises:` style already in use. |
| 179 | +- No misleading docstrings — update them when behavior changes. |
| 180 | + |
| 181 | +### Enforcement Workflow |
| 182 | +- Before committing: run `pip install pylint flake8 bandit` and locally execute `pylint je_mail_thunder`, `flake8 je_mail_thunder`, `bandit -r je_mail_thunder`. |
| 183 | +- Treat any new SonarQube / Codacy finding on changed lines as a blocker. Do not suppress rules (`# noqa`, `# pylint: disable=`) without a comment explaining why and which specific rule is being suppressed. |
0 commit comments