From 0d33c64372fb7417e1590518fce7efc3eb4cd3a7 Mon Sep 17 00:00:00 2001 From: Catherine Date: Mon, 19 Jan 2026 02:25:01 +0000 Subject: [PATCH] [breaking-change] Only allow a single `[[wildcard]].index-repo`. The git-pages webhook security model depends on there being a 1:1 mapping between site URLs and repositories; being able to specify multiple of them breaks this model, as anyone could switch the published site from one to the other if both repositories exist. --- conf/config.example.toml | 2 +- src/auth.go | 35 +++++++++++++++-------------------- src/config.go | 10 +++++----- src/wildcard.go | 35 ++++++++++++++--------------------- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/conf/config.example.toml b/conf/config.example.toml index a2c76b7..9af9ead 100644 --- a/conf/config.example.toml +++ b/conf/config.example.toml @@ -12,7 +12,7 @@ metrics = "tcp/localhost:3002" [[wildcard]] # non-default section domain = "codeberg.page" clone-url = "https://codeberg.org//.git" -index-repos = [".codeberg.page", "pages"] +index-repo = "pages" index-repo-branch = "main" authorization = "forgejo" diff --git a/src/auth.go b/src/auth.go index fd9d410..eb9ad2f 100644 --- a/src/auth.go +++ b/src/auth.go @@ -265,8 +265,8 @@ func authorizeWildcardMatchSite(r *http.Request, pattern *WildcardPattern) (*Aut } if userName, found := pattern.Matches(host); found { - repoURLs, branch := pattern.ApplyTemplate(userName, projectName) - return &Authorization{repoURLs, branch}, nil + repoURL, branch := pattern.ApplyTemplate(userName, projectName) + return &Authorization{[]string{repoURL}, branch}, nil } else { return nil, AuthError{ http.StatusUnauthorized, @@ -632,25 +632,20 @@ func authorizeForgeWithToken(r *http.Request) (*Authorization, error) { } if userName, found := pattern.Matches(host); found { - repoURLs, branch := pattern.ApplyTemplate(userName, projectName) - for _, repoURL := range repoURLs { - parsedRepoURL, err := url.Parse(repoURL) - if err != nil { - panic(err) // misconfiguration - } - - if err = checkGogsRepositoryPushPermission(parsedRepoURL, authorization); err != nil { - errs = append(errs, err) - continue - } - - // This will actually be ignored by the caller of AuthorizeUpdateFromArchive, - // but we return this information as it makes sense to do contextually here. - return &Authorization{ - []string{repoURL}, - branch, - }, nil + repoURL, branch := pattern.ApplyTemplate(userName, projectName) + parsedRepoURL, err := url.Parse(repoURL) + if err != nil { + panic(err) // misconfiguration } + + if err = checkGogsRepositoryPushPermission(parsedRepoURL, authorization); err != nil { + errs = append(errs, err) + continue + } + + // This will actually be ignored by the caller of AuthorizeUpdateFromArchive, + // but we return this information as it makes sense to do contextually here. + return &Authorization{[]string{repoURL}, branch}, nil } } diff --git a/src/config.go b/src/config.go index 6df7a19..586a968 100644 --- a/src/config.go +++ b/src/config.go @@ -79,11 +79,11 @@ type ServerConfig struct { } type WildcardConfig struct { - Domain string `toml:"domain"` - CloneURL string `toml:"clone-url"` // URL template, not an exact URL - IndexRepos []string `toml:"index-repos" default:"[]"` - IndexRepoBranch string `toml:"index-repo-branch" default:"pages"` - Authorization string `toml:"authorization"` + Domain string `toml:"domain"` + CloneURL string `toml:"clone-url"` // URL template, not an exact URL + IndexRepo string `toml:"index-repo" default:"pages"` + IndexRepoBranch string `toml:"index-repo-branch" default:"pages"` + Authorization string `toml:"authorization"` } type FallbackConfig struct { diff --git a/src/wildcard.go b/src/wildcard.go index 66feb92..36398f7 100644 --- a/src/wildcard.go +++ b/src/wildcard.go @@ -11,7 +11,7 @@ import ( type WildcardPattern struct { Domain []string CloneURL *fasttemplate.Template - IndexRepos []*fasttemplate.Template + IndexRepo *fasttemplate.Template IndexBranch string Authorization bool } @@ -49,27 +49,24 @@ func (pattern *WildcardPattern) Matches(host string) (string, bool) { return subdomain, true } -func (pattern *WildcardPattern) ApplyTemplate(userName string, projectName string) ([]string, string) { - var repoURLs []string +func (pattern *WildcardPattern) ApplyTemplate(userName string, projectName string) (string, string) { + var repoURL string var branch string repoURLTemplate := pattern.CloneURL if projectName == ".index" { - for _, indexRepoTemplate := range pattern.IndexRepos { - indexRepo := indexRepoTemplate.ExecuteString(map[string]any{"user": userName}) - repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]any{ - "user": userName, - "project": indexRepo, - })) - } + repoURL = repoURLTemplate.ExecuteString(map[string]any{ + "user": userName, + "project": pattern.IndexRepo.ExecuteString(map[string]any{"user": userName}), + }) branch = pattern.IndexBranch } else { - repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]any{ + repoURL = repoURLTemplate.ExecuteString(map[string]any{ "user": userName, "project": projectName, - })) + }) branch = "pages" } - return repoURLs, branch + return repoURL, branch } func TranslateWildcards(configs []WildcardConfig) ([]*WildcardPattern, error) { @@ -80,14 +77,10 @@ func TranslateWildcards(configs []WildcardConfig) ([]*WildcardPattern, error) { return nil, fmt.Errorf("wildcard pattern: clone URL: %w", err) } - var indexRepoTemplates []*fasttemplate.Template var indexRepoBranch string = config.IndexRepoBranch - for _, indexRepo := range config.IndexRepos { - indexRepoTemplate, err := fasttemplate.NewTemplate(indexRepo, "<", ">") - if err != nil { - return nil, fmt.Errorf("wildcard pattern: index repo: %w", err) - } - indexRepoTemplates = append(indexRepoTemplates, indexRepoTemplate) + indexRepoTemplate, err := fasttemplate.NewTemplate(config.IndexRepo, "<", ">") + if err != nil { + return nil, fmt.Errorf("wildcard pattern: index repo: %w", err) } authorization := false @@ -107,7 +100,7 @@ func TranslateWildcards(configs []WildcardConfig) ([]*WildcardPattern, error) { wildcardPatterns = append(wildcardPatterns, &WildcardPattern{ Domain: strings.Split(config.Domain, "."), CloneURL: cloneURLTemplate, - IndexRepos: indexRepoTemplates, + IndexRepo: indexRepoTemplate, IndexBranch: indexRepoBranch, Authorization: authorization, })