mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-27 13:39:19 -05:00
[GITEA] add /.well-known/security.txt endpoint
resolves #38
adds RFC 9116 machine parsable
File Format to Aid in Security Vulnerability Disclosure
(cherry picked from commit 8ab1f8375c
)
This commit is contained in:
parent
33feed4723
commit
8f04f0e288
4 changed files with 83 additions and 0 deletions
24
routers/web/security_txt.go
Normal file
24
routers/web/security_txt.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2023 The Forgejo Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const securityTxtContent = `Contact: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/CONTRIBUTING.md
|
||||||
|
Contact: mailto:security@forgejo.org
|
||||||
|
Expires: 2025-06-25T00:00:00Z
|
||||||
|
Policy: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/CONTRIBUTING.md
|
||||||
|
Preferred-Languages: en
|
||||||
|
`
|
||||||
|
|
||||||
|
// returns /.well-known/security.txt content
|
||||||
|
// RFC 9116, https://www.rfc-editor.org/rfc/rfc9116
|
||||||
|
// https://securitytxt.org/
|
||||||
|
func securityTxt(ctx *context.Context) {
|
||||||
|
ctx.PlainText(http.StatusOK, securityTxtContent)
|
||||||
|
}
|
57
routers/web/security_txt_test.go
Normal file
57
routers/web/security_txt_test.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2023 The Forgejo Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func extractLines(message, pattern string) []string {
|
||||||
|
ptn := regexp.MustCompile(pattern)
|
||||||
|
return ptn.FindAllString(message, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecurityTxt(t *testing.T) {
|
||||||
|
// Contact: is required and value MUST be https:// or mailto:
|
||||||
|
{
|
||||||
|
contacts := extractLines(securityTxtContent, `(?m:^Contact: .+$)`)
|
||||||
|
if contacts == nil {
|
||||||
|
t.Error("Error: \"Contact: \" field is required")
|
||||||
|
}
|
||||||
|
for _, contact := range contacts {
|
||||||
|
match, err := regexp.MatchString("Contact: (https:)|(mailto:)", contact)
|
||||||
|
if !match {
|
||||||
|
t.Error("Error in line ", contact, "\n\"Contact:\" field have incorrect format")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error in line ", contact, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Expires is required
|
||||||
|
{
|
||||||
|
expires := extractLines(securityTxtContent, `(?m:^Expires: .+$)`)
|
||||||
|
if expires == nil {
|
||||||
|
t.Error("Error: \"Expires: \" field is required")
|
||||||
|
}
|
||||||
|
if len(expires) != 1 {
|
||||||
|
t.Error("Error: \"Expires: \" MUST be single")
|
||||||
|
}
|
||||||
|
expRe := regexp.MustCompile(`Expires: (.*)`)
|
||||||
|
expSlice := expRe.FindStringSubmatch(expires[0])
|
||||||
|
if len(expSlice) != 2 {
|
||||||
|
t.Error("Error: \"Expires: \" have no value")
|
||||||
|
}
|
||||||
|
expValue := expSlice[1]
|
||||||
|
expTime, err := time.Parse(time.RFC3339, expValue)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error parsing Expires value", expValue, err)
|
||||||
|
}
|
||||||
|
if time.Now().AddDate(0, 2, 0).After(expTime) {
|
||||||
|
t.Error("Error: Expires date time almost in the past", expTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -351,6 +351,7 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Get("/change-password", func(ctx *context.Context) {
|
m.Get("/change-password", func(ctx *context.Context) {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
})
|
})
|
||||||
|
m.Get("/security.txt", securityTxt)
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/explore", func() {
|
m.Group("/explore", func() {
|
||||||
|
|
|
@ -38,6 +38,7 @@ func TestLinksNoLogin(t *testing.T) {
|
||||||
"/user2/repo1/projects/1",
|
"/user2/repo1/projects/1",
|
||||||
"/assets/img/404.png",
|
"/assets/img/404.png",
|
||||||
"/assets/img/500.png",
|
"/assets/img/500.png",
|
||||||
|
"/.well-known/security.txt",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, link := range links {
|
for _, link := range links {
|
||||||
|
|
Loading…
Reference in a new issue