Skip to content

Commit

Permalink
Fix CSP inline script tag error in Console HTML template
Browse files Browse the repository at this point in the history
  • Loading branch information
vojtechszocs committed Oct 30, 2024
1 parent 5e8c422 commit e69f6ff
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 55 deletions.
28 changes: 14 additions & 14 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<base href="[[ .BasePath ]]" />
<base href="[[ .ServerFlags.BasePath ]]" />
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
[[ if .CustomProductName ]]
<title>[[ .CustomProductName ]]</title>
<meta name="application-name" content="[[ .CustomProductName ]]" />
[[ else ]] [[ if eq .Branding "okd" ]]
[[ if .ServerFlags.CustomProductName ]]
<title>[[ .ServerFlags.CustomProductName ]]</title>
<meta name="application-name" content="[[ .ServerFlags.CustomProductName ]]" />
[[ else ]] [[ if eq .ServerFlags.Branding "okd" ]]
<title>OKD</title>
<meta name="application-name" content="OKD" />
[[ end ]] [[ if eq .Branding "openshift" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "openshift" ]]
<title>Red Hat OpenShift</title>
<meta name="application-name" content="Red Hat OpenShift" />
[[ end ]] [[ if eq .Branding "ocp" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "ocp" ]]
<title>Red Hat OpenShift</title>
<meta name="application-name" content="Red Hat OpenShift" />
[[ end ]] [[ if eq .Branding "online" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "online" ]]
<title>Red Hat OpenShift Online</title>
<meta name="application-name" content="Red Hat OpenShift Online" />
[[ end ]] [[ if eq .Branding "dedicated" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "dedicated" ]]
<title>Red Hat OpenShift Dedicated</title>
<meta name="application-name" content="Red Hat OpenShift Dedicated" />
[[ end ]] [[ if eq .Branding "azure" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "azure" ]]
<title>Azure Red Hat OpenShift</title>
<meta name="application-name" content="Azure Red Hat OpenShift" />
[[ end ]] [[ if eq .Branding "rosa" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "rosa" ]]
<title>Red Hat OpenShift Service on AWS</title>
<meta name="application-name" content="Red Hat OpenShift Service on AWS" />
[[ end ]] [[ if eq .Branding "okd" ]]
[[ end ]] [[ if eq .ServerFlags.Branding "okd" ]]
<link rel="shortcut icon" href="<%= require('./imgs/okd-favicon.png') %>" />
<link
rel="apple-touch-icon-precomposed"
Expand Down Expand Up @@ -58,8 +58,8 @@

<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="text/javascript">
window.SERVER_FLAGS = [[.]];
<script type="text/javascript" nonce="[[ .ScriptNonce ]]">
window.SERVER_FLAGS = [[ .ServerFlags ]];
let theme = localStorage.getItem('bridge/theme') || 'systemDefault';
if (theme === 'systemDefault' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
theme = 'dark';
Expand Down
19 changes: 6 additions & 13 deletions pkg/auth/oauth2/auth_oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/openshift/console/pkg/auth"
"github.com/openshift/console/pkg/auth/sessions"
"github.com/openshift/console/pkg/metrics"
consoleUtils "github.com/openshift/console/pkg/utils"
)

const (
Expand Down Expand Up @@ -657,20 +658,12 @@ func BenchmarkRefreshSession(b *testing.B) {
}
}

func randomBytes(size int) []byte {
b := make([]byte, size)
if _, err := rand.Read(b); err != nil {
panic(err) // rand should never fail
}
return b
}

func randomString(size int) string {
// each byte (8 bits) gives us 4/3 base64 (6 bits) characters
// we account for that conversion and add one to handle truncation
b64size := base64.RawURLEncoding.DecodedLen(size) + 1
// trim down to the original requested size since we added one above
return base64.RawURLEncoding.EncodeToString(randomBytes(b64size))[:size]
str, err := consoleUtils.RandomString(size)
if err != nil {
panic(err) // should never fail
}
return str
}

func addIDToken(t *oauth2.Token, idtoken string) *oauth2.Token {
Expand Down
21 changes: 6 additions & 15 deletions pkg/auth/sessions/combined_sessions_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package sessions

import (
"crypto/rand"
"encoding/base64"
"net/http"
"net/http/httptest"
"reflect"
Expand All @@ -11,6 +9,7 @@ import (
"time"

"github.com/gorilla/securecookie"
consoleUtils "github.com/openshift/console/pkg/utils"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
utilptr "k8s.io/utils/ptr"
Expand Down Expand Up @@ -274,20 +273,12 @@ func addIDToken(t *oauth2.Token, idtoken string) *oauth2.Token {
return t
}

func randomBytes(size int) []byte {
b := make([]byte, size)
if _, err := rand.Read(b); err != nil {
panic(err) // rand should never fail
}
return b
}

func randomString(size int) string {
// each byte (8 bits) gives us 4/3 base64 (6 bits) characters
// we account for that conversion and add one to handle truncation
b64size := base64.RawURLEncoding.DecodedLen(size) + 1
// trim down to the original requested size since we added one above
return base64.RawURLEncoding.EncodeToString(randomBytes(b64size))[:size]
str, err := consoleUtils.RandomString(size)
if err != nil {
panic(err) // should never fail
}
return str
}

func TestCombinedSessionStore_UpdateTokens(t *testing.T) {
Expand Down
11 changes: 4 additions & 7 deletions pkg/auth/sessions/server_session.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package sessions

import (
"crypto/rand"
"encoding/base64"
"fmt"
"slices"
"sort"
"sync"
"time"

consoleUtils "github.com/openshift/console/pkg/utils"
"golang.org/x/oauth2"
"k8s.io/klog/v2"

"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2"
)

const (
Expand Down Expand Up @@ -175,12 +173,11 @@ func (ss *SessionStore) pruneSessions() {
func loginStateSorter(a, b *LoginState) int { return a.CompareExpiry(b) }

func RandomString(length int) string {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
str, err := consoleUtils.RandomString(length)
if err != nil {
panic(fmt.Sprintf("FATAL ERROR: Unable to get random bytes for session token: %v", err))
}
return base64.StdEncoding.EncodeToString(bytes)
return str
}

func spliceOut(slice []*LoginState, toRemove *LoginState) []*LoginState {
Expand Down
25 changes: 19 additions & 6 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/openshift/console/pkg/terminal"
"github.com/openshift/console/pkg/usage"
"github.com/openshift/console/pkg/usersettings"
consoleUtils "github.com/openshift/console/pkg/utils"
"github.com/openshift/console/pkg/version"

graphql "github.com/graph-gophers/graphql-go"
Expand Down Expand Up @@ -253,7 +254,7 @@ func (s *Server) HTTPHandler() (http.Handler, error) {
handleFunc := func(path string, handler http.HandlerFunc) { handle(path, handler) }

fn := func(loginInfo sessions.LoginJSON, successURL string, w http.ResponseWriter) {
jsg := struct {
templateData := struct {
sessions.LoginJSON `json:",inline"`
LoginSuccessURL string `json:"loginSuccessURL"`
Branding string `json:"branding"`
Expand All @@ -273,9 +274,8 @@ func (s *Server) HTTPHandler() (http.Handler, error) {
os.Exit(1)
}

if err := tpls.ExecuteTemplate(w, tokenizerPageTemplateName, jsg); err != nil {
fmt.Printf("%v", err)
os.Exit(1)
if err := tpls.ExecuteTemplate(w, tokenizerPageTemplateName, templateData); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

Expand Down Expand Up @@ -675,6 +675,11 @@ func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
return
}

indexPageScriptNonce, err := consoleUtils.RandomString(32)
if err != nil {
panic(err)
}

// This Content Security Policy (CSP) applies to Console web application resources.
// Console CSP is deployed in report-only mode via "Content-Security-Policy-Report-Only" header.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP for details on CSP specification.
Expand All @@ -688,7 +693,7 @@ func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Sprintf("base-uri %s", cspSources),
fmt.Sprintf("img-src %s data:", cspSources),
fmt.Sprintf("font-src %s data:", cspSources),
fmt.Sprintf("script-src %s 'unsafe-eval'", cspSources),
fmt.Sprintf("script-src %s 'unsafe-eval' 'nonce-%s'", cspSources, indexPageScriptNonce),
fmt.Sprintf("style-src %s 'unsafe-inline'", cspSources),
"frame-src 'none'",
"frame-ancestors 'none'",
Expand Down Expand Up @@ -760,6 +765,14 @@ func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
jsg.CustomLogoURL = proxy.SingleJoiningSlash(s.BaseURL.Path, customLogoEndpoint)
}

templateData := struct {
ServerFlags *jsGlobals
ScriptNonce string
}{
ServerFlags: jsg,
ScriptNonce: indexPageScriptNonce,
}

tpl := template.New(indexPageTemplateName)
tpl.Delims("[[", "]]")
tpls, err := tpl.ParseFiles(path.Join(s.PublicDir, indexPageTemplateName))
Expand All @@ -768,7 +781,7 @@ func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
os.Exit(1)
}

if err := tpls.ExecuteTemplate(w, indexPageTemplateName, jsg); err != nil {
if err := tpls.ExecuteTemplate(w, indexPageTemplateName, templateData); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Expand Down
26 changes: 26 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package utils

import (
"crypto/rand"
"encoding/base64"
)

// Generate a cryptographically secure random array of bytes.
func RandomBytes(length int) ([]byte, error) {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
return bytes, err
}

// Generate a cryptographically secure random string.
// Returned string is encoded using [encoding.RawURLEncoding]
// which makes it safe to use in URLs and file names.
func RandomString(length int) (string, error) {
encoding := base64.RawURLEncoding
// each byte (8 bits) gives us 4/3 base64 (6 bits) characters,
// we account for that conversion and add one to handle truncation
b64size := encoding.DecodedLen(length) + 1
randomBytes, err := RandomBytes(b64size)
// trim down to the original requested size since we added one above
return encoding.EncodeToString(randomBytes)[:length], err
}

0 comments on commit e69f6ff

Please sign in to comment.