go vendor
This commit is contained in:
125
vendor/github.com/revel/modules/csrf/app/csrf.go
generated
vendored
Normal file
125
vendor/github.com/revel/modules/csrf/app/csrf.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
package csrf
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"html/template"
|
||||
"io"
|
||||
"math"
|
||||
"net/url"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
// allowMethods are HTTP methods that do NOT require a token
|
||||
var allowedMethods = map[string]bool{
|
||||
"GET": true,
|
||||
"HEAD": true,
|
||||
"OPTIONS": true,
|
||||
"TRACE": true,
|
||||
}
|
||||
|
||||
func RandomString(length int) (string, error) {
|
||||
buffer := make([]byte, int(math.Ceil(float64(length)/2)))
|
||||
if _, err := io.ReadFull(rand.Reader, buffer); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
str := hex.EncodeToString(buffer)
|
||||
return str[:length], nil
|
||||
}
|
||||
|
||||
func RefreshToken(c *revel.Controller) {
|
||||
token, err := RandomString(64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.Session["csrf_token"] = token
|
||||
}
|
||||
|
||||
// CsrfFilter enables CSRF request token creation and verification.
|
||||
//
|
||||
// Usage:
|
||||
// 1) Add `csrf.CsrfFilter` to the app's filters (it must come after the revel.SessionFilter).
|
||||
// 2) Add CSRF fields to a form with the template tag `{{ csrftoken . }}`. The filter adds a function closure to the `ViewArgs` that can pull out the secret and make the token as-needed, caching the value in the request. Ajax support provided through the `X-CSRFToken` header.
|
||||
func CsrfFilter(c *revel.Controller, fc []revel.Filter) {
|
||||
token, foundToken := c.Session["csrf_token"]
|
||||
|
||||
if !foundToken {
|
||||
RefreshToken(c)
|
||||
}
|
||||
|
||||
referer, refErr := url.Parse(c.Request.Referer())
|
||||
if refErr != nil {
|
||||
c.Result = c.Forbidden("REVEL CSRF: Unable to fetch referer")
|
||||
return
|
||||
}
|
||||
isSameOrigin := sameOrigin(c.Request.URL, referer)
|
||||
|
||||
// If the Request method isn't in the white listed methods
|
||||
if !allowedMethods[c.Request.Method] && !IsExempt(c) {
|
||||
// Token wasn't present at all
|
||||
if !foundToken {
|
||||
c.Result = c.Forbidden("REVEL CSRF: Session token missing.")
|
||||
return
|
||||
}
|
||||
|
||||
// Referer header is invalid
|
||||
if refErr != nil {
|
||||
c.Result = c.Forbidden("REVEL CSRF: HTTP Referer malformed.")
|
||||
return
|
||||
}
|
||||
|
||||
// Same origin
|
||||
if !isSameOrigin {
|
||||
c.Result = c.Forbidden("REVEL CSRF: Same origin mismatch.")
|
||||
return
|
||||
}
|
||||
|
||||
var requestToken string
|
||||
// First check for token in post data
|
||||
if c.Request.Method == "POST" {
|
||||
requestToken = c.Params.Get("csrftoken")
|
||||
}
|
||||
|
||||
// Then check for token in custom headers, as with AJAX
|
||||
if requestToken == "" {
|
||||
requestToken = c.Request.GetHttpHeader("X-CSRFToken")
|
||||
}
|
||||
|
||||
if requestToken == "" || !compareToken(requestToken, token) {
|
||||
c.Result = c.Forbidden("REVEL CSRF: Invalid token.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fc[0](c, fc[1:])
|
||||
|
||||
// Only add token to ViewArgs if the request is: not AJAX, not missing referer header, and is same origin.
|
||||
if c.Request.GetHttpHeader("X-CSRFToken") == "" && isSameOrigin {
|
||||
c.ViewArgs["_csrftoken"] = token
|
||||
}
|
||||
}
|
||||
|
||||
func compareToken(requestToken, token string) bool {
|
||||
// ConstantTimeCompare will panic if the []byte aren't the same length
|
||||
if len(requestToken) != len(token) {
|
||||
return false
|
||||
}
|
||||
return subtle.ConstantTimeCompare([]byte(requestToken), []byte(token)) == 1
|
||||
}
|
||||
|
||||
// Validates same origin policy
|
||||
func sameOrigin(u1, u2 *url.URL) bool {
|
||||
return u1.Scheme == u2.Scheme && u1.Host == u2.Host
|
||||
}
|
||||
|
||||
func init() {
|
||||
revel.TemplateFuncs["csrftoken"] = func(viewArgs map[string]interface{}) template.HTML {
|
||||
if tokenFunc, ok := viewArgs["_csrftoken"]; !ok {
|
||||
panic("REVEL CSRF: _csrftoken missing from ViewArgs.")
|
||||
} else {
|
||||
return template.HTML(tokenFunc.(string))
|
||||
}
|
||||
}
|
||||
}
|
183
vendor/github.com/revel/modules/csrf/app/csrf_test.go
generated
vendored
Normal file
183
vendor/github.com/revel/modules/csrf/app/csrf_test.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
package csrf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
func NewTestController(w http.ResponseWriter, r *http.Request) *revel.Controller{
|
||||
context := revel.NewGoContext(nil)
|
||||
context.Request.SetRequest(r)
|
||||
context.Response.SetResponse(w)
|
||||
c := revel.NewController(context)
|
||||
return c
|
||||
}
|
||||
|
||||
var testFilters = []revel.Filter{
|
||||
revel.ParamsFilter,
|
||||
CsrfFilter,
|
||||
func(c *revel.Controller, fc []revel.Filter) {
|
||||
c.RenderHTML("{{ csrftoken . }}")
|
||||
},
|
||||
}
|
||||
|
||||
func TestTokenInSession(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
getRequest, _ := http.NewRequest("GET", "http://www.example.com/", nil)
|
||||
c := NewTestController(resp,getRequest)
|
||||
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if _, ok := c.Session["csrf_token"]; !ok {
|
||||
t.Fatal("token should be present in session")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostWithoutToken(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/", nil)
|
||||
c := NewTestController(resp,postRequest)
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status != 403 {
|
||||
t.Fatal("post without token should be forbidden")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoReferrer(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/", nil)
|
||||
|
||||
c := NewTestController(resp,postRequest)
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
RefreshToken(c)
|
||||
token := c.Session["csrf_token"]
|
||||
|
||||
// make a new request with the token
|
||||
data := url.Values{}
|
||||
data.Set("csrftoken", token)
|
||||
formPostRequest, _ := http.NewRequest("POST", "http://www.example.com/", bytes.NewBufferString(data.Encode()))
|
||||
formPostRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
formPostRequest.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
|
||||
|
||||
cnew := NewTestController(resp,formPostRequest)
|
||||
// and replace the old request
|
||||
c.Request = cnew.Request
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status != 403 {
|
||||
t.Fatal("post without referer should be forbidden")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefererHttps(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/", nil)
|
||||
c := NewTestController(resp,postRequest)
|
||||
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
RefreshToken(c)
|
||||
token := c.Session["csrf_token"]
|
||||
|
||||
// make a new request with the token
|
||||
data := url.Values{}
|
||||
data.Set("csrftoken", token)
|
||||
formPostRequest, _ := http.NewRequest("POST", "https://www.example.com/", bytes.NewBufferString(data.Encode()))
|
||||
formPostRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
formPostRequest.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
|
||||
formPostRequest.Header.Add("Referer", "http://www.example.com/")
|
||||
|
||||
cnew := NewTestController(resp,formPostRequest)
|
||||
// and replace the old request
|
||||
c.Request = cnew.Request
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status != 403 {
|
||||
t.Fatal("posts to https should have an https referer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderWithToken(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/", nil)
|
||||
c := NewTestController(resp,postRequest)
|
||||
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
RefreshToken(c)
|
||||
token := c.Session["csrf_token"]
|
||||
|
||||
// make a new request with the token
|
||||
formPostRequest, _ := http.NewRequest("POST", "http://www.example.com/", nil)
|
||||
formPostRequest.Header.Add("X-CSRFToken", token)
|
||||
formPostRequest.Header.Add("Referer", "http://www.example.com/")
|
||||
|
||||
cnew := NewTestController(resp,formPostRequest)
|
||||
// and replace the old request
|
||||
c.Request = cnew.Request
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status == 403 {
|
||||
t.Fatal("post with http header token should be allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormPostWithToken(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/", nil)
|
||||
c := NewTestController(resp,postRequest)
|
||||
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
RefreshToken(c)
|
||||
token := c.Session["csrf_token"]
|
||||
|
||||
// make a new request with the token
|
||||
data := url.Values{}
|
||||
data.Set("csrftoken", token)
|
||||
formPostRequest, _ := http.NewRequest("POST", "http://www.example.com/", bytes.NewBufferString(data.Encode()))
|
||||
formPostRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
formPostRequest.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
|
||||
formPostRequest.Header.Add("Referer", "http://www.example.com/")
|
||||
|
||||
cnew := NewTestController(resp,formPostRequest)
|
||||
// and replace the old request
|
||||
c.Request = cnew.Request
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status == 403 {
|
||||
t.Fatal("form post with token should be allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoTokenInArgsWhenCORs(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
getRequest, _ := http.NewRequest("GET", "http://www.example1.com/", nil)
|
||||
getRequest.Header.Add("Referer", "http://www.example2.com/")
|
||||
|
||||
c := NewTestController(resp,getRequest)
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if _, ok := c.ViewArgs["_csrftoken"]; ok {
|
||||
t.Fatal("ViewArgs should not contain token when not same origin")
|
||||
}
|
||||
}
|
36
vendor/github.com/revel/modules/csrf/app/exempt.go
generated
vendored
Normal file
36
vendor/github.com/revel/modules/csrf/app/exempt.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package csrf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var (
|
||||
exemptPath = make(map[string]bool)
|
||||
exemptAction = make(map[string]bool)
|
||||
)
|
||||
|
||||
func MarkExempt(route string) {
|
||||
if strings.HasPrefix(route, "/") {
|
||||
// e.g. "/controller/action"
|
||||
exemptPath[strings.ToLower(route)] = true
|
||||
} else if routeParts := strings.Split(route, "."); len(routeParts) == 2 {
|
||||
// e.g. "ControllerName.ActionName"
|
||||
exemptAction[route] = true
|
||||
} else {
|
||||
err := fmt.Sprintf("csrf.MarkExempt() received invalid argument \"%v\". Either provide a path prefixed with \"/\" or controller action in the form of \"ControllerName.ActionName\".", route)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func IsExempt(c *revel.Controller) bool {
|
||||
if _, ok := exemptPath[strings.ToLower(c.Request.GetPath())]; ok {
|
||||
return true
|
||||
} else if _, ok := exemptAction[c.Action]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
55
vendor/github.com/revel/modules/csrf/app/exempt_test.go
generated
vendored
Normal file
55
vendor/github.com/revel/modules/csrf/app/exempt_test.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package csrf
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
func TestExemptPath(t *testing.T) {
|
||||
MarkExempt("/Controller/Action")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/Controller/Action", nil)
|
||||
c := NewTestController(resp, postRequest)
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status == 403 {
|
||||
t.Fatal("post to csrf exempt action should pass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExemptPathCaseInsensitive(t *testing.T) {
|
||||
MarkExempt("/Controller/Action")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/controller/action", nil)
|
||||
c := NewTestController(resp, postRequest)
|
||||
c.Session = make(revel.Session)
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status == 403 {
|
||||
t.Fatal("post to csrf exempt action should pass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExemptAction(t *testing.T) {
|
||||
MarkExempt("Controller.Action")
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
postRequest, _ := http.NewRequest("POST", "http://www.example.com/Controller/Action", nil)
|
||||
c := NewTestController(resp, postRequest)
|
||||
c.Session = make(revel.Session)
|
||||
c.Action = "Controller.Action"
|
||||
|
||||
testFilters[0](c, testFilters)
|
||||
|
||||
if c.Response.Status == 403 {
|
||||
t.Fatal("post to csrf exempt action should pass")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user