feat(email-smtp): adds custom headers (#295) - #360
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Email (SMTP) injector module to the OpenAEV injectors monorepo, enabling scenarios to craft and send MIME emails via SMTP (including attachments and validated custom headers). It also updates the Nuclei injector’s contract schema to include predefinedExpectations for expectations fields.
Changes:
- Added a new
email-smtp/injector with contracts, config loading, SMTP delivery implementation, and documentation. - Added tests and Gherkin feature specs for payload normalization, SMTP client behavior, and contract execution flows.
- Updated the Nuclei contract expectations field to include
predefinedExpectations.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| nuclei/nuclei/nuclei_contracts/nuclei_contracts.py | Adds predefinedExpectations to the expectations field definition. |
| email-smtp/tests/email_payload/test_email_payload.py | Pytest coverage for payload normalization (ports, TLS, recipients, headers). |
| email-smtp/tests/email_payload/email_payload.feature | Feature spec for payload building scenarios. |
| email-smtp/tests/email_client/test_email_client.py | Pytest coverage for SMTP send behavior, attachments, and header injection handling. |
| email-smtp/tests/email_client/email_client.feature | Feature spec for SMTP delivery scenarios. |
| email-smtp/tests/craft_email/test_craft_email.py | Pytest coverage for contract routing, attachment download, custom headers, and callbacks. |
| email-smtp/tests/craft_email/craft_email.feature | Feature spec for craft-email execution scenarios. |
| email-smtp/tests/conftest.py | Pytest fixtures (config loading override + injector helper/config mocks). |
| email-smtp/tests/init.py | Marks the tests package. |
| email-smtp/README.md | Injector documentation (setup, configuration, behavior, contract fields). |
| email-smtp/pyproject.toml | Package metadata, dependencies, pytest settings, and dev tooling. |
| email-smtp/manifest-metadata.json | Injector manifest metadata for packaging/registry usage. |
| email-smtp/email_smtp/services/utils.py | Payload builder + custom header parsing/validation utilities. |
| email-smtp/email_smtp/services/email_client.py | SMTP client implementation for crafting/sending MIME messages. |
| email-smtp/email_smtp/services/init.py | Re-exports service layer API. |
| email-smtp/email_smtp/models/exceptions.py | Injector-specific exception types. |
| email-smtp/email_smtp/models/configs/injector_config_override.py | Injector config override model (id/name/type/icon/log level). |
| email-smtp/email_smtp/models/configs/config_loader.py | Config loader wiring OpenAEV + injector configs and contracts. |
| email-smtp/email_smtp/models/configs/init.py | Re-exports config models. |
| email-smtp/email_smtp/models/init.py | Re-exports models/exceptions for external imports. |
| email-smtp/email_smtp/injector/openaev_email_smtp.py | Injector runtime: contract dispatch, attachment extraction, callbacks, logging. |
| email-smtp/email_smtp/injector/init.py | Re-exports injector entrypoint. |
| email-smtp/email_smtp/contracts/email_contracts.py | Contract IDs and contract registration assembly. |
| email-smtp/email_smtp/contracts/craft_email/contract.py | Craft-email contract field definitions (SMTP + message + attachments). |
| email-smtp/email_smtp/contracts/craft_email/init.py | Re-exports craft-email contract builder. |
| email-smtp/email_smtp/contracts/init.py | Re-exports contracts API. |
| email-smtp/email_smtp/main.py | Module entrypoint: load config, load icon, start helper + listener. |
| email-smtp/email_smtp/init.py | Marks the package. |
| email-smtp/Dockerfile | Multi-stage Docker build for the injector (with optional pyoaev override). |
| email-smtp/docker-compose.yml | Compose service definition for running the injector. |
| email-smtp/config.yml.sample | Sample YAML config for manual deployment. |
| email-smtp/.gitignore | Ignores config/env/venv and caches. |
| email-smtp/.env.sample | Sample env var configuration for Docker deployments. |
| email-smtp/.dockerignore | Build context exclusions for smaller Docker builds. |
Comments suppressed due to low confidence (4)
email-smtp/email_smtp/injector/openaev_email_smtp.py:80
- The inline list comprehension is long and not Black-formatted;
black --check .in CI is likely to reformat/fail on this file. Please wrap it over multiple lines to match the repo’s enforced formatting.
documents = data.get("injection", {}).get("inject_documents", [])
attachments = [doc for doc in documents if doc.get("document_attached") is True]
if not attachments:
email-smtp/email_smtp/services/email_client.py:20
- There’s an extra blank line at the start of the class body that violates the repo’s Black formatting (CI runs
black --check .). Removing it will keep formatting consistent.
class EmailClient:
@staticmethod
def send_email(
email-smtp/email_smtp/services/utils.py:86
- This
mail_fromexpression is currently split in a way Black will rewrite (theoris outside the call’s parentheses). Reformat it to a single parenthesized expression to satisfyblack --check ..
"from": content["from"],
"mail_from": EmailPayloadBuilder.parse_optional_email(
content.get("mail_from")
)
or content["from"],
"reply_to": EmailPayloadBuilder.parse_optional_email(
email-smtp/tests/email_client/test_email_client.py:84
- This assertion line is long and will likely be reformatted by Black (CI runs
black --check .). Consider wrapping it to avoid formatting failures.
assert sent_message["Reply-To"] is None
assert instance.send_message.call_args.kwargs["from_addr"] == "from@example.com"
8cb30f8 to
ff55eb9
Compare
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
0358d27 to
0c210ce
Compare
There was a problem hiding this comment.
Great job @mariot !
MVP2 – 10 (Adding custom headers) is functional.
I ran several tests:
✅ Adding a custom header with correct line breaks: no issues. (expected behavior)
The email header:

✅ Adding a single-line custom header (without line breaks): no issues. (incorrect usage)
The email header:

Tip
I think this use case deserves to be documented to facilitate its understanding and use.
✅ Case where a key or value is missing (incomplete header): an error is correctly reported. (expected behavior)
The email header:

Proposed changes
Testing Instructions
Related issues
Checklist
Further comments