mirror of
https://github.com/gtsteffaniak/filebrowser.git
synced 2026-11-04 16:50:08 -05:00
Compare commits
1
Commits
main
...
v2.0.4-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3974b28e5 |
No files matched your search
@@ -2,6 +2,11 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
|
||||
|
||||
## v2.0.4
|
||||
|
||||
**Bugfixes**:
|
||||
- OnlyOffice editor failed to load after the CSP security fix; allow configured `integrations.office.url` and `integrations.office.internalUrl` origins in `script-src` ([#2874](https://github.com/gtsteffaniak/filebrowser/issues/2874)).
|
||||
|
||||
## v2.0.3
|
||||
|
||||
**Security**:
|
||||
|
||||
@@ -50,6 +50,10 @@ func TestSessionCookie_NotSecureOnHTTP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSpaContentSecurityPolicy_BlocksUntrustedScripts(t *testing.T) {
|
||||
prev := settings.Config.Integrations.OnlyOffice
|
||||
settings.Config.Integrations.OnlyOffice = settings.OnlyOffice{}
|
||||
t.Cleanup(func() { settings.Config.Integrations.OnlyOffice = prev })
|
||||
|
||||
csp := spaContentSecurityPolicy("testnonce")
|
||||
if csp != "script-src 'self' 'nonce-testnonce' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com" {
|
||||
t.Fatalf("unexpected CSP: %s", csp)
|
||||
@@ -61,3 +65,58 @@ func TestSpaContentSecurityPolicy_BlocksUntrustedScripts(t *testing.T) {
|
||||
t.Fatal("CSP should only restrict script-src to avoid breaking frames, blobs, and external assets")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpaContentSecurityPolicy_IncludesOnlyOfficeOrigins(t *testing.T) {
|
||||
prev := settings.Config.Integrations.OnlyOffice
|
||||
t.Cleanup(func() { settings.Config.Integrations.OnlyOffice = prev })
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
oo settings.OnlyOffice
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "url only",
|
||||
oo: settings.OnlyOffice{Url: "http://localhost:9052"},
|
||||
want: "script-src 'self' 'nonce-testnonce' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com http://localhost:9052",
|
||||
},
|
||||
{
|
||||
name: "internal url only",
|
||||
oo: settings.OnlyOffice{InternalUrl: "http://onlyoffice-internal:80"},
|
||||
want: "script-src 'self' 'nonce-testnonce' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com http://onlyoffice-internal:80",
|
||||
},
|
||||
{
|
||||
name: "both urls",
|
||||
oo: settings.OnlyOffice{
|
||||
Url: "https://office.example.com",
|
||||
InternalUrl: "http://onlyoffice-internal",
|
||||
},
|
||||
want: "script-src 'self' 'nonce-testnonce' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com https://office.example.com http://onlyoffice-internal",
|
||||
},
|
||||
{
|
||||
name: "duplicate origins",
|
||||
oo: settings.OnlyOffice{
|
||||
Url: "http://localhost:9052",
|
||||
InternalUrl: "http://localhost:9052",
|
||||
},
|
||||
want: "script-src 'self' 'nonce-testnonce' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com http://localhost:9052",
|
||||
},
|
||||
{
|
||||
name: "invalid urls ignored",
|
||||
oo: settings.OnlyOffice{
|
||||
Url: "not-a-url",
|
||||
InternalUrl: "://bad",
|
||||
},
|
||||
want: "script-src 'self' 'nonce-testnonce' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
settings.Config.Integrations.OnlyOffice = tt.oo
|
||||
if got := spaContentSecurityPolicy("testnonce"); got != tt.want {
|
||||
t.Fatalf("unexpected CSP:\ngot: %s\nwant: %s", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -61,10 +61,46 @@ func (t *TemplateRenderer) Render(w http.ResponseWriter, name string, data inter
|
||||
// spaContentSecurityPolicy restricts only scripts. srcdoc preview frames inherit
|
||||
// this header, blocking inline scripts without limiting frames, images, or API calls.
|
||||
func spaContentSecurityPolicy(nonce string) string {
|
||||
return fmt.Sprintf(
|
||||
policy := fmt.Sprintf(
|
||||
"script-src 'self' 'nonce-%s' https://cdn.jsdelivr.net https://www.google.com https://www.gstatic.com",
|
||||
nonce,
|
||||
)
|
||||
for _, origin := range onlyOfficeScriptSrcOrigins() {
|
||||
policy += " " + origin
|
||||
}
|
||||
return policy
|
||||
}
|
||||
|
||||
func onlyOfficeScriptSrcOrigins() []string {
|
||||
oo := settings.Config.Integrations.OnlyOffice
|
||||
candidates := []string{
|
||||
cspOriginFromURL(oo.Url),
|
||||
cspOriginFromURL(oo.InternalUrl),
|
||||
}
|
||||
var origins []string
|
||||
seen := make(map[string]struct{}, len(candidates))
|
||||
for _, origin := range candidates {
|
||||
if origin == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[origin]; ok {
|
||||
continue
|
||||
}
|
||||
seen[origin] = struct{}{}
|
||||
origins = append(origins, origin)
|
||||
}
|
||||
return origins
|
||||
}
|
||||
|
||||
func cspOriginFromURL(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
return ""
|
||||
}
|
||||
return u.Scheme + "://" + u.Host
|
||||
}
|
||||
|
||||
func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestContext, file, contentType string) (int, error) {
|
||||
|
||||
Reference in new issue
Block a user