Revert getPathSuffixUnescaped (#13256)

This commit is contained in:
Chris S. Kim 2022-06-01 13:17:14 -04:00 committed by GitHub
parent e6dc26e087
commit 58ffa0488d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 66 additions and 272 deletions

3
.changelog/13256.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
agent: Fixed a bug in HTTP handlers where URLs were being decoded twice
```

View File

@ -122,10 +122,7 @@ func (s *HTTPHandlers) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
} }
policyID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/policy/") policyID := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/")
if err != nil {
return nil, err
}
if policyID == "" && req.Method != "PUT" { if policyID == "" && req.Method != "PUT" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing policy ID"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing policy ID"}
} }
@ -170,10 +167,7 @@ func (s *HTTPHandlers) ACLPolicyReadByName(resp http.ResponseWriter, req *http.R
return nil, aclDisabled return nil, aclDisabled
} }
policyName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/policy/name/") policyName := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/name/")
if err != nil {
return nil, err
}
if policyName == "" { if policyName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing policy Name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing policy Name"}
} }
@ -308,10 +302,7 @@ func (s *HTTPHandlers) ACLTokenCRUD(resp http.ResponseWriter, req *http.Request)
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
} }
tokenID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/token/") tokenID := strings.TrimPrefix(req.URL.Path, "/v1/acl/token/")
if err != nil {
return nil, err
}
if strings.HasSuffix(tokenID, "/clone") && req.Method == "PUT" { if strings.HasSuffix(tokenID, "/clone") && req.Method == "PUT" {
tokenID = tokenID[:len(tokenID)-6] tokenID = tokenID[:len(tokenID)-6]
fn = s.ACLTokenClone fn = s.ACLTokenClone
@ -541,10 +532,7 @@ func (s *HTTPHandlers) ACLRoleCRUD(resp http.ResponseWriter, req *http.Request)
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
} }
roleID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/role/") roleID := strings.TrimPrefix(req.URL.Path, "/v1/acl/role/")
if err != nil {
return nil, err
}
if roleID == "" && req.Method != "PUT" { if roleID == "" && req.Method != "PUT" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing role ID"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing role ID"}
} }
@ -557,10 +545,7 @@ func (s *HTTPHandlers) ACLRoleReadByName(resp http.ResponseWriter, req *http.Req
return nil, aclDisabled return nil, aclDisabled
} }
roleName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/role/name/") roleName := strings.TrimPrefix(req.URL.Path, "/v1/acl/role/name/")
if err != nil {
return nil, err
}
if roleName == "" { if roleName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing role Name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing role Name"}
} }
@ -711,10 +696,7 @@ func (s *HTTPHandlers) ACLBindingRuleCRUD(resp http.ResponseWriter, req *http.Re
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
} }
bindingRuleID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/binding-rule/") bindingRuleID := strings.TrimPrefix(req.URL.Path, "/v1/acl/binding-rule/")
if err != nil {
return nil, err
}
if bindingRuleID == "" && req.Method != "PUT" { if bindingRuleID == "" && req.Method != "PUT" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing binding rule ID"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing binding rule ID"}
} }
@ -857,10 +839,7 @@ func (s *HTTPHandlers) ACLAuthMethodCRUD(resp http.ResponseWriter, req *http.Req
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}} return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
} }
methodName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/auth-method/") methodName := strings.TrimPrefix(req.URL.Path, "/v1/acl/auth-method/")
if err != nil {
return nil, err
}
if methodName == "" && req.Method != "PUT" { if methodName == "" && req.Method != "PUT" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing auth method name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing auth method name"}
} }

View File

@ -382,10 +382,7 @@ func (s *HTTPHandlers) AgentServices(resp http.ResponseWriter, req *http.Request
// blocking watch using hash-based blocking. // blocking watch using hash-based blocking.
func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Get the proxy ID. Note that this is the ID of a proxy's service instance. // Get the proxy ID. Note that this is the ID of a proxy's service instance.
id, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/service/") id := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/")
if err != nil {
return nil, err
}
// Maybe block // Maybe block
var queryOpts structs.QueryOptions var queryOpts structs.QueryOptions
@ -404,7 +401,7 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request)
} }
// need to resolve to default the meta // need to resolve to default the meta
_, err = s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil) _, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -640,10 +637,7 @@ func (s *HTTPHandlers) AgentJoin(resp http.ResponseWriter, req *http.Request) (i
} }
// Get the address // Get the address
addr, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/join/") addr := strings.TrimPrefix(req.URL.Path, "/v1/agent/join/")
if err != nil {
return nil, err
}
if wan { if wan {
if s.agent.config.ConnectMeshGatewayWANFederationEnabled { if s.agent.config.ConnectMeshGatewayWANFederationEnabled {
@ -703,10 +697,7 @@ func (s *HTTPHandlers) AgentForceLeave(resp http.ResponseWriter, req *http.Reque
// Check if the WAN is being queried // Check if the WAN is being queried
_, wan := req.URL.Query()["wan"] _, wan := req.URL.Query()["wan"]
addr, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/force-leave/") addr := strings.TrimPrefix(req.URL.Path, "/v1/agent/force-leave/")
if err != nil {
return nil, err
}
if wan { if wan {
return nil, s.agent.ForceLeaveWAN(addr, prune, entMeta) return nil, s.agent.ForceLeaveWAN(addr, prune, entMeta)
} else { } else {
@ -792,11 +783,8 @@ func (s *HTTPHandlers) AgentRegisterCheck(resp http.ResponseWriter, req *http.Re
} }
func (s *HTTPHandlers) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
ID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/check/deregister/") id := strings.TrimPrefix(req.URL.Path, "/v1/agent/check/deregister/")
if err != nil { checkID := structs.NewCheckID(types.CheckID(id), nil)
return nil, err
}
checkID := structs.NewCheckID(types.CheckID(ID), nil)
// Get the provided token, if any, and vet against any ACL policies. // Get the provided token, if any, and vet against any ACL policies.
var token string var token string
@ -829,21 +817,15 @@ func (s *HTTPHandlers) AgentDeregisterCheck(resp http.ResponseWriter, req *http.
} }
func (s *HTTPHandlers) AgentCheckPass(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentCheckPass(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
ID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/check/pass/") id := strings.TrimPrefix(req.URL.Path, "/v1/agent/check/pass/")
if err != nil { checkID := types.CheckID(id)
return nil, err
}
checkID := types.CheckID(ID)
note := req.URL.Query().Get("note") note := req.URL.Query().Get("note")
return s.agentCheckUpdate(resp, req, checkID, api.HealthPassing, note) return s.agentCheckUpdate(resp, req, checkID, api.HealthPassing, note)
} }
func (s *HTTPHandlers) AgentCheckWarn(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentCheckWarn(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
ID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/check/warn/") id := strings.TrimPrefix(req.URL.Path, "/v1/agent/check/warn/")
if err != nil { checkID := types.CheckID(id)
return nil, err
}
checkID := types.CheckID(ID)
note := req.URL.Query().Get("note") note := req.URL.Query().Get("note")
return s.agentCheckUpdate(resp, req, checkID, api.HealthWarning, note) return s.agentCheckUpdate(resp, req, checkID, api.HealthWarning, note)
@ -851,11 +833,8 @@ func (s *HTTPHandlers) AgentCheckWarn(resp http.ResponseWriter, req *http.Reques
} }
func (s *HTTPHandlers) AgentCheckFail(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentCheckFail(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
ID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/check/fail/") id := strings.TrimPrefix(req.URL.Path, "/v1/agent/check/fail/")
if err != nil { checkID := types.CheckID(id)
return nil, err
}
checkID := types.CheckID(ID)
note := req.URL.Query().Get("note") note := req.URL.Query().Get("note")
return s.agentCheckUpdate(resp, req, checkID, api.HealthCritical, note) return s.agentCheckUpdate(resp, req, checkID, api.HealthCritical, note)
@ -890,12 +869,8 @@ func (s *HTTPHandlers) AgentCheckUpdate(resp http.ResponseWriter, req *http.Requ
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Invalid check status: '%s'", update.Status)} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Invalid check status: '%s'", update.Status)}
} }
ID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/check/update/") id := strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/")
if err != nil { checkID := types.CheckID(id)
return nil, err
}
checkID := types.CheckID(ID)
return s.agentCheckUpdate(resp, req, checkID, update.Status, update.Output) return s.agentCheckUpdate(resp, req, checkID, update.Status, update.Output)
} }
@ -977,10 +952,7 @@ func returnTextPlain(req *http.Request) bool {
// AgentHealthServiceByID return the local Service Health given its ID // AgentHealthServiceByID return the local Service Health given its ID
func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Pull out the service id (service id since there may be several instance of the same service on this host) // Pull out the service id (service id since there may be several instance of the same service on this host)
serviceID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/health/service/id/") serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/health/service/id/")
if err != nil {
return nil, err
}
if serviceID == "" { if serviceID == "" {
return nil, &HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing serviceID"} return nil, &HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing serviceID"}
} }
@ -1038,11 +1010,7 @@ func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *htt
// AgentHealthServiceByName return the worse status of all the services with given name on an agent // AgentHealthServiceByName return the worse status of all the services with given name on an agent
func (s *HTTPHandlers) AgentHealthServiceByName(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentHealthServiceByName(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Pull out the service name // Pull out the service name
serviceName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/health/service/name/") serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/health/service/name/")
if err != nil {
return nil, err
}
if serviceName == "" { if serviceName == "" {
return nil, &HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service Name"} return nil, &HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service Name"}
} }
@ -1247,11 +1215,7 @@ func (s *HTTPHandlers) AgentRegisterService(resp http.ResponseWriter, req *http.
} }
func (s *HTTPHandlers) AgentDeregisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentDeregisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
serviceID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/service/deregister/") serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/deregister/")
if err != nil {
return nil, err
}
sid := structs.NewServiceID(serviceID, nil) sid := structs.NewServiceID(serviceID, nil)
// Get the provided token, if any, and vet against any ACL policies. // Get the provided token, if any, and vet against any ACL policies.
@ -1287,11 +1251,7 @@ func (s *HTTPHandlers) AgentDeregisterService(resp http.ResponseWriter, req *htt
func (s *HTTPHandlers) AgentServiceMaintenance(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentServiceMaintenance(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Ensure we have a service ID // Ensure we have a service ID
serviceID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/service/maintenance/") serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/maintenance/")
if err != nil {
return nil, err
}
sid := structs.NewServiceID(serviceID, nil) sid := structs.NewServiceID(serviceID, nil)
if sid.ID == "" { if sid.ID == "" {
@ -1489,10 +1449,7 @@ func (s *HTTPHandlers) AgentToken(resp http.ResponseWriter, req *http.Request) (
} }
// Figure out the target token. // Figure out the target token.
target, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/token/") target := strings.TrimPrefix(req.URL.Path, "/v1/agent/token/")
if err != nil {
return nil, err
}
err = s.agent.tokens.WithPersistenceLock(func() error { err = s.agent.tokens.WithPersistenceLock(func() error {
triggerAntiEntropySync := false triggerAntiEntropySync := false
@ -1565,10 +1522,7 @@ func (s *HTTPHandlers) AgentConnectCARoots(resp http.ResponseWriter, req *http.R
func (s *HTTPHandlers) AgentConnectCALeafCert(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) AgentConnectCALeafCert(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Get the service name. Note that this is the name of the service, // Get the service name. Note that this is the name of the service,
// not the ID of the service instance. // not the ID of the service instance.
serviceName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/agent/connect/ca/leaf/") serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/connect/ca/leaf/")
if err != nil {
return nil, err
}
args := cachetype.ConnectCALeafRequest{ args := cachetype.ConnectCALeafRequest{
Service: serviceName, // Need name not ID Service: serviceName, // Need name not ID

View File

@ -3,6 +3,7 @@ package agent
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
metrics "github.com/armon/go-metrics" metrics "github.com/armon/go-metrics"
"github.com/armon/go-metrics/prometheus" "github.com/armon/go-metrics/prometheus"
@ -361,11 +362,7 @@ func (s *HTTPHandlers) catalogServiceNodes(resp http.ResponseWriter, req *http.R
} }
// Pull out the service name // Pull out the service name
var err error args.ServiceName = strings.TrimPrefix(req.URL.Path, pathPrefix)
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, pathPrefix)
if err != nil {
return nil, err
}
if args.ServiceName == "" { if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"}
} }
@ -436,11 +433,7 @@ func (s *HTTPHandlers) CatalogNodeServices(resp http.ResponseWriter, req *http.R
} }
// Pull out the node name // Pull out the node name
var err error args.Node = strings.TrimPrefix(req.URL.Path, "/v1/catalog/node/")
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/catalog/node/")
if err != nil {
return nil, err
}
if args.Node == "" { if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
} }
@ -501,11 +494,7 @@ func (s *HTTPHandlers) CatalogNodeServiceList(resp http.ResponseWriter, req *htt
} }
// Pull out the node name // Pull out the node name
var err error args.Node = strings.TrimPrefix(req.URL.Path, "/v1/catalog/node-services/")
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/catalog/node-services/")
if err != nil {
return nil, err
}
if args.Node == "" { if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
} }
@ -552,11 +541,7 @@ func (s *HTTPHandlers) CatalogGatewayServices(resp http.ResponseWriter, req *htt
} }
// Pull out the gateway's service name // Pull out the gateway's service name
var err error args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/catalog/gateway-services/")
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, "/v1/catalog/gateway-services/")
if err != nil {
return nil, err
}
if args.ServiceName == "" { if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"}
} }

View File

@ -33,10 +33,7 @@ func (s *HTTPHandlers) configGet(resp http.ResponseWriter, req *http.Request) (i
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil return nil, nil
} }
kindAndName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/config/") kindAndName := strings.TrimPrefix(req.URL.Path, "/v1/config/")
if err != nil {
return nil, err
}
pathArgs := strings.SplitN(kindAndName, "/", 2) pathArgs := strings.SplitN(kindAndName, "/", 2)
switch len(pathArgs) { switch len(pathArgs) {
@ -84,10 +81,7 @@ func (s *HTTPHandlers) configDelete(resp http.ResponseWriter, req *http.Request)
var args structs.ConfigEntryRequest var args structs.ConfigEntryRequest
s.parseDC(req, &args.Datacenter) s.parseDC(req, &args.Datacenter)
s.parseToken(req, &args.Token) s.parseToken(req, &args.Token)
kindAndName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/config/") kindAndName := strings.TrimPrefix(req.URL.Path, "/v1/config/")
if err != nil {
return nil, err
}
pathArgs := strings.SplitN(kindAndName, "/", 2) pathArgs := strings.SplitN(kindAndName, "/", 2)
if len(pathArgs) != 2 { if len(pathArgs) != 2 {

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"sort" "sort"
"strings"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
) )
@ -99,10 +100,7 @@ func (s *HTTPHandlers) CoordinateNode(resp http.ResponseWriter, req *http.Reques
return nil, err return nil, err
} }
node, err := getPathSuffixUnescaped(req.URL.Path, "/v1/coordinate/node/") node := strings.TrimPrefix(req.URL.Path, "/v1/coordinate/node/")
if err != nil {
return nil, err
}
args := structs.NodeSpecificRequest{Node: node} args := structs.NodeSpecificRequest{Node: node}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil return nil, nil

View File

@ -3,6 +3,7 @@ package agent
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -19,11 +20,7 @@ func (s *HTTPHandlers) DiscoveryChainRead(resp http.ResponseWriter, req *http.Re
return nil, nil return nil, nil
} }
var err error args.Name = strings.TrimPrefix(req.URL.Path, "/v1/discovery-chain/")
args.Name, err = getPathSuffixUnescaped(req.URL.Path, "/v1/discovery-chain/")
if err != nil {
return nil, err
}
if args.Name == "" { if args.Name == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing chain name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing chain name"}
} }

View File

@ -5,6 +5,7 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
@ -19,11 +20,7 @@ func (s *HTTPHandlers) EventFire(resp http.ResponseWriter, req *http.Request) (i
s.parseDC(req, &dc) s.parseDC(req, &dc)
event := &UserEvent{} event := &UserEvent{}
var err error event.Name = strings.TrimPrefix(req.URL.Path, "/v1/event/fire/")
event.Name, err = getPathSuffixUnescaped(req.URL.Path, "/v1/event/fire/")
if err != nil {
return nil, err
}
if event.Name == "" { if event.Name == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing name"}
} }

View File

@ -2,16 +2,14 @@ package agent
import ( import (
"net/http" "net/http"
"strings"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
) )
// GET /v1/internal/federation-state/<datacenter> // GET /v1/internal/federation-state/<datacenter>
func (s *HTTPHandlers) FederationStateGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) FederationStateGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
datacenterName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/internal/federation-state/") datacenterName := strings.TrimPrefix(req.URL.Path, "/v1/internal/federation-state/")
if err != nil {
return nil, err
}
if datacenterName == "" { if datacenterName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing datacenter name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing datacenter name"}
} }

View File

@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
@ -28,11 +29,7 @@ func (s *HTTPHandlers) HealthChecksInState(resp http.ResponseWriter, req *http.R
} }
// Pull out the service name // Pull out the service name
var err error args.State = strings.TrimPrefix(req.URL.Path, "/v1/health/state/")
args.State, err = getPathSuffixUnescaped(req.URL.Path, "/v1/health/state/")
if err != nil {
return nil, err
}
if args.State == "" { if args.State == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing check state"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing check state"}
} }
@ -76,11 +73,7 @@ func (s *HTTPHandlers) HealthNodeChecks(resp http.ResponseWriter, req *http.Requ
} }
// Pull out the service name // Pull out the service name
var err error args.Node = strings.TrimPrefix(req.URL.Path, "/v1/health/node/")
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/health/node/")
if err != nil {
return nil, err
}
if args.Node == "" { if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
} }
@ -126,11 +119,7 @@ func (s *HTTPHandlers) HealthServiceChecks(resp http.ResponseWriter, req *http.R
} }
// Pull out the service name // Pull out the service name
var err error args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/health/checks/")
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, "/v1/health/checks/")
if err != nil {
return nil, err
}
if args.ServiceName == "" { if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"}
} }
@ -222,11 +211,7 @@ func (s *HTTPHandlers) healthServiceNodes(resp http.ResponseWriter, req *http.Re
} }
// Pull out the service name // Pull out the service name
var err error args.ServiceName = strings.TrimPrefix(req.URL.Path, prefix)
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, prefix)
if err != nil {
return nil, err
}
if args.ServiceName == "" { if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"}
} }

View File

@ -1139,18 +1139,6 @@ func (s *HTTPHandlers) parseFilter(req *http.Request, filter *string) {
} }
} }
func getPathSuffixUnescaped(path string, prefixToTrim string) (string, error) {
// The suffix may be URL-encoded, so attempt to decode
suffixRaw := strings.TrimPrefix(path, prefixToTrim)
suffixUnescaped, err := url.PathUnescape(suffixRaw)
if err != nil {
return suffixRaw, fmt.Errorf("failure in unescaping path param %q: %v", suffixRaw, err)
}
return suffixUnescaped, nil
}
func setMetaProtobuf(resp http.ResponseWriter, queryMeta *pbcommon.QueryMeta) { func setMetaProtobuf(resp http.ResponseWriter, queryMeta *pbcommon.QueryMeta) {
qm := new(structs.QueryMeta) qm := new(structs.QueryMeta)
pbcommon.QueryMetaToStructs(queryMeta, qm) pbcommon.QueryMetaToStructs(queryMeta, qm)

View File

@ -1686,42 +1686,3 @@ func TestRPC_HTTPSMaxConnsPerClient(t *testing.T) {
}) })
} }
} }
func TestGetPathSuffixUnescaped(t *testing.T) {
t.Parallel()
cases := []struct {
name string
pathInput string
pathPrefix string
suffixResult string
errString string
}{
// No decoding required (resource name must be unaffected by the decode)
{"Normal Valid", "/foo/bar/resource-1", "/foo/bar/", "resource-1", ""},
// This function is not responsible for enforcing a valid URL, just for decoding escaped values.
// If there's an invalid URL segment in the path, it will be returned as is.
{"Unencoded Invalid", "/foo/bar/resource 1", "/foo/bar/", "resource 1", ""},
// Decode the encoded value properly
{"Encoded Valid", "/foo/bar/re%2Fsource%201", "/foo/bar/", "re/source 1", ""},
// Fail to decode an invalidly encoded input
{"Encoded Invalid", "/foo/bar/re%Fsource%201", "/foo/bar/", "re%Fsource%201", "failure in unescaping path param"},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
suffixResult, err := getPathSuffixUnescaped(tc.pathInput, tc.pathPrefix)
require.Equal(t, suffixResult, tc.suffixResult)
if tc.errString == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errString)
}
})
}
}

View File

@ -486,10 +486,7 @@ func parseIntentionStringComponent(input string, entMeta *acl.EnterpriseMeta) (s
// IntentionSpecific handles the endpoint for /v1/connect/intentions/:id. // IntentionSpecific handles the endpoint for /v1/connect/intentions/:id.
// Deprecated: use IntentionExact. // Deprecated: use IntentionExact.
func (s *HTTPHandlers) IntentionSpecific(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) IntentionSpecific(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
id, err := getPathSuffixUnescaped(req.URL.Path, "/v1/connect/intentions/") id := strings.TrimPrefix(req.URL.Path, "/v1/connect/intentions/")
if err != nil {
return nil, err
}
switch req.Method { switch req.Method {
case "GET": case "GET":

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
@ -19,11 +20,7 @@ func (s *HTTPHandlers) KVSEndpoint(resp http.ResponseWriter, req *http.Request)
} }
// Pull out the key name, validation left to each sub-handler // Pull out the key name, validation left to each sub-handler
var err error args.Key = strings.TrimPrefix(req.URL.Path, "/v1/kv/")
args.Key, err = getPathSuffixUnescaped(req.URL.Path, "/v1/kv/")
if err != nil {
return nil, err
}
// Check for a key list // Check for a key list
keyList := false keyList := false

View File

@ -3,6 +3,7 @@ package agent
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
@ -12,10 +13,7 @@ import (
// PeeringEndpoint handles GET, DELETE on v1/peering/name // PeeringEndpoint handles GET, DELETE on v1/peering/name
func (s *HTTPHandlers) PeeringEndpoint(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPHandlers) PeeringEndpoint(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
name, err := getPathSuffixUnescaped(req.URL.Path, "/v1/peering/") name := strings.TrimPrefix(req.URL.Path, "/v1/peering/")
if err != nil {
return nil, err
}
if name == "" { if name == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Must specify a name to fetch."} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Must specify a name to fetch."}
} }

View File

@ -309,10 +309,7 @@ func (s *HTTPHandlers) PreparedQuerySpecific(resp http.ResponseWriter, req *http
} }
path := req.URL.Path path := req.URL.Path
id, err := getPathSuffixUnescaped(path, "/v1/query/") id := strings.TrimPrefix(path, "/v1/query/")
if err != nil {
return nil, err
}
switch { switch {
case strings.HasSuffix(path, "/execute"): case strings.HasSuffix(path, "/execute"):

View File

@ -3,6 +3,7 @@ package agent
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
@ -69,11 +70,7 @@ func (s *HTTPHandlers) SessionDestroy(resp http.ResponseWriter, req *http.Reques
} }
// Pull out the session id // Pull out the session id
var err error args.Session.ID = strings.TrimPrefix(req.URL.Path, "/v1/session/destroy/")
args.Session.ID, err = getPathSuffixUnescaped(req.URL.Path, "/v1/session/destroy/")
if err != nil {
return nil, err
}
if args.Session.ID == "" { if args.Session.ID == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing session"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing session"}
} }
@ -96,11 +93,7 @@ func (s *HTTPHandlers) SessionRenew(resp http.ResponseWriter, req *http.Request)
} }
// Pull out the session id // Pull out the session id
var err error args.SessionID = strings.TrimPrefix(req.URL.Path, "/v1/session/renew/")
args.SessionID, err = getPathSuffixUnescaped(req.URL.Path, "/v1/session/renew/")
if err != nil {
return nil, err
}
args.Session = args.SessionID args.Session = args.SessionID
if args.SessionID == "" { if args.SessionID == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing session"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing session"}
@ -127,11 +120,7 @@ func (s *HTTPHandlers) SessionGet(resp http.ResponseWriter, req *http.Request) (
} }
// Pull out the session id // Pull out the session id
var err error args.SessionID = strings.TrimPrefix(req.URL.Path, "/v1/session/info/")
args.SessionID, err = getPathSuffixUnescaped(req.URL.Path, "/v1/session/info/")
if err != nil {
return nil, err
}
args.Session = args.SessionID args.Session = args.SessionID
if args.SessionID == "" { if args.SessionID == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing session"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing session"}
@ -184,11 +173,7 @@ func (s *HTTPHandlers) SessionsForNode(resp http.ResponseWriter, req *http.Reque
} }
// Pull out the node name // Pull out the node name
var err error args.Node = strings.TrimPrefix(req.URL.Path, "/v1/session/node/")
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/session/node/")
if err != nil {
return nil, err
}
if args.Node == "" { if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
} }

View File

@ -134,11 +134,7 @@ func (s *HTTPHandlers) UINodeInfo(resp http.ResponseWriter, req *http.Request) (
} }
// Verify we have some DC, or use the default // Verify we have some DC, or use the default
var err error args.Node = strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/node/")
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/internal/ui/node/")
if err != nil {
return nil, err
}
if args.Node == "" { if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
} }
@ -266,11 +262,7 @@ func (s *HTTPHandlers) UIGatewayServicesNodes(resp http.ResponseWriter, req *htt
} }
// Pull out the service name // Pull out the service name
var err error args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/gateway-services-nodes/")
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, "/v1/internal/ui/gateway-services-nodes/")
if err != nil {
return nil, err
}
if args.ServiceName == "" { if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"}
} }
@ -310,11 +302,7 @@ func (s *HTTPHandlers) UIServiceTopology(resp http.ResponseWriter, req *http.Req
return nil, err return nil, err
} }
var err error args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/service-topology/")
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, "/v1/internal/ui/service-topology/")
if err != nil {
return nil, err
}
if args.ServiceName == "" { if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"}
} }
@ -588,11 +576,7 @@ func (s *HTTPHandlers) UIGatewayIntentions(resp http.ResponseWriter, req *http.R
} }
// Pull out the service name // Pull out the service name
var err error name := strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/gateway-intentions/")
name, err := getPathSuffixUnescaped(req.URL.Path, "/v1/internal/ui/gateway-intentions/")
if err != nil {
return nil, err
}
if name == "" { if name == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"} return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"}
} }
@ -674,10 +658,7 @@ func (s *HTTPHandlers) UIMetricsProxy(resp http.ResponseWriter, req *http.Reques
// here. // here.
// Replace prefix in the path // Replace prefix in the path
subPath, err := getPathSuffixUnescaped(req.URL.Path, "/v1/internal/ui/metrics-proxy") subPath := strings.TrimPrefix(req.URL.Path, "/v1/internal/ui/metrics-proxy")
if err != nil {
return nil, err
}
// Append that to the BaseURL (which might contain a path prefix component) // Append that to the BaseURL (which might contain a path prefix component)
newURL := cfg.BaseURL + subPath newURL := cfg.BaseURL + subPath