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:
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/gochat/gochat/internal/campaign"
|
||||
"github.com/gochat/gochat/internal/config"
|
||||
apiv1 "github.com/gochat/gochat/internal/handler/api/v1"
|
||||
"github.com/gochat/gochat/internal/middleware"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
channelmodel "github.com/gochat/gochat/internal/model/channel"
|
||||
"github.com/gochat/gochat/internal/repository"
|
||||
@@ -243,6 +244,67 @@ func seedWidgetHandlerData(t *testing.T, db *gorm.DB) (*model.Account, *model.In
|
||||
return account, inbox
|
||||
}
|
||||
|
||||
func TestAllowIframeRequestsScopesEmbeddingToWidgetPage(t *testing.T) {
|
||||
db, _, handler := setupWidgetHandlerTest(t)
|
||||
account, _ := seedWidgetHandlerData(t, db)
|
||||
|
||||
configJSON, err := json.Marshal(map[string]any{
|
||||
"website_token": "restricted_widget",
|
||||
"allowed_domains": "https://allowed.example, https://support.example",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.Create(&model.Inbox{
|
||||
AccountID: account.ID, Name: "Restricted Widget", ChannelType: "web_widget", Enabled: true, ChannelConfig: string(configJSON),
|
||||
}).Error)
|
||||
|
||||
invalidConfigJSON, err := json.Marshal(map[string]any{
|
||||
"website_token": "invalid_widget",
|
||||
"allowed_domains": "https://allowed.example; frame-ancestors *",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.Create(&model.Inbox{
|
||||
AccountID: account.ID, Name: "Invalid Widget", ChannelType: "web_widget", Enabled: true, ChannelConfig: string(invalidConfigJSON),
|
||||
}).Error)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.SecurityHeaders(middleware.DefaultSecurityHeadersConfig()))
|
||||
router.GET("/widget", handler.AllowIframeRequests, func(c *gin.Context) { c.String(http.StatusOK, "widget") })
|
||||
router.GET("/app", func(c *gin.Context) { c.String(http.StatusOK, "dashboard") })
|
||||
|
||||
for _, origin := range []string{"https://go-web-inbox.yqbmb.com", "https://unrelated.example"} {
|
||||
t.Run("default widget from "+origin, func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "/widget?website_token=handler_ws_token_123", nil)
|
||||
request.Header.Set("Referer", origin+"/")
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
assert.Empty(t, response.Header().Get("X-Frame-Options"))
|
||||
assert.NotContains(t, response.Header().Get("Content-Security-Policy"), "frame-ancestors")
|
||||
assert.Contains(t, response.Header().Get("Content-Security-Policy"), "script-src 'self'")
|
||||
})
|
||||
}
|
||||
|
||||
restricted := httptest.NewRecorder()
|
||||
router.ServeHTTP(restricted, httptest.NewRequest(http.MethodGet, "/widget?website_token=restricted_widget", nil))
|
||||
assert.Equal(t, http.StatusOK, restricted.Code)
|
||||
assert.Empty(t, restricted.Header().Get("X-Frame-Options"))
|
||||
assert.Contains(t, restricted.Header().Get("Content-Security-Policy"), "frame-ancestors https://allowed.example https://support.example")
|
||||
assert.Contains(t, restricted.Header().Get("Content-Security-Policy"), "script-src 'self'")
|
||||
|
||||
invalid := httptest.NewRecorder()
|
||||
router.ServeHTTP(invalid, httptest.NewRequest(http.MethodGet, "/widget?website_token=invalid_widget", nil))
|
||||
assert.Equal(t, http.StatusInternalServerError, invalid.Code)
|
||||
assert.Equal(t, "DENY", invalid.Header().Get("X-Frame-Options"))
|
||||
assert.Contains(t, invalid.Header().Get("Content-Security-Policy"), "frame-ancestors 'none'")
|
||||
|
||||
dashboard := httptest.NewRecorder()
|
||||
router.ServeHTTP(dashboard, httptest.NewRequest(http.MethodGet, "/app", nil))
|
||||
assert.Equal(t, http.StatusOK, dashboard.Code)
|
||||
assert.Equal(t, "DENY", dashboard.Header().Get("X-Frame-Options"))
|
||||
assert.Contains(t, dashboard.Header().Get("Content-Security-Policy"), "frame-ancestors 'none'")
|
||||
}
|
||||
|
||||
func seedPublicAPIInbox(t *testing.T, db *gorm.DB) (*model.Account, *model.Inbox, *channelmodel.ChannelAPI) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user