HH-540: scope iframe policy to Widget page (#123)
* fix(HH-540): scope iframe policy to widget page * fix(HH-540): reject non-string allowed domains --------- Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
@@ -29,6 +29,70 @@ func NewHandler(widgetService *service.WidgetService) *WidgetHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// AllowIframeRequests applies Chatwoot's WidgetsController iframe policy only
|
||||
// to the widget HTML response.
|
||||
func (h *WidgetHandler) AllowIframeRequests(c *gin.Context) {
|
||||
inbox, err := h.widgetService.GetInboxByWebsiteToken(c.Request.Context(), strings.TrimSpace(c.Query("website_token")))
|
||||
if err != nil {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
config, err := service.ParseWebWidgetConfig(inbox.ChannelConfig)
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
frameAncestors, ok := widgetFrameAncestors(config.AllowedDomains)
|
||||
if !ok {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Del("X-Frame-Options")
|
||||
c.Header("Content-Security-Policy", widgetContentSecurityPolicy(c.Writer.Header().Get("Content-Security-Policy"), frameAncestors))
|
||||
}
|
||||
|
||||
func widgetFrameAncestors(allowedDomains string) (string, bool) {
|
||||
domains := make([]string, 0)
|
||||
for _, domain := range strings.Split(allowedDomains, ",") {
|
||||
domain = strings.TrimSpace(domain)
|
||||
if domain == "" {
|
||||
continue
|
||||
}
|
||||
if strings.ContainsAny(domain, "; \t\r\n") {
|
||||
return "", false
|
||||
}
|
||||
domains = append(domains, domain)
|
||||
}
|
||||
return strings.Join(domains, " "), true
|
||||
}
|
||||
|
||||
func widgetContentSecurityPolicy(policy, frameAncestors string) string {
|
||||
directives := strings.Split(policy, ";")
|
||||
result := make([]string, 0, len(directives))
|
||||
found := false
|
||||
for _, directive := range directives {
|
||||
directive = strings.TrimSpace(directive)
|
||||
if strings.HasPrefix(directive, "frame-ancestors ") {
|
||||
found = true
|
||||
if frameAncestors != "" {
|
||||
result = append(result, "frame-ancestors "+frameAncestors)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if directive != "" {
|
||||
result = append(result, directive)
|
||||
}
|
||||
}
|
||||
if frameAncestors != "" && !found {
|
||||
result = append(result, "frame-ancestors "+frameAncestors)
|
||||
}
|
||||
return strings.Join(result, "; ")
|
||||
}
|
||||
|
||||
// Init handles widget initialization — authenticates/creates a contact
|
||||
// and returns a widget_token (pubsub_token) for subsequent requests.
|
||||
// POST /widget/init
|
||||
|
||||
Reference in New Issue
Block a user