Repository: coreruleset/go-ftw
Pull request #96 (2022-10-30 14:19:00)

fix: properly URL encode data for POST requests
While doing tests using unencoded data, we figured out that some parts of the encoding were not working.

This PR fixes this problem.


Patch:
diff --git a/check/base.go b/check/base.go
index 765f812..fea8dc2 100644
--- a/check/base.go
+++ b/check/base.go
@@ -60,7 +60,7 @@ func (c *FTWCheck) SetNoLogContains(contains string) {
 
 // ForcedIgnore check if this id need to be ignored from results
 func (c *FTWCheck) ForcedIgnore(id string) bool {
-	for re, _ := range c.overrides.Ignore {
+	for re := range c.overrides.Ignore {
 		if re.MatchString(id) {
 			return true
 		}
@@ -70,7 +70,7 @@ func (c *FTWCheck) ForcedIgnore(id string) bool {
 
 // ForcedPass check if this id need to be ignored from results
 func (c *FTWCheck) ForcedPass(id string) bool {
-	for re, _ := range c.overrides.ForcePass {
+	for re := range c.overrides.ForcePass {
 		if re.MatchString(id) {
 			return true
 		}
@@ -80,7 +80,7 @@ func (c *FTWCheck) ForcedPass(id string) bool {
 
 // ForcedFail check if this id need to be ignored from results
 func (c *FTWCheck) ForcedFail(id string) bool {
-	for re, _ := range c.overrides.ForceFail {
+	for re := range c.overrides.ForceFail {
 		if re.MatchString(id) {
 			return true
 		}
diff --git a/ftwhttp/request.go b/ftwhttp/request.go
index e84c836..23a80d7 100644
--- a/ftwhttp/request.go
+++ b/ftwhttp/request.go
@@ -183,33 +183,17 @@ func buildRequest(r *Request) ([]byte, error) {
 	return b.Bytes(), err
 }
 
-// If the values are empty in the map, then don't encode anythin
-// This keeps the compatibility with the python implementation
-func emptyQueryValues(values url.Values) bool {
-	for _, v := range values {
-		val := v
-		if len(val) > 1 {
-			return false
-		}
-	}
-	return true
-}
-
 // encodeDataParameters url encode parameters in data
 func encodeDataParameters(h Header, data []byte) ([]byte, error) {
 	var err error
 
 	if h.Get(ContentTypeHeader) == "application/x-www-form-urlencoded" {
-		if escapedData, _ := url.QueryUnescape(string(data)); escapedData == string(data) {
-			queryString, err := url.ParseQuery(string(data))
-			if (err != nil && strings.Contains(err.Error(), "invalid semicolon separator in query")) || emptyQueryValues(queryString) {
-				return data, nil
-			}
-			encodedData := queryString.Encode()
-			if encodedData != string(data) {
-				// we need to encode data
-				return []byte(encodedData), nil
+		if escapedData, err := url.QueryUnescape(string(data)); escapedData == string(data) {
+			if err != nil {
+				return nil, errors.New("Failed")
 			}
+			queryString := url.QueryEscape(string(data))
+			return []byte(queryString), nil
 		}
 	}
 	return data, err


Test patch:
diff --git a/ftwhttp/request_test.go b/ftwhttp/request_test.go
index 2f02d69..2a15966 100644
--- a/ftwhttp/request_test.go
+++ b/ftwhttp/request_test.go
@@ -226,3 +226,25 @@ func TestRequestURLParseFail(t *testing.T) {
 	err := req.SetData([]byte("test=This&that=but with;;;;;; data now"))
 	assert.NoError(t, err)
 }
+
+func TestRequestEncodesPostData(t *testing.T) {
+	req := generateBaseRequestForTesting()
+
+	h := req.Headers()
+	h.Add(ContentTypeHeader, "application/x-www-form-urlencoded")
+	// Test adding semicolons to test parse
+	err := req.SetData([]byte(`c4= ;c3=t;c2=a;c1=c;a1=/;a2=e;a3=t;a4=c;a5=/;a6=p;a7=a;a8=s;a9=s;a10=w;a11=d;$c1$c2$c3$c4$a1$a2$a3$a4$a5$a6$a7$a8$a9$a10$a11`))
+	if err != nil {
+		t.Errorf("Failed !")
+	}
+	result, err := encodeDataParameters(h, req.Data())
+	if err != nil {
+		t.Errorf("Failed to encode %s", req.Data())
+	}
+
+	expected := "c4%3D+%3Bc3%3Dt%3Bc2%3Da%3Bc1%3Dc%3Ba1%3D%2F%3Ba2%3De%3Ba3%3Dt%3Ba4%3Dc%3Ba5%3D%2F%3Ba6%3Dp%3Ba7%3Da%3Ba8%3Ds%3Ba9%3Ds%3Ba10%3Dw%3Ba11%3Dd%3B%24c1%24c2%24c3%24c4%24a1%24a2%24a3%24a4%24a5%24a6%24a7%24a8%24a9%24a10%24a11"
+	actual := string(result)
+	if actual != expected {
+		t.Error("Unexpected URL encoded payload")
+	}
+}