Connect through the destination guard, keep server errors out of Test, and fix CI, pins and rules beyond en/fr/pt #1

Closed
opened 2026-09-15 21:23:35 +00:00 by tiagoagueda · 0 comments
Owner

Found in the 2026-09-15 audit of the plugin repositories.

1. The IMAP connection dials any host and port (SSRF, port scanning)

  • src/postulo_imap/mailbox.py:194-207 calls IMAPClient(host, port=port, …) with the raw typed host and port. No destination check runs anywhere in the plugin.
  • sync.py:133-137: Test returns f"{type(error).__name__}: {error}" to the person.
  • Any account can point Host and Port at 127.0.0.1:6379, a Compose service name or a cloud metadata address, and read reachability and banners through Test.
  • POSTULO_CONNECTIONS_ALLOW_PRIVATE is ignored.

Fix: resolve and approve the host under the instance's policy and connect to the approved address, keeping SNI and certificate checks on the typed name. The core already does this for SMTP (core/destinations.py: approve, private_allowed) and should expose it on the plugin surface; that is tracked in the core's outbound-requests issue (postulo/postulo#215). Until then, refuse anything that is not a public address unless private destinations are allowed. Make Test errors generic ("could not connect", "sign-in refused").

2. CI cannot pass

.forgejo/workflows/ci.yml:39,41 runs uv run python scripts/compile_messages.py, and the repository has no scripts/ directory.

Fix: use the postulo-messages compile command the tests already name. Add a tag-triggered job that runs uv run pytest -m release.

3. The version pin is a label

  • pyproject.toml says 0.3.0, but src/postulo_imap/__init__.py has __version__ = "0.1.0", which is what Postulo displays.
  • The dev dependency is the core by git URL with no ref, and uv.lock pins a main commit that is not on the 0.3.0 branch.
  • The catalogue tests importorskip silently against that branch.

Fix: derive __version__ from importlib.metadata, pin the core to v0.3.0 (or the release branch), and make the skip a hard failure under -m release.

4. Imports past postulo.plugins.api

sync.py:20-22 and matching.py:24-25 import Application, Suggestion, suggest and Contact from core internals. Move to the surface once the core exposes them (core plugin-surface issue (postulo/postulo#229)), and add the surface AST check to this repo's CI.

5. Detection rules only for en, fr and pt

src/postulo_imap/rules/ has rule lists for English, French and Portuguese only, so rejection and interview detection fails silently for German, Spanish, Italian, Dutch, Polish and the rest of the EU scope.

Fix: add de, es, it, nl and pl first, and a test that fails when a language with a translated catalogue has no rules file (or document the fallback).

Found in the 2026-09-15 audit of the plugin repositories. ## 1. The IMAP connection dials any host and port (SSRF, port scanning) - `src/postulo_imap/mailbox.py:194-207` calls `IMAPClient(host, port=port, …)` with the raw typed host and port. No destination check runs anywhere in the plugin. - `sync.py:133-137`: *Test* returns `f"{type(error).__name__}: {error}"` to the person. - Any account can point Host and Port at `127.0.0.1:6379`, a Compose service name or a cloud metadata address, and read reachability and banners through *Test*. - `POSTULO_CONNECTIONS_ALLOW_PRIVATE` is ignored. **Fix:** resolve and approve the host under the instance's policy and connect to the approved address, keeping SNI and certificate checks on the typed name. The core already does this for SMTP (`core/destinations.py`: `approve`, `private_allowed`) and should expose it on the plugin surface; that is tracked in the core's outbound-requests issue (postulo/postulo#215). Until then, refuse anything that is not a public address unless private destinations are allowed. Make *Test* errors generic ("could not connect", "sign-in refused"). ## 2. CI cannot pass `.forgejo/workflows/ci.yml:39,41` runs `uv run python scripts/compile_messages.py`, and the repository has no `scripts/` directory. **Fix:** use the `postulo-messages` compile command the tests already name. Add a tag-triggered job that runs `uv run pytest -m release`. ## 3. The version pin is a label - `pyproject.toml` says `0.3.0`, but `src/postulo_imap/__init__.py` has `__version__ = "0.1.0"`, which is what Postulo displays. - The dev dependency is the core by git URL with no ref, and `uv.lock` pins a `main` commit that is not on the `0.3.0` branch. - The catalogue tests `importorskip` silently against that branch. **Fix:** derive `__version__` from `importlib.metadata`, pin the core to `v0.3.0` (or the release branch), and make the skip a hard failure under `-m release`. ## 4. Imports past `postulo.plugins.api` `sync.py:20-22` and `matching.py:24-25` import `Application`, `Suggestion`, `suggest` and `Contact` from core internals. Move to the surface once the core exposes them (core plugin-surface issue (postulo/postulo#229)), and add the surface AST check to this repo's CI. ## 5. Detection rules only for en, fr and pt `src/postulo_imap/rules/` has rule lists for English, French and Portuguese only, so rejection and interview detection fails silently for German, Spanish, Italian, Dutch, Polish and the rest of the EU scope. **Fix:** add `de`, `es`, `it`, `nl` and `pl` first, and a test that fails when a language with a translated catalogue has no rules file (or document the fallback).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo-imap#1
No description provided.