@ -6,6 +6,7 @@ package agent
import (
"encoding/json"
"fmt"
"github.com/hashicorp/go-hclog"
"io"
"net"
"net/http"
@ -43,6 +44,11 @@ import (
"github.com/hashicorp/consul/proto/private/pbcommon"
)
const (
contentTypeHeader = "Content-Type"
plainContentType = "text/plain; charset=utf-8"
)
var HTTPSummaries = [ ] prometheus . SummaryDefinition {
{
Name : [ ] string { "api" , "http" } ,
@ -220,6 +226,7 @@ func (s *HTTPHandlers) handler() http.Handler {
// If enableDebug register wrapped pprof handlers
if ! s . agent . enableDebug . Load ( ) && s . checkACLDisabled ( ) {
resp . WriteHeader ( http . StatusNotFound )
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
return
}
@ -228,6 +235,7 @@ func (s *HTTPHandlers) handler() http.Handler {
authz , err := s . agent . delegate . ResolveTokenAndDefaultMeta ( token , nil , nil )
if err != nil {
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
resp . WriteHeader ( http . StatusForbidden )
return
}
@ -237,6 +245,7 @@ func (s *HTTPHandlers) handler() http.Handler {
// TODO(partitions): should this be possible in a partition?
// TODO(acl-error-enhancements): We should return error details somehow here.
if authz . OperatorRead ( nil ) != acl . Allow {
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
resp . WriteHeader ( http . StatusForbidden )
return
}
@ -317,6 +326,8 @@ func (s *HTTPHandlers) handler() http.Handler {
}
h = withRemoteAddrHandler ( h )
h = ensureContentTypeHeader ( h , s . agent . logger )
s . h = & wrappedMux {
mux : mux ,
handler : h ,
@ -337,6 +348,20 @@ func withRemoteAddrHandler(next http.Handler) http.Handler {
} )
}
// Injects content type explicitly if not already set into response to prevent XSS
func ensureContentTypeHeader ( next http . Handler , logger hclog . Logger ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
next . ServeHTTP ( resp , req )
val := resp . Header ( ) . Get ( contentTypeHeader )
if val == "" {
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
logger . Debug ( "warning: content-type header not explicitly set." , "request-path" , req . URL )
}
} )
}
// nodeName returns the node name of the agent
func ( s * HTTPHandlers ) nodeName ( ) string {
return s . agent . config . NodeName
@ -380,6 +405,8 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
"from" , req . RemoteAddr ,
"error" , err ,
)
//set response type to plain to prevent XSS
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
resp . WriteHeader ( http . StatusInternalServerError )
return
}
@ -406,6 +433,8 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
"from" , req . RemoteAddr ,
"error" , errMsg ,
)
//set response type to plain to prevent XSS
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
resp . WriteHeader ( http . StatusForbidden )
fmt . Fprint ( resp , errMsg )
return
@ -585,6 +614,8 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
resp . Header ( ) . Add ( "X-Consul-Reason" , errPayload . Reason )
}
} else {
//set response type to plain to prevent XSS
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
handleErr ( err )
return
}
@ -596,6 +627,8 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
if contentType == "application/json" {
buf , err = s . marshalJSON ( req , obj )
if err != nil {
//set response type to plain to prevent XSS
resp . Header ( ) . Set ( contentTypeHeader , plainContentType )
handleErr ( err )
return
}
@ -606,7 +639,7 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
}
}
}
resp . Header ( ) . Set ( "Content-Type" , contentType )
resp . Header ( ) . Set ( contentTypeHeader , contentType )
resp . WriteHeader ( httpCode )
resp . Write ( buf )
}