mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 14:31:02 +00: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) { | ||||
| 			ctx.Redirect(setting.AppSubURL + "/user/settings/account") | ||||
| 		}) | ||||
| 		m.Get("/security.txt", securityTxt) | ||||
| 	}) | ||||
| 
 | ||||
| 	m.Group("/explore", func() { | ||||
|  |  | |||
|  | @ -38,6 +38,7 @@ func TestLinksNoLogin(t *testing.T) { | |||
| 		"/user2/repo1/projects/1", | ||||
| 		"/assets/img/404.png", | ||||
| 		"/assets/img/500.png", | ||||
| 		"/.well-known/security.txt", | ||||
| 	} | ||||
| 
 | ||||
| 	for _, link := range links { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue