Skip to content

Allow Go templates in campaign From header - #3056

Open
nemunaire wants to merge 2 commits into
knadh:masterfrom
nemunaire:f/template_in_form
Open

Allow Go templates in campaign From header#3056
nemunaire wants to merge 2 commits into
knadh:masterfrom
nemunaire:f/template_in_form

Conversation

@nemunaire

Copy link
Copy Markdown

Some concurrent softwares (like ActiveCampaign) allows template in the From header.

This pull request adds support for this!

Mirror the Subject templating path so the campaign From header can be
personalized per subscriber (e.g. `{{ .Subscriber.FirstName }}
<news@example.com>`). Compile FromEmail when it contains template
syntax, execute it per message before send, and relax the From input
validation to allow template syntax (compile errors still surface).
@nemunaire
nemunaire force-pushed the f/template_in_form branch from 1fe0545 to 5c33a0d Compare May 19, 2026 05:05
@knadh knadh added the hodor-review Automated AI code review label May 27, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues Found

No issues found.

Summary

Total issues: 0 critical, 0 important, 0 minor.

Overall Verdict

Status: Patch is correct

Explanation: The diff cleanly adds compilation and per-subscriber rendering for templated campaign From headers, and updates validation to render against a dummy subscriber before address checks. No discrete production-breaking issues were identified in the introduced changes.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 26 turns, 27 tool calls, 4m 13s

  • Tokens: in 18.0K | cached 306.2K | out 13.8K (total 337.9K)
  • Cost: $0.2775

@knadh
knadh force-pushed the f/template_in_form branch 2 times, most recently from 2aa246d to e1dfc2a Compare June 19, 2026 12:12

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues Found

Critical (P0/P1)

  • [P1] Validate/sanitize rendered templated From header before sending (internal/manager/message.go:45-52)
    • FromEmail templates bypass the existing static email validation and the rendered value is assigned directly to msg.from; if the template expands to an invalid address or contains CR/LF (eg. subscriber-provided name like "Eve\r\nBcc: victim@x.com"), downstream SMTP/message building may fail or allow header injection because the only mail.ParseAddress usage is for Message-Id generation and its error is ignored. Consider rejecting rendered From values that contain \r/\n and/or require mail.ParseAddress to succeed (per-message at render time) to prevent malformed/injectable headers from being emitted.

Summary

Total issues: 1 critical, 0 important, 0 minor.

Overall Verdict

Status: Patch has blocking issues

Explanation: The feature works functionally, but it introduces a new path where unvalidated, subscriber-influenced template output becomes the SMTP From header. Without per-message validation/sanitization, malformed output can break deliveries and can enable CRLF header injection in some SMTP backends.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 18 turns, 20 tool calls, 2m 22s

  • Tokens: in 17.6K | cached 206.3K | out 7.8K (total 231.8K)
  • Cost: $0.1764

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues Found

Critical (P0/P1)

  • [P1] Validate rendered templated From header before sending (internal/manager/message.go:45-52)
    • CampaignMessage.render() copies the rendered template output directly into m.from and it later becomes models.Message.From for SMTP sending; if the template (or subscriber data used inside it) produces an invalid RFC5322 address or contains \r/\n, some SMTP backends will hard-fail the send and others may treat it as header-injection input. Add a post-render validation step (eg mail.ParseAddress + explicit CR/LF/control-char rejection) and return a clear error before queueing the message.
  • [P1] Don't skip all FromEmail validation when template syntax is present (cmd/campaigns.go:663-670)
    • validateCampaignFields() now bypasses the From address regex/sanitization as soon as {{...}} is detected, which permits storing obviously unsafe values (notably ones containing CR/LF) that can later become email headers. Even if full address validation can't be done pre-render, it should still reject newline/control characters (and ideally enforce a reasonable max length) for templated From strings to avoid persistent bad campaign configs.

Summary

Total issues: 2 critical, 0 important, 0 minor.

Overall Verdict

Status: Patch has blocking issues

Explanation: The feature works functionally, but it introduces a new path where unvalidated, potentially invalid/unsafe data can become the outgoing email From header. This can cause production send failures for certain subscriber data and may enable header-injection style inputs unless the rendered output is validated/sanitized.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 35 turns, 36 tool calls, 3m 31s

  • Tokens: in 22.5K | cached 433.2K | out 10.6K (total 466.3K)
  • Cost: $0.2633

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues Found

Critical (P0/P1)

  • [P1] Reject CR/LF in templated campaign FromEmail during validation (cmd/campaigns.go:664-669)
    • validateCampaignFields() skips all address sanitization when FromEmail contains {{...}}, so a campaign can be saved with a multi-line FromEmail (eg "foo\r\nBcc:..." wrapped in a template) which then becomes a raw SMTP header value at send time; this is both a send-breaker and a potential header-injection vector, so templated values should still be rejected if they contain CR/LF and ideally be validated after render.
  • [P1] Validate rendered From header before putting it on outbound messages (internal/manager/message.go:42-54)
    • When FromEmailTpl is executed, the rendered string is assigned to m.from and later copied into models.Message.From without any normalization or validation; if the template expands to an invalid address or includes CR/LF via subscriber/campaign fields, SMTP delivery may fail or extra headers may be injected. Consider trimming and rejecting any output containing \r/\n, and/or requiring mail.ParseAddress to succeed before enqueueing the message.

Summary

Total issues: 2 critical, 0 important, 0 minor.

Overall Verdict

Status: Patch has blocking issues

Explanation: The feature works functionally, but templated From values bypass existing validation and the rendered output is used as a header verbatim, which can lead to invalid outbound messages and enable header injection when templates expand to multi-line values.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 21 turns, 24 tool calls, 2m 48s

  • Tokens: in 15.6K | cached 237.3K | out 8.5K (total 261.3K)
  • Cost: $0.1872

@knadh
knadh force-pushed the f/template_in_form branch from e1dfc2a to 4d8c246 Compare June 19, 2026 14:42
@knadh knadh removed the hodor-review Automated AI code review label Jun 19, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues Found

Critical (P0/P1)

  • [P1] Validate/sanitize rendered templated From header to prevent header injection (internal/manager/message.go:42-52)
    • FromEmail can now be templated and is rendered directly into msg.from without validating the final value or stripping CR/LF. If the template output contains newlines (eg. {{.Subscriber.Name}} where a subscriber name includes \r\n...), the resulting raw From string may break MIME formatting or enable header injection depending on the SMTP backend; even without newlines, invalid rendered addresses can cause send failures/bounces. Consider validating the rendered string with mail.ParseAddress (or existing From regex) and explicitly rejecting/stripping \r/\n before constructing the outbound message.

Important (P2)

  • [P2] Avoid skipping FromEmail validation solely due to presence of template braces (cmd/campaigns.go:663-668)
    • validateCampaignFields() now bypasses From address validation whenever the string contains {{...}}, which makes it easy to persist obviously-invalid From values (or values containing CR/LF) and only discover problems at send time. If templating must be allowed, it is still useful to do basic structural validation (eg. reject \r/\n, and/or validate the rendered preview in UI/API) rather than treating any {{...}} as valid.

Summary

Total issues: 1 critical, 1 important, 0 minor.

Overall Verdict

Status: Patch has blocking issues

Explanation: The feature works conceptually, but templated From values are accepted and emitted without validating or sanitizing the rendered header, which can lead to malformed emails and potential header-injection scenarios. Tightening validation on the rendered From string (and/or applying basic CR/LF checks at save time) is needed before this is safe for production.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 31 turns, 34 tool calls, 3m 20s

  • Tokens: in 22.7K | cached 436.1K | out 10.7K (total 469.5K)
  • Cost: $0.2656

@knadh

knadh commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Hi @nemunaire. I refactored and simplified the logic and added it to the central template compilation, rather than it being a separate if condition in the messenger. This is cleaner and consistent with all other template compilation logic in general (altbody, headers etc.). Please take a look (I've tested it, but please do at your end as well) and let me know.

@knadh knadh self-assigned this Jun 21, 2026
@nemunaire

Copy link
Copy Markdown
Author

Hi @knadh. Tested on top of v0.6.2. It also works for me as expected. 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants