Skip to content

Commit

Permalink
debugged function actually working, passed tests
Browse files Browse the repository at this point in the history
  • Loading branch information
KDreynolds committed May 8, 2023
1 parent 961513d commit c716cb9
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 14 deletions.
55 changes: 43 additions & 12 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package gin

import (
"encoding/json"
"errors"
"io"
"log"
Expand All @@ -15,6 +16,7 @@ import (
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -956,25 +958,54 @@ func (c *Context) SecureJSON(code int, obj any) {
// JSONP serializes the given struct as JSON into the response body.
// It adds padding to response body to request data from a server residing in a different domain than the client.
// It also sets the Content-Type as "application/javascript".
func (c *Context) JSONP(code int, obj any) {
callback := c.DefaultQuery("callback", "")
if callback == "" {
c.Render(code, render.JSON{Data: obj})
return
}
func (c *Context) JSONP(code int, obj interface{}) {
// Get the callback query parameter from the request or use an empty string as the default value

Check failure on line 962 in context.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gci`-ed with --skip-generated -s standard,default (gci)
callback := c.DefaultQuery("callback", "")

// If the callback query parameter is empty, respond with a JSON object
if callback == "" {
c.Render(code, render.JSON{Data: obj})
return
}

// Add type checking for the callback function name
callbackPattern := `^[\p{L}\p{N}_]+$` // Unicode-aware pattern for alphanumeric characters and underscores
// Add type checking for the callback function name

Check failure on line 971 in context.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gci`-ed with --skip-generated -s standard,default (gci)
// Use a Unicode-aware pattern for alphanumeric characters and underscores
callbackPattern := `^[\p{L}\p{N}_]+$`
isValidCallback := regexp.MustCompile(callbackPattern).MatchString(callback)

// If the callback function name is not valid, respond with an error message
if !isValidCallback {
// Handle the invalid callback function name, e.g., return an error or set a default callback function name
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid callback function name"})
c.JSON(http.StatusBadRequest, H{"error": "Invalid callback function name"})
return
}

c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})

// Convert the input object to a slice of H (map[string]interface{}) values

Check failure on line 982 in context.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gci`-ed with --skip-generated -s standard,default (gci)
var data []H
if d, ok := obj.([]H); ok {
data = d
} else if d, ok := obj.(H); ok {
data = []H{d}
} else {
data = []H{{"message": obj}}
}

// Convert the H slice to a slice of empty interface values
var anyData []interface{}
for _, item := range data {
anyData = append(anyData, item)
}

// Marshal the anyData slice to a JSON string
jsonString, _ := json.Marshal(anyData)

// Respond with a JavaScript callback function call that includes the JSON data
c.Render(code, render.String{Format: "/**/ typeof " + callback + " === 'function' && " + callback + "(%s);", Data: []interface{}{string(jsonString)}})
}





// JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj any) {
Expand Down
50 changes: 50 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2413,3 +2413,53 @@ func TestInterceptedHeader(t *testing.T) {
assert.Equal(t, "", w.Result().Header.Get("X-Test"))
assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
}


func TestJSONPCallbackTypeChecking(t *testing.T) {
router := New()
router.GET("/jsonp", func(c *Context) {
c.JSONP(http.StatusOK, H{"message": "success"})
})
testCases := []struct {
callback string
expected string
statusCode int
description string
}{
{
callback: "validCallback",
expected: `/**/ typeof validCallback === 'function' && validCallback([{"message":"success"}]);`,
statusCode: http.StatusOK,
description: "Valid callback function name",
},

{
callback: url.QueryEscape("invalidCallback();"),
expected: "{\"error\":\"Invalid callback function name\"}",
statusCode: http.StatusBadRequest,
description: "Invalid callback function name",
},
}

for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
req, _ := http.NewRequest("GET", "/jsonp?callback="+tc.callback, nil)
resp := httptest.NewRecorder()

router.ServeHTTP(resp, req)

if resp.Code != tc.statusCode {
t.Errorf("Expected status code %d, got %d", tc.statusCode, resp.Code)
}

actualBody := resp.Body.String()
expectedBody := tc.expected
if actualBody != expectedBody {
t.Errorf("Expected response body %q, got %q", expectedBody, actualBody)
}
})
}
}



6 changes: 4 additions & 2 deletions docs/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,9 @@ func main() {

#### JSONP

Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists.
Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter `callback` exists and contains a valid callback function name. Valid callback function names consist of alphanumeric characters and underscores.

Note: To enhance security, Gin performs type checking on the JSONP callback function names. Only alphanumeric characters and underscores are allowed in the callback function names. If an invalid callback function name is provided, Gin will return an error. This type checking mechanism helps prevent attackers from exploiting the JSONP endpoint to bypass content security headers and execute malicious scripts.

```go
func main() {
Expand All @@ -1093,7 +1095,7 @@ func main() {
// client
// curl http://127.0.0.1:8080/JSONP?callback=x
}
```


#### AsciiJSON

Expand Down

0 comments on commit c716cb9

Please sign in to comment.