Repository: owncloud/ocis
Pull request #9033 (2024-04-30 15:41:26)

chore: remove unused return value from BindSourcesToStructs
## Description
The returned gookit config struct was unused and given the function comment it should never be used.
Removing it as a result.

## Motivation and Context
:broom: 

## How Has This Been Tested?
- :robot: 

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [x] Technical debt
- [ ] Tests only (no source changes)

## Checklist:
<!-- Tick the checkboxes when done. -->
<!-- Raise documentation ticket in owncloud.github.io/ -->
- [x] Code changes
- [ ] Unit tests added
- [ ] Acceptance tests added
- [ ] Documentation ticket raised: <link> 


update-docs[bot]: Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis/blob/master/changelog/README.md) item based on your changes.

sonarcloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9033) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [71.7% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9033&metric=new_duplicated_lines_density&view=list) (required ≤ 50%)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9033)



Patch:
diff --git a/deployments/examples/ocis_keycloak/config/ocis/csp.yaml b/deployments/examples/ocis_keycloak/config/ocis/csp.yaml
index 6af24773c81..0f960cf343b 100644
--- a/deployments/examples/ocis_keycloak/config/ocis/csp.yaml
+++ b/deployments/examples/ocis_keycloak/config/ocis/csp.yaml
@@ -3,7 +3,8 @@ directives:
     - '''self'''
   connect-src:
     - '''self'''
-    - 'https://${KEYCLOAK_DOMAIN:-keycloak.owncloud.test}/'
+    # In contrary to bash and docker the default is given after the | character
+    - 'https://${KEYCLOAK_DOMAIN|keycloak.owncloud.test}/'
   default-src:
     - '''none'''
   font-src:
diff --git a/deployments/examples/ocis_wopi/config/ocis/csp.yaml b/deployments/examples/ocis_wopi/config/ocis/csp.yaml
index 518f906f142..f8cfb14eb42 100644
--- a/deployments/examples/ocis_wopi/config/ocis/csp.yaml
+++ b/deployments/examples/ocis_wopi/config/ocis/csp.yaml
@@ -12,14 +12,16 @@ directives:
   frame-src:
     - '''self'''
     - 'https://embed.diagrams.net/'
-    - 'https://${ONLYOFFICE_DOMAIN:-onlyoffice.owncloud.test}/'
-    - 'https://${COLLABORA_DOMAIN:-collabora.owncloud.test}/'
+    # In contrary to bash and docker the default is given after the | character
+    - 'https://${ONLYOFFICE_DOMAIN|onlyoffice.owncloud.test}/'
+    - 'https://${COLLABORA_DOMAIN|collabora.owncloud.test}/'
   img-src:
     - '''self'''
     - 'data:'
     - 'blob:'
-    - 'https://${ONLYOFFICE_DOMAIN:-onlyoffice.owncloud.test}/'
-    - 'https://${COLLABORA_DOMAIN:-collabora.owncloud.test}/'
+    # In contrary to bash and docker the default is given after the | character
+    - 'https://${ONLYOFFICE_DOMAIN|onlyoffice.owncloud.test}/'
+    - 'https://${COLLABORA_DOMAIN|collabora.owncloud.test}/'
   manifest-src:
     - '''self'''
   media-src:
diff --git a/docs/ocis/config.md b/docs/ocis/config.md
index c6eafc91f49..9d8916667ec 100644
--- a/docs/ocis/config.md
+++ b/docs/ocis/config.md
@@ -44,7 +44,7 @@ Let's explore with examples this approach.
 - docker images: `/etc/ocis/`
 - binary releases: `$HOME/.ocis/config/`
 
-followed by the `<extension name>.yaml`, eg `proxy.yaml` for the extension configuration. You also can put an `ocis.yaml` config file to the expected loading location to use a single config file.
+followed by the `<extension name>.yaml`, e.g. `proxy.yaml` for the extension configuration. You also can put an `ocis.yaml` config file to the expected loading location to use a single config file.
 
 You can set another directory as config path in the environment variable `OCIS_CONFIG_DIR`. It will then pick the same file names, but from the folder you configured.
 
@@ -99,6 +99,21 @@ log:
   level: info
 ```
 
+#### Substitute ENV variables in configuration files
+
+Environment variables can be used in the configurations files and will be replaced by oCIS when loading these.
+Default values can be specified after a `|` character - see below.
+
+```yaml
+proxy:
+  http:
+    addr: ${PROXY_HTTP_ADDR|localhost:4321}
+log:
+  pretty: true
+  color: true
+  level: info
+```
+
 ### Workflows
 
 Since one can run an extension using the runtime (supervised) or not (unsupervised), we ensure correct behavior in both modes, expecting the same outputs.
diff --git a/go.mod b/go.mod
index d467a376874..675178ccfb8 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,6 @@ require (
 	github.com/Masterminds/semver v1.5.0
 	github.com/MicahParks/keyfunc v1.9.0
 	github.com/Nerzal/gocloak/v13 v13.9.0
-	github.com/a8m/envsubst v1.4.2
 	github.com/bbalet/stopwords v1.0.0
 	github.com/beevik/etree v1.3.0
 	github.com/blevesearch/bleve/v2 v2.4.0
diff --git a/go.sum b/go.sum
index c5de2105a1b..9215dd8437b 100644
--- a/go.sum
+++ b/go.sum
@@ -826,8 +826,6 @@ github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVO
 github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
-github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
-github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
 github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6 h1:ws0yvsikTQdmheKINP16tBzAHdttrHwbz/q3Fgl9X1Y=
 github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
 github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
diff --git a/ocis-pkg/config/helpers.go b/ocis-pkg/config/helpers.go
index d2f867ef62d..d8e4722bb86 100644
--- a/ocis-pkg/config/helpers.go
+++ b/ocis-pkg/config/helpers.go
@@ -17,15 +17,14 @@ var (
 	decoderConfigTagName = "yaml"
 )
 
-// BindSourcesToStructs assigns any config value from a config file / env variable to struct `dst`. Its only purpose
-// is to solely modify `dst`, not dealing with the config structs; and do so in a thread safe manner.
-func BindSourcesToStructs(service string, dst interface{}) (*gofig.Config, error) {
+// BindSourcesToStructs assigns any config value from a config file / env variable to struct `dst`.
+func BindSourcesToStructs(service string, dst interface{}) error {
 	fileSystem := os.DirFS("/")
 	filePath := strings.TrimLeft(path.Join(defaults.BaseConfigPath(), service+".yaml"), "/")
 	return bindSourcesToStructs(fileSystem, filePath, service, dst)
 }
 
-func bindSourcesToStructs(fileSystem fs.FS, filePath, service string, dst interface{}) (*gofig.Config, error) {
+func bindSourcesToStructs(fileSystem fs.FS, filePath, service string, dst interface{}) error {
 	cnf := gofig.NewWithOptions(service)
 	cnf.WithOptions(func(options *gofig.Options) {
 		options.ParseEnv = true
@@ -36,17 +35,17 @@ func bindSourcesToStructs(fileSystem fs.FS, filePath, service string, dst interf
 	yamlContent, err := fs.ReadFile(fileSystem, filePath)
 	if err != nil {
 		if os.IsNotExist(err) {
-			return cnf, nil
+			return nil
 		}
 
-		return nil, err
+		return err
 	}
 	_ = cnf.LoadSources("yaml", yamlContent)
 
 	err = cnf.BindStruct("", &dst)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
-	return cnf, nil
+	return nil
 }
diff --git a/ocis-pkg/config/parser/parse.go b/ocis-pkg/config/parser/parse.go
index e2f5d0fb293..27d98f7b1e2 100644
--- a/ocis-pkg/config/parser/parse.go
+++ b/ocis-pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 // copies applicable parts into the commons part, from
 // where the services can copy it into their own config
 func ParseConfig(cfg *config.Config, skipValidate bool) error {
-	_, err := config.BindSourcesToStructs("ocis", cfg)
+	err := config.BindSourcesToStructs("ocis", cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/antivirus/pkg/config/parser/parse.go b/services/antivirus/pkg/config/parser/parse.go
index be14dc7b451..b762b9eb571 100644
--- a/services/antivirus/pkg/config/parser/parse.go
+++ b/services/antivirus/pkg/config/parser/parse.go
@@ -14,7 +14,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/app-provider/pkg/config/parser/parse.go b/services/app-provider/pkg/config/parser/parse.go
index 290a970b3e3..575041aec3f 100644
--- a/services/app-provider/pkg/config/parser/parse.go
+++ b/services/app-provider/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/app-registry/pkg/config/parser/parse.go b/services/app-registry/pkg/config/parser/parse.go
index 6b0d7e0fb59..4fb879c6b89 100644
--- a/services/app-registry/pkg/config/parser/parse.go
+++ b/services/app-registry/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/audit/pkg/config/parser/parse.go b/services/audit/pkg/config/parser/parse.go
index f4e5f3d6835..6eb0d12dd73 100644
--- a/services/audit/pkg/config/parser/parse.go
+++ b/services/audit/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/auth-basic/pkg/config/parser/parse.go b/services/auth-basic/pkg/config/parser/parse.go
index bf000769c1f..77a36f5025c 100644
--- a/services/auth-basic/pkg/config/parser/parse.go
+++ b/services/auth-basic/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/auth-bearer/pkg/config/parser/parse.go b/services/auth-bearer/pkg/config/parser/parse.go
index 01890e2e998..77e3df436be 100644
--- a/services/auth-bearer/pkg/config/parser/parse.go
+++ b/services/auth-bearer/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/auth-machine/pkg/config/parser/parse.go b/services/auth-machine/pkg/config/parser/parse.go
index 1b9c1606a92..1f6b850d9a7 100644
--- a/services/auth-machine/pkg/config/parser/parse.go
+++ b/services/auth-machine/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/auth-service/pkg/config/parser/parse.go b/services/auth-service/pkg/config/parser/parse.go
index 5e783305232..5eb858c5341 100644
--- a/services/auth-service/pkg/config/parser/parse.go
+++ b/services/auth-service/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/clientlog/pkg/config/parser/parse.go b/services/clientlog/pkg/config/parser/parse.go
index 2006207e0e0..16a73bef01e 100644
--- a/services/clientlog/pkg/config/parser/parse.go
+++ b/services/clientlog/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/collaboration/pkg/config/parser/parse.go b/services/collaboration/pkg/config/parser/parse.go
index 7fc029fc423..6f926fc4bb2 100644
--- a/services/collaboration/pkg/config/parser/parse.go
+++ b/services/collaboration/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/eventhistory/pkg/config/parser/parse.go b/services/eventhistory/pkg/config/parser/parse.go
index 196ef16a74f..e21eae15f4b 100644
--- a/services/eventhistory/pkg/config/parser/parse.go
+++ b/services/eventhistory/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/frontend/pkg/config/parser/parse.go b/services/frontend/pkg/config/parser/parse.go
index 121e033bb0e..baca0098cfc 100644
--- a/services/frontend/pkg/config/parser/parse.go
+++ b/services/frontend/pkg/config/parser/parse.go
@@ -14,7 +14,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/gateway/pkg/config/parser/parse.go b/services/gateway/pkg/config/parser/parse.go
index 7ca0fddc167..f86f8f4c61a 100644
--- a/services/gateway/pkg/config/parser/parse.go
+++ b/services/gateway/pkg/config/parser/parse.go
@@ -14,7 +14,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/graph/pkg/config/parser/parse.go b/services/graph/pkg/config/parser/parse.go
index 06813a7ce8d..4cdb9387974 100644
--- a/services/graph/pkg/config/parser/parse.go
+++ b/services/graph/pkg/config/parser/parse.go
@@ -16,7 +16,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/groups/pkg/config/parser/parse.go b/services/groups/pkg/config/parser/parse.go
index 074f6972d14..ed312dfc44d 100644
--- a/services/groups/pkg/config/parser/parse.go
+++ b/services/groups/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/idm/pkg/config/parser/parse.go b/services/idm/pkg/config/parser/parse.go
index e1446c17595..105792280ab 100644
--- a/services/idm/pkg/config/parser/parse.go
+++ b/services/idm/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/idp/pkg/config/parser/parse.go b/services/idp/pkg/config/parser/parse.go
index 3939baccdb9..9f13ae41070 100644
--- a/services/idp/pkg/config/parser/parse.go
+++ b/services/idp/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/invitations/pkg/config/parser/parse.go b/services/invitations/pkg/config/parser/parse.go
index f0a3e666730..d1e09361bc3 100644
--- a/services/invitations/pkg/config/parser/parse.go
+++ b/services/invitations/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/nats/pkg/config/parser/parse.go b/services/nats/pkg/config/parser/parse.go
index b741cf3a091..07dfdd37074 100644
--- a/services/nats/pkg/config/parser/parse.go
+++ b/services/nats/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/notifications/pkg/config/parser/parse.go b/services/notifications/pkg/config/parser/parse.go
index 127a26cc688..032d2a6b345 100644
--- a/services/notifications/pkg/config/parser/parse.go
+++ b/services/notifications/pkg/config/parser/parse.go
@@ -15,7 +15,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/ocdav/pkg/config/parser/parse.go b/services/ocdav/pkg/config/parser/parse.go
index 545dfe45af8..ada294bfafb 100644
--- a/services/ocdav/pkg/config/parser/parse.go
+++ b/services/ocdav/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/ocm/pkg/config/parser/parse.go b/services/ocm/pkg/config/parser/parse.go
index b13a2d4fbcd..d0a77880100 100644
--- a/services/ocm/pkg/config/parser/parse.go
+++ b/services/ocm/pkg/config/parser/parse.go
@@ -14,7 +14,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/ocs/pkg/config/parser/parse.go b/services/ocs/pkg/config/parser/parse.go
index bff72f8e7da..257052fb59b 100644
--- a/services/ocs/pkg/config/parser/parse.go
+++ b/services/ocs/pkg/config/parser/parse.go
@@ -14,7 +14,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/policies/pkg/config/parser/parse.go b/services/policies/pkg/config/parser/parse.go
index ce3f1f2cbf5..f89b989da95 100644
--- a/services/policies/pkg/config/parser/parse.go
+++ b/services/policies/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/postprocessing/pkg/config/parser/parse.go b/services/postprocessing/pkg/config/parser/parse.go
index c1fc11d5e0e..e10e15291a2 100644
--- a/services/postprocessing/pkg/config/parser/parse.go
+++ b/services/postprocessing/pkg/config/parser/parse.go
@@ -15,7 +15,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/proxy/pkg/config/parser/parse.go b/services/proxy/pkg/config/parser/parse.go
index af176ccf9f1..f4d96a484e5 100644
--- a/services/proxy/pkg/config/parser/parse.go
+++ b/services/proxy/pkg/config/parser/parse.go
@@ -14,7 +14,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/proxy/pkg/middleware/security.go b/services/proxy/pkg/middleware/security.go
index 13f2e5beb16..414d9f5c6e6 100644
--- a/services/proxy/pkg/middleware/security.go
+++ b/services/proxy/pkg/middleware/security.go
@@ -1,11 +1,11 @@
 package middleware
 
 import (
-	"github.com/a8m/envsubst"
+	gofig "github.com/gookit/config/v2"
+	"github.com/gookit/config/v2/yaml"
 	"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
 	"github.com/unrolled/secure"
 	"github.com/unrolled/secure/cspbuilder"
-	"gopkg.in/yaml.v2"
 	"net/http"
 	"os"
 )
@@ -16,15 +16,23 @@ func LoadCSPConfig(proxyCfg *config.Config) (*config.CSP, error) {
 	if err != nil {
 		return nil, err
 	}
-	// replace env vars ..
-	yamlContent, err = envsubst.Bytes(yamlContent)
+	return loadCSPConfig(yamlContent)
+}
+
+// LoadCSPConfig loads CSP header configuration from a yaml file.
+func loadCSPConfig(yamlContent []byte) (*config.CSP, error) {
+	// substitute env vars and load to struct
+	gofig.WithOptions(gofig.ParseEnv)
+	gofig.AddDriver(yaml.Driver)
+
+	err := gofig.LoadSources("yaml", yamlContent)
 	if err != nil {
 		return nil, err
 	}
 
 	// read yaml
 	cspConfig := config.CSP{}
-	err = yaml.Unmarshal(yamlContent, &cspConfig)
+	err = gofig.BindStruct("", &cspConfig)
 	if err != nil {
 		return nil, err
 	}
diff --git a/services/search/pkg/config/parser/parse.go b/services/search/pkg/config/parser/parse.go
index c16ccebac70..7e8ee90937c 100644
--- a/services/search/pkg/config/parser/parse.go
+++ b/services/search/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/settings/pkg/config/parser/parse.go b/services/settings/pkg/config/parser/parse.go
index bbfcd1175dc..ef1edf14fc2 100644
--- a/services/settings/pkg/config/parser/parse.go
+++ b/services/settings/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/sharing/pkg/config/parser/parse.go b/services/sharing/pkg/config/parser/parse.go
index c22fb4c0a32..f03d02aeaa0 100644
--- a/services/sharing/pkg/config/parser/parse.go
+++ b/services/sharing/pkg/config/parser/parse.go
@@ -17,7 +17,7 @@ const (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/sse/pkg/config/parser/parse.go b/services/sse/pkg/config/parser/parse.go
index dc9375210de..87429b8a224 100644
--- a/services/sse/pkg/config/parser/parse.go
+++ b/services/sse/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/storage-publiclink/pkg/config/parser/parse.go b/services/storage-publiclink/pkg/config/parser/parse.go
index b081f858be5..1b037a9215b 100644
--- a/services/storage-publiclink/pkg/config/parser/parse.go
+++ b/services/storage-publiclink/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/storage-shares/pkg/config/parser/parse.go b/services/storage-shares/pkg/config/parser/parse.go
index 6c7b5c37162..6eda2da7a0c 100644
--- a/services/storage-shares/pkg/config/parser/parse.go
+++ b/services/storage-shares/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/storage-system/pkg/config/parser/parse.go b/services/storage-system/pkg/config/parser/parse.go
index ce706ed670f..d18bea7bdc3 100644
--- a/services/storage-system/pkg/config/parser/parse.go
+++ b/services/storage-system/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/storage-users/pkg/config/parser/parse.go b/services/storage-users/pkg/config/parser/parse.go
index e9d69cc9e2c..fcea9c93330 100644
--- a/services/storage-users/pkg/config/parser/parse.go
+++ b/services/storage-users/pkg/config/parser/parse.go
@@ -15,7 +15,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/store/pkg/config/parser/parse.go b/services/store/pkg/config/parser/parse.go
index 5fb6a3c557a..ebe57d4150d 100644
--- a/services/store/pkg/config/parser/parse.go
+++ b/services/store/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/thumbnails/pkg/config/parser/parse.go b/services/thumbnails/pkg/config/parser/parse.go
index 020c1374737..6334261084a 100644
--- a/services/thumbnails/pkg/config/parser/parse.go
+++ b/services/thumbnails/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/userlog/pkg/config/parser/parse.go b/services/userlog/pkg/config/parser/parse.go
index 7cb43fb0f92..0f556aafc8a 100644
--- a/services/userlog/pkg/config/parser/parse.go
+++ b/services/userlog/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/users/pkg/config/parser/parse.go b/services/users/pkg/config/parser/parse.go
index b776daa64cd..1dd9a8bd31e 100644
--- a/services/users/pkg/config/parser/parse.go
+++ b/services/users/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/web/pkg/config/parser/parse.go b/services/web/pkg/config/parser/parse.go
index c5bb6f03f50..ee53a35e6be 100644
--- a/services/web/pkg/config/parser/parse.go
+++ b/services/web/pkg/config/parser/parse.go
@@ -13,7 +13,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
@@ -29,7 +29,7 @@ func ParseConfig(cfg *config.Config) error {
 	}
 
 	// apps are a special case, as they are not part of the main config, but are loaded from a separate config file
-	_, err = ociscfg.BindSourcesToStructs("apps", &cfg.Apps)
+	err = ociscfg.BindSourcesToStructs("apps", &cfg.Apps)
 	if err != nil {
 		return err
 	}
diff --git a/services/webdav/pkg/config/parser/parse.go b/services/webdav/pkg/config/parser/parse.go
index f1f34ba4954..6c3dd66bc6e 100644
--- a/services/webdav/pkg/config/parser/parse.go
+++ b/services/webdav/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/services/webfinger/pkg/config/parser/parse.go b/services/webfinger/pkg/config/parser/parse.go
index 014f95a27d5..14b570cedf7 100644
--- a/services/webfinger/pkg/config/parser/parse.go
+++ b/services/webfinger/pkg/config/parser/parse.go
@@ -12,7 +12,7 @@ import (
 
 // ParseConfig loads configuration from known paths.
 func ParseConfig(cfg *config.Config) error {
-	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
 	if err != nil {
 		return err
 	}
diff --git a/vendor/github.com/a8m/envsubst/.travis.yml b/vendor/github.com/a8m/envsubst/.travis.yml
deleted file mode 100644
index 78df272d555..00000000000
--- a/vendor/github.com/a8m/envsubst/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: go
-go:
-  - 1.13
-  - 1.14
-  - tip
-scripts:
-  - go test
diff --git a/vendor/github.com/a8m/envsubst/LICENSE b/vendor/github.com/a8m/envsubst/LICENSE
deleted file mode 100644
index 8d89ecb65e0..00000000000
--- a/vendor/github.com/a8m/envsubst/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 envsubst contributors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/a8m/envsubst/README.md b/vendor/github.com/a8m/envsubst/README.md
deleted file mode 100644
index 52e64636f3b..00000000000
--- a/vendor/github.com/a8m/envsubst/README.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# envsubst
-[![GoDoc][godoc-img]][godoc-url]
-[![License][license-image]][license-url]
-[![Build status][travis-image]][travis-url]
-[![Github All Releases][releases-image]][releases]
-
-> Environment variables substitution for Go. see docs [below](#docs)
-
-#### Installation:
-
-##### From binaries
-Latest stable `envsubst` [prebuilt binaries for 64-bit Linux, or Mac OS X][releases] are available via Github releases.
-
-###### Linux and MacOS
-```console
-curl -L https://github.com/a8m/envsubst/releases/download/v1.2.0/envsubst-`uname -s`-`uname -m` -o envsubst
-chmod +x envsubst
-sudo mv envsubst /usr/local/bin
-```
-
-###### Windows
-Download the latest prebuilt binary from [releases page][releases], or if you have curl installed:
-```console
-curl -L https://github.com/a8m/envsubst/releases/download/v1.2.0/envsubst.exe
-```
-
-##### With go
-You can install via `go get` (provided you have installed go):
-```console
-go get github.com/a8m/envsubst/cmd/envsubst
-```
-
-
-#### Using via cli
-```sh
-envsubst < input.tmpl > output.text
-echo 'welcome $HOME ${USER:=a8m}' | envsubst
-envsubst -help
-```
-
-#### Imposing restrictions
-There are three command line flags with which you can cause the substitution to stop with an error code, should the restriction associated with the flag not be met. This can be handy if you want to avoid creating e.g. configuration files with unset or empty parameters.
-Setting a `-fail-fast` flag in conjunction with either no-unset or no-empty or both will result in a faster feedback loop, this can be especially useful when running through a large file or byte array input, otherwise a list of errors is returned.
-
-The flags and their restrictions are: 
-
-|__Option__     | __Meaning__    | __Type__ | __Default__  |
-| ------------| -------------- | ------------ | ------------ |
-|`-i`  | input file  | ```string | stdin``` | `stdin`
-|`-o`  | output file | ```string | stdout``` |  `stdout` 
-|`-no-digit`  | do not replace variables starting with a digit, e.g. $1 and ${1} | `flag` |  `false` 
-|`-no-unset`  | fail if a variable is not set | `flag` |  `false` 
-|`-no-empty`  | fail if a variable is set but empty | `flag` | `false`
-|`-fail-fast`  | fails at first occurence of an error, if `-no-empty` or `-no-unset` flags were **not** specified this is ignored | `flag` | `false`
-
-These flags can be combined to form tighter restrictions. 
-
-#### Using `envsubst` programmatically ?
-You can take a look on [`_example/main`](https://github.com/a8m/envsubst/blob/master/_example/main.go) or see the example below.
-```go
-package main
-
-import (
-	"fmt"
-	"github.com/a8m/envsubst"
-)
-
-func main() {
-    input := "welcom $HOME"
-    str, err := envsubst.String(input)
-    // ...
-    buf, err := envsubst.Bytes([]byte(input))
-    // ...
-    buf, err := envsubst.ReadFile("filename")
-}
-```
-### Docs
-> api docs here: [![GoDoc][godoc-img]][godoc-url]
-
-|__Expression__     | __Meaning__    |
-| ----------------- | -------------- |
-|`${var}`           | Value of var (same as `$var`)
-|`${var-$DEFAULT}`  | If var not set, evaluate expression as $DEFAULT
-|`${var:-$DEFAULT}` | If var not set or is empty, evaluate expression as $DEFAULT
-|`${var=$DEFAULT}`  | If var not set, evaluate expression as $DEFAULT
-|`${var:=$DEFAULT}` | If var not set or is empty, evaluate expression as $DEFAULT
-|`${var+$OTHER}`    | If var set, evaluate expression as $OTHER, otherwise as empty string
-|`${var:+$OTHER}`   | If var set, evaluate expression as $OTHER, otherwise as empty string
-|`$$var`            | Escape expressions. Result will be `$var`. 
-
-<sub>Most of the rows in this table were taken from [here](http://www.tldp.org/LDP/abs/html/refcards.html#AEN22728)</sub>
-
-### See also
-
-* `os.ExpandEnv(s string) string` - only supports `$var` and `${var}` notations
-
-#### License
-MIT
-
-[releases]: https://github.com/a8m/envsubst/releases
-[releases-image]: https://img.shields.io/github/downloads/a8m/envsubst/total.svg?style=for-the-badge
-[godoc-url]: https://godoc.org/github.com/a8m/envsubst
-[godoc-img]: https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge
-[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge
-[license-url]: LICENSE
-[travis-image]: https://img.shields.io/travis/a8m/envsubst.svg?style=for-the-badge
-[travis-url]: https://travis-ci.org/a8m/envsubst
diff --git a/vendor/github.com/a8m/envsubst/envsubst.go b/vendor/github.com/a8m/envsubst/envsubst.go
deleted file mode 100644
index 22b5bb83845..00000000000
--- a/vendor/github.com/a8m/envsubst/envsubst.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package envsubst
-
-import (
-	"io/ioutil"
-	"os"
-
-	"github.com/a8m/envsubst/parse"
-)
-
-// String returns the parsed template string after processing it.
-// If the parser encounters invalid input, it returns an error describing the failure.
-func String(s string) (string, error) {
-	return StringRestricted(s, false, false)
-}
-
-// StringRestricted returns the parsed template string after processing it.
-// If the parser encounters invalid input, or a restriction is violated, it returns
-// an error describing the failure.
-// Errors on first failure or returns a collection of failures if failOnFirst is false
-func StringRestricted(s string, noUnset, noEmpty bool) (string, error) {
-	return StringRestrictedNoDigit(s, noUnset, noEmpty , false)
-}
-
-// Like StringRestricted but additionally allows to ignore env variables which start with a digit.
-func StringRestrictedNoDigit(s string, noUnset, noEmpty bool, noDigit bool) (string, error) {
-	return parse.New("string", os.Environ(),
-		&parse.Restrictions{noUnset, noEmpty, noDigit}).Parse(s)
-}
-
-// Bytes returns the bytes represented by the parsed template after processing it.
-// If the parser encounters invalid input, it returns an error describing the failure.
-func Bytes(b []byte) ([]byte, error) {
-	return BytesRestricted(b, false, false)
-}
-
-// BytesRestricted returns the bytes represented by the parsed template after processing it.
-// If the parser encounters invalid input, or a restriction is violated, it returns
-// an error describing the failure.
-func BytesRestricted(b []byte, noUnset, noEmpty bool) ([]byte, error) {
-	return BytesRestrictedNoDigit(b, noUnset, noEmpty, false)
-}
-
-// Like BytesRestricted but additionally allows to ignore env variables which start with a digit.
-func BytesRestrictedNoDigit(b []byte, noUnset, noEmpty bool, noDigit bool) ([]byte, error) {
-	s, err := parse.New("bytes", os.Environ(),
-		&parse.Restrictions{noUnset, noEmpty, noDigit}).Parse(string(b))
-	if err != nil {
-		return nil, err
-	}
-	return []byte(s), nil
-}
-
-// ReadFile call io.ReadFile with the given file name.
-// If the call to io.ReadFile failed it returns the error; otherwise it will
-// call envsubst.Bytes with the returned content.
-func ReadFile(filename string) ([]byte, error) {
-	return ReadFileRestricted(filename, false, false)
-}
-
-// ReadFileRestricted calls io.ReadFile with the given file name.
-// If the call to io.ReadFile failed it returns the error; otherwise it will
-// call envsubst.Bytes with the returned content.
-func ReadFileRestricted(filename string, noUnset, noEmpty bool) ([]byte, error) {
-	return ReadFileRestrictedNoDigit(filename, noUnset, noEmpty, false)
-}
-
-// Like ReadFileRestricted but additionally allows to ignore env variables which start with a digit.
-func ReadFileRestrictedNoDigit(filename string, noUnset, noEmpty bool, noDigit bool) ([]byte, error) {
-	b, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return nil, err
-	}
-	return BytesRestrictedNoDigit(b, noUnset, noEmpty, noDigit)
-}
diff --git a/vendor/github.com/a8m/envsubst/parse/env.go b/vendor/github.com/a8m/envsubst/parse/env.go
deleted file mode 100644
index 99a8ceee5d3..00000000000
--- a/vendor/github.com/a8m/envsubst/parse/env.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package parse
-
-import (
-	"strings"
-)
-
-type Env []string
-
-func (e Env) Get(name string) string {
-	v, _ := e.Lookup(name)
-	return v
-}
-
-func (e Env) Has(name string) bool {
-	_, ok := e.Lookup(name)
-	return ok
-}
-
-func (e Env) Lookup(name string) (string, bool) {
-	prefix := name + "="
-	for _, pair := range e {
-		if strings.HasPrefix(pair, prefix) {
-			return pair[len(prefix):], true
-		}
-	}
-	return "", false
-}
diff --git a/vendor/github.com/a8m/envsubst/parse/lex.go b/vendor/github.com/a8m/envsubst/parse/lex.go
deleted file mode 100644
index 5cd4c4699eb..00000000000
--- a/vendor/github.com/a8m/envsubst/parse/lex.go
+++ /dev/null
@@ -1,269 +0,0 @@
-package parse
-
-import (
-	"fmt"
-	"strings"
-	"unicode"
-	"unicode/utf8"
-)
-
-// itemType identifies the type of lex items.
-type itemType int
-
-// Pos represents a byte position in the original input text from which
-// this template was parsed.
-type Pos int
-
-// item represents a token or text string returned from the scanner.
-type item struct {
-	typ itemType // The type of this item.
-	pos Pos      // The starting position, in bytes, of this item in the input string.
-	val string   // The value of this item.
-}
-
-func (i item) String() string {
-	typ := "OP"
-	if t, ok := tokens[i.typ]; ok {
-		typ = t
-	}
-	return fmt.Sprintf("%s: %.40q", typ, i.val)
-}
-
-const (
-	eof                = -1
-	itemError itemType = iota // error occurred; value is text of error
-	itemEOF
-	itemText        // plain text
-	itemPlus        // plus('+')
-	itemDash        // dash('-')
-	itemEquals      // equals
-	itemColonEquals // colon-equals (':=')
-	itemColonDash   // colon-dash(':-')
-	itemColonPlus   // colon-plus(':+')
-	itemVariable    // variable starting with '$', such as '$hello' or '$1'
-	itemLeftDelim   // left action delimiter '${'
-	itemRightDelim  // right action delimiter '}'
-)
-
-var tokens = map[itemType]string{
-	itemEOF:        "EOF",
-	itemError:      "ERROR",
-	itemText:       "TEXT",
-	itemVariable:   "VAR",
-	itemLeftDelim:  "START EXP",
-	itemRightDelim: "END EXP",
-}
-
-// stateFn represents the state of the lexer as a function that returns the next state.
-type stateFn func(*lexer) stateFn
-
-// lexer holds the state of the scanner
-type lexer struct {
-	input     string    // the string being lexed
-	state     stateFn   // the next lexing function to enter
-	pos       Pos       // current position in the input
-	start     Pos       // start position of this item
-	width     Pos       // width of last rune read from input
-	lastPos   Pos       // position of most recent item returned by nextItem
-	items     chan item // channel of lexed items
-	subsDepth int       // depth of substitution
-	noDigit   bool      // if the lexer skips variables that start with a digit
-}
-
-// next returns the next rune in the input.
-func (l *lexer) next() rune {
-	if int(l.pos) >= len(l.input) {
-		l.width = 0
-		return eof
-	}
-	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
-	l.width = Pos(w)
-	l.pos += l.width
-	return r
-}
-
-// peek returns but does not consume the next rune in the input.
-func (l *lexer) peek() rune {
-	r := l.next()
-	l.backup()
-	return r
-}
-
-// backup steps back one rune. Can only be called once per call of next.
-func (l *lexer) backup() {
-	l.pos -= l.width
-}
-
-// emit passes an item back to the client.
-func (l *lexer) emit(t itemType) {
-	l.items <- item{t, l.start, l.input[l.start:l.pos]}
-	l.lastPos = l.start
-	l.start = l.pos
-}
-
-// ignore skips over the pending input before this point.
-func (l *lexer) ignore() {
-	l.start = l.pos
-}
-
-// errorf returns an error token and terminates the scan by passing
-// back a nil pointer that will be the next state, terminating l.nextItem.
-func (l *lexer) errorf(format string, args ...interface{}) stateFn {
-	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
-	return nil
-}
-
-// nextItem returns the next item from the input.
-// Called by the parser, not in the lexing goroutine.
-func (l *lexer) nextItem() item {
-	item := <-l.items
-	return item
-}
-
-// lex creates a new scanner for the input string.
-func lex(input string, noDigit bool) *lexer {
-	l := &lexer{
-		input:   input,
-		items:   make(chan item),
-		noDigit: noDigit,
-	}
-	go l.run()
-	return l
-}
-
-// run runs the state machine for the lexer.
-func (l *lexer) run() {
-	for l.state = lexText; l.state != nil; {
-		l.state = l.state(l)
-	}
-	close(l.items)
-}
-
-// lexText scans until encountering with "$" or an opening action delimiter, "${".
-func lexText(l *lexer) stateFn {
-Loop:
-	for {
-		switch r := l.next(); r {
-		case '$':
-			l.pos--
-			// emit the text we've found until here, if any.
-			if l.pos > l.start {
-				l.emit(itemText)
-			}
-			l.pos++
-			switch r := l.peek(); {
-			case l.noDigit && unicode.IsDigit(r):
-				// ignore variable starting with digit like $1.
-				l.next()
-				l.emit(itemText)
-			case r == '$':
-				// ignore the previous '$'.
-				l.ignore()
-				l.next()
-				l.emit(itemText)
-			case r == '{':
-				l.next()
-				r2 := l.peek()
-				if l.noDigit && unicode.IsDigit(r2) {
-					// ignore variable starting with digit like ${1}.
-					l.next()
-					l.emit(itemText)
-					break
-				}
-				l.subsDepth++
-				l.emit(itemLeftDelim)
-				return lexSubstitutionOperator
-			case isAlphaNumeric(r):
-				return lexVariable
-			}
-		case eof:
-			break Loop
-		}
-	}
-	// Correctly reached EOF.
-	if l.pos > l.start {
-		l.emit(itemText)
-	}
-	l.emit(itemEOF)
-	return nil
-}
-
-// lexVariable scans a Variable: $Alphanumeric.
-// The $ has been scanned.
-func lexVariable(l *lexer) stateFn {
-	var r rune
-	for {
-		r = l.next()
-		if !isAlphaNumeric(r) {
-			l.backup()
-			break
-		}
-	}
-	if v := l.input[l.start:l.pos]; v == "_" || v == "$_" {
-		return lexText
-	}
-	l.emit(itemVariable)
-	if l.subsDepth > 0 {
-		return lexSubstitutionOperator
-	}
-	return lexText
-}
-
-// lexSubstitutionOperator scans a starting substitution operator (if any) and continues with lexSubstitution
-func lexSubstitutionOperator(l *lexer) stateFn {
-	switch r := l.next(); {
-	case r == '}':
-		l.subsDepth--
-		l.emit(itemRightDelim)
-		return lexText
-	case r == eof || isEndOfLine(r):
-		return l.errorf("closing brace expected")
-	case isAlphaNumeric(r) && strings.HasPrefix(l.input[l.lastPos:], "${"):
-		return lexVariable
-	case r == '+':
-		l.emit(itemPlus)
-	case r == '-':
-		l.emit(itemDash)
-	case r == '=':
-		l.emit(itemEquals)
-	case r == ':':
-		switch l.next() {
-		case '-':
-			l.emit(itemColonDash)
-		case '=':
-			l.emit(itemColonEquals)
-		case '+':
-			l.emit(itemColonPlus)
-		}
-	}
-	return lexSubstitution
-}
-
-// lexSubstitution scans the elements inside substitution delimiters.
-func lexSubstitution(l *lexer) stateFn {
-	switch r := l.next(); {
-	case r == '}':
-		l.subsDepth--
-		l.emit(itemRightDelim)
-		return lexText
-	case r == eof || isEndOfLine(r):
-		return l.errorf("closing brace expected")
-	case isAlphaNumeric(r) && strings.HasPrefix(l.input[l.lastPos:], "${"):
-		fallthrough
-	case r == '$':
-		return lexVariable
-	default:
-		l.emit(itemText)
-	}
-	return lexSubstitution
-}
-
-// isEndOfLine reports whether r is an end-of-line character.
-func isEndOfLine(r rune) bool {
-	return r == '\r' || r == '\n'
-}
-
-// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
-func isAlphaNumeric(r rune) bool {
-	return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
-}
diff --git a/vendor/github.com/a8m/envsubst/parse/node.go b/vendor/github.com/a8m/envsubst/parse/node.go
deleted file mode 100644
index e566d705578..00000000000
--- a/vendor/github.com/a8m/envsubst/parse/node.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package parse
-
-import (
-	"fmt"
-)
-
-type Node interface {
-	Type() NodeType
-	String() (string, error)
-}
-
-// NodeType identifies the type of a node.
-type NodeType int
-
-// Type returns itself and provides an easy default implementation
-// for embedding in a Node. Embedded in all non-trivial Nodes.
-func (t NodeType) Type() NodeType {
-	return t
-}
-
-const (
-	NodeText NodeType = iota
-	NodeSubstitution
-	NodeVariable
-)
-
-type TextNode struct {
-	NodeType
-	Text string
-}
-
-func NewText(text string) *TextNode {
-	return &TextNode{NodeText, text}
-}
-
-func (t *TextNode) String() (string, error) {
-	return t.Text, nil
-}
-
-type VariableNode struct {
-	NodeType
-	Ident    string
-	Env      Env
-	Restrict *Restrictions
-}
-
-func NewVariable(ident string, env Env, restrict *Restrictions) *VariableNode {
-	return &VariableNode{NodeVariable, ident, env, restrict}
-}
-
-func (t *VariableNode) String() (string, error) {
-	if err := t.validateNoUnset(); err != nil {
-		return "", err
-	}
-	value := t.Env.Get(t.Ident)
-	if err := t.validateNoEmpty(value); err != nil {
-		return "", err
-	}
-	return value, nil
-}
-
-func (t *VariableNode) isSet() bool {
-	return t.Env.Has(t.Ident)
-}
-
-func (t *VariableNode) validateNoUnset() error {
-	if t.Restrict.NoUnset && !t.isSet() {
-		return fmt.Errorf("variable ${%s} not set", t.Ident)
-	}
-	return nil
-}
-
-func (t *VariableNode) validateNoEmpty(value string) error {
-	if t.Restrict.NoEmpty && value == "" && t.isSet() {
-		return fmt.Errorf("variable ${%s} set but empty", t.Ident)
-	}
-	return nil
-}
-
-type SubstitutionNode struct {
-	NodeType
-	ExpType  itemType
-	Variable *VariableNode
-	Default  Node // Default could be variable or text
-}
-
-func (t *SubstitutionNode) String() (string, error) {
-	if t.ExpType >= itemPlus && t.Default != nil {
-		switch t.ExpType {
-		case itemColonDash, itemColonEquals:
-			if s, _ := t.Variable.String(); s != "" {
-				return s, nil
-			}
-			return t.Default.String()
-		case itemPlus, itemColonPlus:
-			if t.Variable.isSet() {
-				return t.Default.String()
-			}
-			return "", nil
-		default:
-			if !t.Variable.isSet() {
-				return t.Default.String()
-			}
-		}
-	}
-	return t.Variable.String()
-}
diff --git a/vendor/github.com/a8m/envsubst/parse/parse.go b/vendor/github.com/a8m/envsubst/parse/parse.go
deleted file mode 100644
index 8b9b427a5d1..00000000000
--- a/vendor/github.com/a8m/envsubst/parse/parse.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// Most of the code in this package taken from golang/text/template/parse
-package parse
-
-import (
-	"errors"
-	"strings"
-)
-
-// A mode value is a set of flags (or 0). They control parser behavior.
-type Mode int
-
-// Mode for parser behaviour
-const (
-	Quick     Mode = iota // stop parsing after first error encoutered and return
-	AllErrors             // report all errors
-)
-
-// The restrictions option controls the parsring restriction.
-type Restrictions struct {
-	NoUnset bool
-	NoEmpty bool
-	NoDigit bool
-}
-
-// Restrictions specifier
-var (
-	Relaxed = &Restrictions{false, false, false}
-	NoEmpty = &Restrictions{false, true, false}
-	NoUnset = &Restrictions{true, false, false}
-	Strict  = &Restrictions{true, true, false}
-)
-
-// Parser type initializer
-type Parser struct {
-	Name     string // name of the processing template
-	Env      Env
-	Restrict *Restrictions
-	Mode     Mode
-	// parsing state;
-	lex       *lexer
-	token     [3]item // three-token lookahead
-	peekCount int
-	nodes     []Node
-}
-
-// New allocates a new Parser with the given name.
-func New(name string, env []string, r *Restrictions) *Parser {
-	return &Parser{
-		Name:     name,
-		Env:      Env(env),
-		Restrict: r,
-	}
-}
-
-// Parse parses the given string.
-func (p *Parser) Parse(text string) (string, error) {
-	p.lex = lex(text, p.Restrict.NoDigit)
-	// Build internal array of all unset or empty vars here
-	var errs []error
-	// clean parse state
-	p.nodes = make([]Node, 0)
-	p.peekCount = 0
-	if err := p.parse(); err != nil {
-		switch p.Mode {
-		case Quick:
-			return "", err
-		case AllErrors:
-			errs = append(errs, err)
-		}
-	}
-	var out string
-	for _, node := range p.nodes {
-		s, err := node.String()
-		if err != nil {
-			switch p.Mode {
-			case Quick:
-				return "", err
-			case AllErrors:
-				errs = append(errs, err)
-			}
-		}
-		out += s
-	}
-	if len(errs) > 0 {
-		var b strings.Builder
-		for i, err := range errs {
-			if i > 0 {
-				b.WriteByte('\n')
-			}
-			b.WriteString(err.Error())
-		}
-		return "", errors.New(b.String())
-	}
-	return out, nil
-}
-
-// parse is the top-level parser for the template.
-// It runs to EOF and return an error if something isn't right.
-func (p *Parser) parse() error {
-Loop:
-	for {
-		switch t := p.next(); t.typ {
-		case itemEOF:
-			break Loop
-		case itemError:
-			return p.errorf(t.val)
-		case itemVariable:
-			varNode := NewVariable(strings.TrimPrefix(t.val, "$"), p.Env, p.Restrict)
-			p.nodes = append(p.nodes, varNode)
-		case itemLeftDelim:
-			if p.peek().typ == itemVariable {
-				n, err := p.action()
-				if err != nil {
-					return err
-				}
-				p.nodes = append(p.nodes, n)
-				continue
-			}
-			fallthrough
-		default:
-			textNode := NewText(t.val)
-			p.nodes = append(p.nodes, textNode)
-		}
-	}
-	return nil
-}
-
-// Parse substitution. first item is a variable.
-func (p *Parser) action() (Node, error) {
-	var expType itemType
-	var defaultNode Node
-	varNode := NewVariable(p.next().val, p.Env, p.Restrict)
-Loop:
-	for {
-		switch t := p.next(); t.typ {
-		case itemRightDelim:
-			break Loop
-		case itemError:
-			return nil, p.errorf(t.val)
-		case itemVariable:
-			defaultNode = NewVariable(strings.TrimPrefix(t.val, "$"), p.Env, p.Restrict)
-		case itemText:
-			n := NewText(t.val)
-		Text:
-			for {
-				switch p.peek().typ {
-				case itemRightDelim, itemError, itemEOF:
-					break Text
-				default:
-					// patch to accept all kind of chars
-					n.Text += p.next().val
-				}
-			}
-			defaultNode = n
-		default:
-			expType = t.typ
-		}
-	}
-	return &SubstitutionNode{NodeSubstitution, expType, varNode, defaultNode}, nil
-}
-
-func (p *Parser) errorf(s string) error {
-	return errors.New(s)
-}
-
-// next returns the next token.
-func (p *Parser) next() item {
-	if p.peekCount > 0 {
-		p.peekCount--
-	} else {
-		p.token[0] = p.lex.nextItem()
-	}
-	return p.token[p.peekCount]
-}
-
-// backup backs the input stream up one token.
-func (p *Parser) backup() {
-	p.peekCount++
-}
-
-// peek returns but does not consume the next token.
-func (p *Parser) peek() item {
-	if p.peekCount > 0 {
-		return p.token[p.peekCount-1]
-	}
-	p.peekCount = 1
-	p.token[0] = p.lex.nextItem()
-	return p.token[0]
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 48350ce4180..9eea9ca86b8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -69,10 +69,6 @@ github.com/ProtonMail/go-crypto/openpgp/s2k
 github.com/RoaringBitmap/roaring
 github.com/RoaringBitmap/roaring/internal
 github.com/RoaringBitmap/roaring/roaring64
-# github.com/a8m/envsubst v1.4.2
-## explicit; go 1.17
-github.com/a8m/envsubst
-github.com/a8m/envsubst/parse
 # github.com/agnivade/levenshtein v1.1.1
 ## explicit; go 1.13
 github.com/agnivade/levenshtein


Test patch:
diff --git a/ocis-pkg/config/helpers_test.go b/ocis-pkg/config/helpers_test.go
index 447cdda4582..7b737870384 100644
--- a/ocis-pkg/config/helpers_test.go
+++ b/ocis-pkg/config/helpers_test.go
@@ -25,7 +25,7 @@ c: "${CODE_VAR|code}"
 	}
 	// perform test
 	c := TestConfig{}
-	_, err := bindSourcesToStructs(fs, filePath, "foo", &c)
+	err := bindSourcesToStructs(fs, filePath, "foo", &c)
 	if err != nil {
 		t.Error(err)
 	}
@@ -41,7 +41,7 @@ func TestBindSourcesToStructs_UnknownFile(t *testing.T) {
 	fs := fstest.MapFS{}
 	// perform test
 	c := TestConfig{}
-	_, err := bindSourcesToStructs(fs, filePath, "foo", &c)
+	err := bindSourcesToStructs(fs, filePath, "foo", &c)
 	if err != nil {
 		t.Error(err)
 	}
@@ -179,7 +179,7 @@ clientlog:
 	}
 	// perform test
 	c := Config{}
-	_, err := bindSourcesToStructs(fs, filePath, "foo", &c)
+	err := bindSourcesToStructs(fs, filePath, "foo", &c)
 	if err != nil {
 		t.Error(err)
 	}
diff --git a/services/proxy/pkg/middleware/security_test.go b/services/proxy/pkg/middleware/security_test.go
new file mode 100644
index 00000000000..5e6fc2cfcc3
--- /dev/null
+++ b/services/proxy/pkg/middleware/security_test.go
@@ -0,0 +1,27 @@
+package middleware
+
+import (
+	"gotest.tools/v3/assert"
+	"testing"
+)
+
+func TestLoadCSPConfig(t *testing.T) {
+	// setup test env
+	yaml := `
+directives:
+  frame-src:
+    - '''self'''
+    - 'https://embed.diagrams.net/'
+    - 'https://${ONLYOFFICE_DOMAIN|onlyoffice.owncloud.test}/'
+    - 'https://${COLLABORA_DOMAIN|collabora.owncloud.test}/'
+`
+
+	config, err := loadCSPConfig([]byte(yaml))
+	if err != nil {
+		t.Error(err)
+	}
+	assert.Equal(t, config.Directives["frame-src"][0], "'self'")
+	assert.Equal(t, config.Directives["frame-src"][1], "https://embed.diagrams.net/")
+	assert.Equal(t, config.Directives["frame-src"][2], "https://onlyoffice.owncloud.test/")
+	assert.Equal(t, config.Directives["frame-src"][3], "https://collabora.owncloud.test/")
+}