Merge branch 'develop' into TM-316

This commit is contained in:
Nima Nakhsotin
2025-12-08 10:16:23 +03:30
10 changed files with 372 additions and 29 deletions
+17 -7
View File
@@ -2,8 +2,11 @@ package inquiry
import (
"fmt"
"html"
"strings"
"time"
"tm/pkg/security"
)
// EmailTemplateData represents the data needed for the email template
@@ -20,7 +23,14 @@ type EmailTemplateData struct {
}
// GenerateNewInquiryEmailTemplate generates a beautiful HTML email template for new inquiry notifications
func GenerateNewInquiryEmailTemplate(data *EmailTemplateData) string {
func GenerateNewInquiryEmailTemplate(data *EmailTemplateData, xssPolicy *security.XSSPolicy) string {
// Sanitize all user inputs before inserting into HTML
safeFullName := html.EscapeString(data.FullName)
safeCompanyName := html.EscapeString(data.CompanyName)
safeWorkEmail := html.EscapeString(data.WorkEmail)
safePhoneNumber := html.EscapeString(data.PhoneNumber)
safeInquiryID := html.EscapeString(data.InquiryID)
// Format the creation date
createdAt := time.Unix(data.CreatedAt, 0).Format("January 2, 2006 at 3:04 PM")
@@ -249,12 +259,12 @@ func GenerateNewInquiryEmailTemplate(data *EmailTemplateData) string {
</body>
</html>`
// Replace placeholders with actual data
html = strings.ReplaceAll(html, "{{COMPANY_NAME}}", data.CompanyName)
html = strings.ReplaceAll(html, "{{INQUIRY_ID}}", data.InquiryID)
html = strings.ReplaceAll(html, "{{FULL_NAME}}", data.FullName)
html = strings.ReplaceAll(html, "{{WORK_EMAIL}}", data.WorkEmail)
html = strings.ReplaceAll(html, "{{PHONE_NUMBER}}", data.PhoneNumber)
// Replace placeholders with sanitized data
html = strings.ReplaceAll(html, "{{COMPANY_NAME}}", safeCompanyName)
html = strings.ReplaceAll(html, "{{INQUIRY_ID}}", safeInquiryID)
html = strings.ReplaceAll(html, "{{FULL_NAME}}", safeFullName)
html = strings.ReplaceAll(html, "{{WORK_EMAIL}}", safeWorkEmail)
html = strings.ReplaceAll(html, "{{PHONE_NUMBER}}", safePhoneNumber)
html = strings.ReplaceAll(html, "{{CREATED_AT}}", createdAt)
return html
+75 -1
View File
@@ -1,6 +1,11 @@
package inquiry
import "tm/pkg/response"
import (
"tm/pkg/response"
"tm/pkg/security"
"github.com/asaskevich/govalidator"
)
// Inquiry DTOs
@@ -83,3 +88,72 @@ func (i *Inquiry) ToResponse() *InquiryResponse {
UpdatedAt: i.UpdatedAt,
}
}
// ValidateAndSanitize validates and sanitizes the form
func (f *CreateInquiryForm) ValidateAndSanitize(xssPolicy *security.XSSPolicy) error {
var err error
// Sanitize and validate FullName
f.FullName, err = xssPolicy.ValidateAndSanitizeInput(f.FullName, "text")
if err != nil {
return err
}
// Sanitize and validate CompanyName
f.CompanyName, err = xssPolicy.ValidateAndSanitizeInput(f.CompanyName, "text")
if err != nil {
return err
}
// Sanitize and validate WorkEmail (email validation is separate)
f.WorkEmail, err = xssPolicy.ValidateAndSanitizeInput(f.WorkEmail, "email")
if err != nil {
return err
}
// Sanitize and validate PhoneNumber
f.PhoneNumber, err = xssPolicy.ValidateAndSanitizeInput(f.PhoneNumber, "phone")
if err != nil {
return err
}
// Standard govalidator validation
_, err = govalidator.ValidateStruct(f)
return err
}
// ValidateAndSanitize validates and sanitizes the status update form
func (f *UpdateInquiryStatusForm) ValidateAndSanitize(xssPolicy *security.XSSPolicy) error {
var err error
// Sanitize Reason
f.Reason, err = xssPolicy.ValidateAndSanitizeInput(f.Reason, "text")
if err != nil {
return err
}
// Sanitize Description
f.Description, err = xssPolicy.ValidateAndSanitizeInput(f.Description, "html")
if err != nil {
return err
}
// Standard govalidator validation
_, err = govalidator.ValidateStruct(f)
return err
}
// ValidateAndSanitize validates and sanitizes the search form
func (f *SearchInquiriesForm) ValidateAndSanitize(xssPolicy *security.XSSPolicy) error {
if f.Search != nil {
sanitized, err := xssPolicy.ValidateAndSanitizeInput(*f.Search, "text")
if err != nil {
return err
}
*f.Search = sanitized
}
// Standard govalidator validation
_, err := govalidator.ValidateStruct(f)
return err
}
+14 -12
View File
@@ -4,22 +4,24 @@ import (
"encoding/json"
"tm/pkg/logger"
"tm/pkg/response"
"tm/pkg/security"
"github.com/asaskevich/govalidator"
"github.com/labstack/echo/v4"
)
// Handler handles HTTP requests for inquiry operations
type Handler struct {
service Service
logger logger.Logger
service Service
logger logger.Logger
xssPolicy *security.XSSPolicy
}
// NewInquiryHandler creates a new inquiry handler
func NewHandler(service Service, logger logger.Logger) *Handler {
func NewHandler(service Service, logger logger.Logger, xssPolicy *security.XSSPolicy) *Handler {
return &Handler{
service: service,
logger: logger,
service: service,
logger: logger,
xssPolicy: xssPolicy,
}
}
@@ -42,8 +44,8 @@ func (h *Handler) CreateInquiry(c echo.Context) error {
return response.BadRequest(c, "Invalid request format", "")
}
// Validate form
if _, err := govalidator.ValidateStruct(form); err != nil {
// Validate and sanitize form
if err := form.ValidateAndSanitize(h.xssPolicy); err != nil {
return response.ValidationError(c, err.Error(), "")
}
@@ -122,8 +124,8 @@ func (h *Handler) UpdateInquiryStatus(c echo.Context) error {
return response.BadRequest(c, "Invalid request format", "")
}
// Validate form
if _, err := govalidator.ValidateStruct(form); err != nil {
// Validate and sanitize form
if err := form.ValidateAndSanitize(h.xssPolicy); err != nil {
return response.ValidationError(c, err.Error(), "")
}
@@ -163,8 +165,8 @@ func (h *Handler) SearchInquiries(c echo.Context) error {
return response.BadRequest(c, "Invalid request format", "")
}
// Validate form
if _, err := govalidator.ValidateStruct(form); err != nil {
// Validate and sanitize form
if err := form.ValidateAndSanitize(h.xssPolicy); err != nil {
return response.ValidationError(c, err.Error(), "")
}
+6 -3
View File
@@ -7,6 +7,7 @@ import (
"tm/pkg/logger"
"tm/pkg/notification"
"tm/pkg/response"
"tm/pkg/security"
)
// Service defines business logic for inquiry operations
@@ -23,14 +24,16 @@ type inquiryService struct {
repository Repository
sdk notification.SDK
logger logger.Logger
xssPolicy *security.XSSPolicy
}
// NewService creates a new inquiry service
func NewService(repository Repository, sdk notification.SDK, logger logger.Logger) Service {
func NewService(repository Repository, sdk notification.SDK, logger logger.Logger, xssPolicy *security.XSSPolicy) Service {
return &inquiryService{
repository: repository,
sdk: sdk,
logger: logger,
xssPolicy: xssPolicy,
}
}
@@ -114,7 +117,7 @@ func (s *inquiryService) Create(ctx context.Context, form *CreateInquiryForm) (*
CompanyLogo: "",
CompanyURL: "https://opplens.com",
SupportEmail: "info@opplens.com",
}),
}, s.xssPolicy),
Type: "alert",
Priority: "important",
EventType: notification.EventTypeEmail,
@@ -137,7 +140,7 @@ func (s *inquiryService) Create(ctx context.Context, form *CreateInquiryForm) (*
CompanyLogo: "",
CompanyURL: "https://opplens.com",
SupportEmail: "info@opplens.com",
}),
}, s.xssPolicy),
Type: "alert",
Priority: "important",
EventType: notification.EventTypeEmail,