Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added fuzzers in utils and authorization(graphql) #4467

Merged
merged 12 commits into from
Mar 14, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package authorization

import (
"fmt"
"testing"
"time"

fuzz "github.com/AdaLogics/go-fuzz-headers"
"github.com/golang-jwt/jwt"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/utils"
)

// generateExpiredFakeJWTToken generates a fake JWT token with expiration time set to the past
func generateExpiredFakeJWTToken(username string) string {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["username"] = username
claims["exp"] = time.Now().Add(-time.Hour).Unix() // Set expiration time to 1 hour ago
signedToken, _ := token.SignedString([]byte("your-secret-key")) // Sign the token with a secret key
return signedToken
}

// generateFakeJWTTokenWithInvalidSignature generates a fake JWT token with an invalid signature
func generateFakeJWTTokenWithInvalidSignature(username string) string {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["username"] = username
claims["exp"] = time.Now().Add(time.Hour * 24).Unix() // Set expiration time to 24 hours from now
signedToken, _ := token.SignedString([]byte("invalid-secret-key")) // Sign the token with an invalid secret key
return signedToken
}

// generateFakeJWTToken generates a fake JWT token with predefined claims
func generateFakeJWTToken(username string) string {
token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
"username": username,
"exp": time.Now().Add(time.Hour * 24).Unix(), // Set expiration time to 24 hours from now
})

signedToken, _ := token.SignedString([]byte(utils.Config.JwtSecret)) // No signature is needed for testing
return signedToken
}

func FuzzGetUsername(f *testing.F) {
f.Fuzz(func(t *testing.T, input string) {
// Create a fake JWT token with predefined claims

// Invalid token format check
_, err := GetUsername(input)
if err == nil {
t.Error("Expected error for invalid token format")
}

// Generating fake jwt token for testing
token := generateFakeJWTToken(input)

// Run the test with the fake JWT token
username, err := GetUsername(token)
if err != nil {
t.Errorf("Error encountered: %v", err)
}

// Check if the decoded username matches the input string
if username != input {
t.Errorf("Expected username: %s, got: %s", input, username)
}

// Additional checks
// Expiration check
expiredToken := generateExpiredFakeJWTToken(input)
_, err = GetUsername(expiredToken)
if err == nil {
t.Error("Expected error for expired token")
}

// Token signature check (invalid secret key)
invalidSignatureToken := generateFakeJWTTokenWithInvalidSignature(input)
_, err = GetUsername(invalidSignatureToken)
if err == nil {
t.Error("Expected error for token with invalid signature")
}

})
}

// generateJWTToken generates a JWT token with the given claims
func generateJWTTokenFromClaims(claims jwt.MapClaims) (string, error) {
// Set expiration time to 24 hours from now
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()

// Create a new token with the claims
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign the token with a secret key
tokenString, err := token.SignedString([]byte(utils.Config.JwtSecret))
if err != nil {
return "", fmt.Errorf("failed to sign JWT token: %v", err)
}

return tokenString, nil
}

func FuzzUserValidateJWT(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzConsumer := fuzz.NewConsumer(data)
inputClaims := &jwt.MapClaims{}
err := fuzzConsumer.GenerateStruct(inputClaims)
if err != nil {
return
}
// Generate a JWT token with fuzzed claims
tokenString, err := generateJWTTokenFromClaims(*inputClaims)
if err != nil {
t.Fatalf("Error generating JWT token: %v", err)
}

// Run the test with the generated JWT token
claims, err := UserValidateJWT(tokenString)
if err != nil {
t.Errorf("Error encountered: %v", err)
}

// Optionally, check if claims are nil when there's an error
if claims == nil && err == nil {
t.Errorf("Claims are nil while no error is returned")
}

})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
string("0")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
string("\x88")
25 changes: 3 additions & 22 deletions chaoscenter/graphql/server/pkg/authorization/user_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ func UserValidateJWT(token string) (jwt.MapClaims, error) {

if err != nil {
log.Print("USER JWT ERROR: ", err)
return nil, errors.New("Invalid Token")
return nil, errors.New("invalid Token")
}

if !tkn.Valid {
return nil, errors.New("Invalid Token")
return nil, errors.New("invalid Token")
}

claims, ok := tkn.Claims.(jwt.MapClaims)
if ok {
return claims, nil
}

return nil, errors.New("Invalid Token")
return nil, errors.New("invalid Token")
}

// GetUsername returns the username from the jwt token
Expand All @@ -54,22 +54,3 @@ func GetUsername(token string) (string, error) {

return "", errors.New("invalid Token")
}

// GetUserID returns the GetUserID from the jwt token
func GetUserID(token string) (string, error) {
tkn, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(utils.Config.JwtSecret), nil
})

if err != nil {
log.Print("USER JWT ERROR: ", err)
return "", errors.New("invalid Token")
}

claims, ok := tkn.Claims.(jwt.MapClaims)
if ok {
return claims["uid"].(string), nil
}

return "", errors.New("invalid Token")
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package handler
package test

namkyu1999 marked this conversation as resolved.
Show resolved Hide resolved
import (
"context"
Expand All @@ -13,6 +13,7 @@ import (
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/environments"
dbOperationsEnvironment "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/environments"
dbMocks "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/mocks"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/environment/handler"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/utils"
"github.com/stretchr/testify/mock"
"go.mongodb.org/mongo-driver/bson"
Expand Down Expand Up @@ -96,7 +97,7 @@ func TestCreateEnvironment(t *testing.T) {
token := tc.given()
ctx := context.WithValue(context.Background(), authorization.AuthKey, token)
mockOperator := environmentOperator
service := NewEnvironmentService(mockOperator)
service := handler.NewEnvironmentService(mockOperator)

env, err := service.CreateEnvironment(ctx, tc.projectID, tc.input)
if (err != nil && tc.expectedErr == nil) ||
Expand Down Expand Up @@ -173,7 +174,7 @@ func TestDeleteEnvironment(t *testing.T) {
ctx := context.WithValue(context.Background(), authorization.AuthKey, token)

mockOperator := environmentOperator
service := NewEnvironmentService(mockOperator)
service := handler.NewEnvironmentService(mockOperator)

_, err := service.DeleteEnvironment(ctx, tc.projectID, tc.environmentID)
if (err != nil && tc.expectedErr == nil) ||
Expand Down
17 changes: 10 additions & 7 deletions chaoscenter/graphql/server/utils/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ func WriteHeaders(w *gin.ResponseWriter, statusCode int) {

// RandomString generates random strings, can be used to create ids or random secrets
func RandomString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-")
rand.Seed(time.Now().UnixNano())
s := make([]rune, n)
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}
if n > 0 {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-")
rand.Seed(time.Now().UnixNano())
s := make([]rune, n)
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}

return string(s)
return string(s)
}
return ""
}

func AddRootIndent(b []byte, n int) []byte {
Expand Down
81 changes: 81 additions & 0 deletions chaoscenter/graphql/server/utils/tests/fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package tests
namkyu1999 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Saranya-jena , this file is still in the /tests folder. Can you change this one? Other than that LGTM 🚀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry missed it, added the changes


import (
"strings"
"testing"

fuzz "github.com/AdaLogics/go-fuzz-headers"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/utils"
)

func isValidString(s string) bool {
// Define the set of valid characters
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"

// Iterate over each character in the string
for _, char := range s {
// Check if the character is not in the set of valid characters
if !strings.ContainsRune(validChars, char) {
return false
}
}
return true
}

func FuzzRandomString(f *testing.F) {
f.Add(10)
f.Fuzz(func(t *testing.T, n int) {
randomString := utils.RandomString(n)
// Perform checks on the generated string
// Check if the length matches the expected length
if n >= 0 && len(randomString) != n {
t.Errorf("Generated string length doesn't match expected length")
}

// Check if the string contains only valid characters
if !isValidString(randomString) {
t.Errorf("Generated string contains invalid characters")
}
})

}

func FuzzContainsString(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzConsumer := fuzz.NewConsumer(data)
targetStruct := &struct {
s []string
str string
}{}
err := fuzzConsumer.GenerateStruct(targetStruct)
if err != nil {
return
}
// Perform checks on the ContainsString function
// Check if ContainsString returns true when the target string is in the array
if utils.ContainsString(targetStruct.s, targetStruct.str) {
found := false
for _, v := range targetStruct.s {
if v == targetStruct.str {
found = true
break
}
}
if !found {
t.Errorf("ContainsString returned true for target '%s' not present in the array", targetStruct.str)
}
} else {
// Check if ContainsString returns false when the target string is not in the array
found := false
for _, v := range targetStruct.s {
if v == targetStruct.str {
found = true
break
}
}
if found {
t.Errorf("ContainsString returned false for target '%s' present in the array", targetStruct.str)
}
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
int(-57)
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,12 @@ export default function DeleteApiTokenController(props: DeleteApiTokenController
}
);

return <DeleteApiTokenView handleClose={handleClose} deleteApiTokenMutation={deleteApiTokenMutation} deleteApiTokenMutationLoading={deleteApiTokenMutationLoading} token={token} />;
return (
<DeleteApiTokenView
handleClose={handleClose}
deleteApiTokenMutation={deleteApiTokenMutation}
deleteApiTokenMutationLoading={deleteApiTokenMutationLoading}
token={token}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function EnableDisableUserController(props: EnableDisableUserCont
const { getUsersRefetch } = props;
const { showSuccess } = useToaster();

const { mutate: updateStateMutation, isLoading: updateStateMutationLoading} = useUpdateStateMutation(
const { mutate: updateStateMutation, isLoading: updateStateMutationLoading } = useUpdateStateMutation(
{},
{
onSuccess: data => {
Expand All @@ -27,5 +27,11 @@ export default function EnableDisableUserController(props: EnableDisableUserCont
}
);

return <EnableDisableUserView {...props} updateStateMutation={updateStateMutation} updateStateMutationLoading={updateStateMutationLoading} />;
return (
<EnableDisableUserView
{...props}
updateStateMutation={updateStateMutation}
updateStateMutationLoading={updateStateMutationLoading}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export default function ResetPasswordController(props: ResetPasswordControllerPr
);

return (
<ResetPasswordView username={username} handleClose={handleClose} resetPasswordMutation={resetPasswordMutation} resetPasswordMutationLoading={resetPasswordMutationLoading} />
<ResetPasswordView
username={username}
handleClose={handleClose}
resetPasswordMutation={resetPasswordMutation}
resetPasswordMutationLoading={resetPasswordMutationLoading}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ export default function AccountDetailsChangeView(props: AccountDetailsChangeView
<Button
type="submit"
variation={ButtonVariation.PRIMARY}
text={updateDetailsMutationLoading ? <Icon name='loading' size={16}/> : getString('confirm')}
text={updateDetailsMutationLoading ? <Icon name="loading" size={16} /> : getString('confirm')}
loading={updateDetailsMutationLoading}
disabled={updateDetailsMutationLoading || isUserDetailsUpdated(formikProps.values)}
style={{minWidth: '90px'}}
style={{ minWidth: '90px' }}
/>
<Button
variation={ButtonVariation.TERTIARY}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ export default function AccountPasswordChangeView(props: AccountPasswordChangeVi
<Button
type="submit"
variation={ButtonVariation.PRIMARY}
text={updatePasswordMutationLoading ? <Icon name='loading' size={16}/> : getString('confirm')}
text={updatePasswordMutationLoading ? <Icon name="loading" size={16} /> : getString('confirm')}
loading={updatePasswordMutationLoading}
disabled={updatePasswordMutationLoading || isSubmitButtonDisabled(formikProps.values)}
style={{minWidth: '90px'}}
style={{ minWidth: '90px' }}
/>
<Button
variation={ButtonVariation.TERTIARY}
Expand Down
Loading
Loading