Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions internal/controller/httpapi/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,25 @@ import (
"github.com/device-management-toolkit/console/pkg/logger"
)

const (
xFrameOptionsHeaderValue = "SAMEORIGIN"
contentSecurityPolicyHeaderValue = "frame-ancestors 'self'"
)
Comment thread
DevipriyaS17 marked this conversation as resolved.

func clickjackingProtectionMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("X-Frame-Options", xFrameOptionsHeaderValue)
c.Header("Content-Security-Policy", contentSecurityPolicyHeaderValue)
c.Next()
}
}

// NewRouter -.
func NewRouter(handler *gin.Engine, l logger.Interface, t usecase.Usecases, cfg *config.Config) {
// Options
handler.Use(gin.Logger())
handler.Use(gin.Recovery())
handler.Use(clickjackingProtectionMiddleware())

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.

The current approach adds these headers for container and headless deployments as well. I recommend limiting them to the standalone Console build with the embedded UI, since container/headless deployments expose only REST APIs and do not serve browser-rendered content where clickjacking or browser capability policies apply.
Consider making the browser security-header behavior runtime-configurable to allow deployment-specific customization.

@rsdmike / @MadhaviLosetty : Do you agree on this ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@rsdmike @MadhaviLosetty please let us know your thoughts


// Add Prometheus middleware for automatic HTTP metrics
// Don't automatically register /metrics endpoint - we have our own
Expand Down
53 changes: 53 additions & 0 deletions internal/controller/httpapi/router_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package httpapi

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"

"github.com/device-management-toolkit/console/config"
"github.com/device-management-toolkit/console/internal/usecase"
"github.com/device-management-toolkit/console/pkg/logger"
)

func TestClickjackingProtectionMiddlewareSetsHeaders(t *testing.T) {
t.Parallel()

engine := gin.New()
engine.Use(clickjackingProtectionMiddleware())
engine.GET("/healthz", func(c *gin.Context) {
c.Status(http.StatusOK)
})

req := httptest.NewRequest(http.MethodGet, "/healthz", http.NoBody)
w := httptest.NewRecorder()

engine.ServeHTTP(w, req)

require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, xFrameOptionsHeaderValue, w.Header().Get("X-Frame-Options"))
require.Equal(t, contentSecurityPolicyHeaderValue, w.Header().Get("Content-Security-Policy"))
}

//nolint:paralleltest // mutates shared global config.ConsoleConfig
func TestNewRouterAuthorizeHasClickjackingHeaders(t *testing.T) {
prev := config.ConsoleConfig
config.ConsoleConfig = &config.Config{}

t.Cleanup(func() { config.ConsoleConfig = prev })

engine := gin.New()
NewRouter(engine, logger.New("error"), usecase.Usecases{}, &config.Config{})

req := httptest.NewRequest(http.MethodPost, "/api/v1/authorize", http.NoBody)
w := httptest.NewRecorder()

engine.ServeHTTP(w, req)

require.Equal(t, http.StatusBadRequest, w.Code)
require.Equal(t, xFrameOptionsHeaderValue, w.Header().Get("X-Frame-Options"))
require.Equal(t, contentSecurityPolicyHeaderValue, w.Header().Get("Content-Security-Policy"))
}
Loading